mirror of
https://github.com/gchq/CyberChef.git
synced 2025-05-10 00:05:11 -04:00
Initial command line/TCP server script
This fairly simple script leverages the Node API provided by CyberChef to allow performing operations on local filesystem objects without needing to copy them into and out of a browser window. It requires a recipe to be specified at runtime as a JSON file. Also includes a simple TCP server for use by other local processes. This server is hard-coded to use localhost in order to discourage production use of the script.
This commit is contained in:
parent
4c3324aea1
commit
648ecb6e69
2 changed files with 161 additions and 0 deletions
157
cli.js
Executable file
157
cli.js
Executable file
|
@ -0,0 +1,157 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* @author Boolean263 [boolean263@protonmail.com]
|
||||||
|
* @copyright Crown Copyright 2020
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const chef = require("cyberchef");
|
||||||
|
const program = require("commander");
|
||||||
|
|
||||||
|
let slurpStream = (istream) => { // {{{1
|
||||||
|
var ret = [];
|
||||||
|
var len = 0;
|
||||||
|
return new Promise(resolve => {
|
||||||
|
istream.on('readable', () => {
|
||||||
|
var chunk;
|
||||||
|
while ((chunk = istream.read()) !== null) {
|
||||||
|
ret.push(chunk);
|
||||||
|
len += chunk.length;
|
||||||
|
}
|
||||||
|
resolve(Buffer.concat(ret, len));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}; // }}}1
|
||||||
|
|
||||||
|
let slurp = (fname) => { // {{{1
|
||||||
|
let istream;
|
||||||
|
|
||||||
|
if (fname === undefined || fname == '-') {
|
||||||
|
istream = process.stdin;
|
||||||
|
if (istream.isTTY) {
|
||||||
|
throw new Error("TTY input not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
istream = fs.createReadStream(fname, { flags: 'r' });
|
||||||
|
}
|
||||||
|
return slurpStream(istream);
|
||||||
|
}; // }}}1
|
||||||
|
|
||||||
|
let getPort = (value, dummyPrevious) => { // {{{1
|
||||||
|
// Get a valid port number from the command line
|
||||||
|
let ret = parseInt(value, 10);
|
||||||
|
if (ret < 1 || ret > 65535) {
|
||||||
|
throw new Error("invalid port number");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
// }}}1
|
||||||
|
|
||||||
|
///////// MAIN ///////// {{{1
|
||||||
|
|
||||||
|
program
|
||||||
|
.version(require('./package.json').version)
|
||||||
|
.usage('[options] [file [file ...]]')
|
||||||
|
.requiredOption('-r, --recipe-file <file>',
|
||||||
|
'recipe JSON file')
|
||||||
|
.option('-l, --listen [port]',
|
||||||
|
'listen on TCP port for data (default:random)', getPort, false)
|
||||||
|
.option('-o, --output <file-or-dir>',
|
||||||
|
'write result here (not for TCP; default:stdout)')
|
||||||
|
.parse(process.argv);
|
||||||
|
|
||||||
|
// If we get no inputs and we aren't running a server,
|
||||||
|
// make stdin our single input
|
||||||
|
let inputs = program.args;
|
||||||
|
if (inputs.length == 0 && !program.listen) {
|
||||||
|
inputs = [ '-' ];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Likewise stdout for our output
|
||||||
|
let ostream;
|
||||||
|
let outputIsDir = false;
|
||||||
|
if (program.output === undefined && !program.listen) {
|
||||||
|
ostream = process.stdout;
|
||||||
|
}
|
||||||
|
else if (inputs.length > 0) {
|
||||||
|
// See if our output is a directory
|
||||||
|
let st;
|
||||||
|
try {
|
||||||
|
st = fs.statSync(program.output);
|
||||||
|
outputIsDir = st.isDirectory();
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
if (err.code != 'ENOENT') throw err;
|
||||||
|
}
|
||||||
|
if (!outputIsDir) {
|
||||||
|
ostream = fs.createWriteStream(program.output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let recipe;
|
||||||
|
slurp(program.recipeFile).then((data) => {
|
||||||
|
recipe = JSON.parse(data);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(`Error parsing recipe: ${err}`);
|
||||||
|
process.exit(1);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// First, deal with any files we want to read
|
||||||
|
for(let i of inputs) {
|
||||||
|
slurp(i).then((data) => {
|
||||||
|
let output = chef.bake(data, recipe);
|
||||||
|
if (outputIsDir) {
|
||||||
|
let outFileName = path.basename(i);
|
||||||
|
if (outFileName == '-') outFileName = 'from-stdin';
|
||||||
|
ostream = fs.createWriteStream(
|
||||||
|
path.join(program.output, outFileName));
|
||||||
|
}
|
||||||
|
ostream.write(output.presentAs("string", true));
|
||||||
|
if (outputIsDir) ostream.end();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, listen for TCP requests.
|
||||||
|
// This is intentionally hardcoded to localhost to discourage
|
||||||
|
// the use of this script as a production system.
|
||||||
|
if (program.listen) {
|
||||||
|
const net = require('net');
|
||||||
|
const server = net.createServer((socket) => {
|
||||||
|
slurpStream(socket).then((data) => {
|
||||||
|
let output = chef.bake(data, recipe);
|
||||||
|
socket.write(output.presentAs("string", true));
|
||||||
|
socket.end();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// If no port given, let the OS choose one
|
||||||
|
if (program.listen === true) program.listen = 0;
|
||||||
|
server.listen(program.listen, '127.0.0.1')
|
||||||
|
.on('listening', () => {
|
||||||
|
console.log('Now listening on '
|
||||||
|
+ server.address().address
|
||||||
|
+ ":" + server.address().port);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Exit gracefully
|
||||||
|
process.on('SIGINT', () => {
|
||||||
|
console.log("Exiting");
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(2);
|
||||||
|
})
|
|
@ -35,6 +35,9 @@
|
||||||
"Firefox >= 38",
|
"Firefox >= 38",
|
||||||
"node >= 10"
|
"node >= 10"
|
||||||
],
|
],
|
||||||
|
"bin": {
|
||||||
|
"cyberchef": "./cli.js"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.8.7",
|
"@babel/core": "^7.8.7",
|
||||||
"@babel/plugin-transform-runtime": "^7.8.3",
|
"@babel/plugin-transform-runtime": "^7.8.3",
|
||||||
|
@ -98,6 +101,7 @@
|
||||||
"bson": "^4.0.3",
|
"bson": "^4.0.3",
|
||||||
"chi-squared": "^1.1.0",
|
"chi-squared": "^1.1.0",
|
||||||
"codepage": "^1.14.0",
|
"codepage": "^1.14.0",
|
||||||
|
"commander": "^2.14.1",
|
||||||
"core-js": "^3.6.4",
|
"core-js": "^3.6.4",
|
||||||
"crypto-api": "^0.8.5",
|
"crypto-api": "^0.8.5",
|
||||||
"crypto-js": "^4.0.0",
|
"crypto-js": "^4.0.0",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue