mirror of
https://github.com/gchq/CyberChef.git
synced 2024-11-16 00:48:31 +01:00
Formally disallow flowcontrol operations from being used in bake recipes
This commit is contained in:
parent
939208903a
commit
53e69835ff
4 changed files with 81 additions and 74 deletions
|
@ -28,16 +28,22 @@ class NodeRecipe {
|
||||||
* @param {String | Function | Object} ing
|
* @param {String | Function | Object} ing
|
||||||
*/
|
*/
|
||||||
_validateIngredient(ing) {
|
_validateIngredient(ing) {
|
||||||
|
// CASE operation name given. Find operation and validate
|
||||||
if (typeof ing === "string") {
|
if (typeof ing === "string") {
|
||||||
const op = operations.find((op) => {
|
const op = operations.find((op) => {
|
||||||
return sanitise(op.opName) === sanitise(ing);
|
return sanitise(op.opName) === sanitise(ing);
|
||||||
});
|
});
|
||||||
if (op) {
|
if (op) {
|
||||||
return op;
|
return this._validateIngredient(op);
|
||||||
} else {
|
} else {
|
||||||
throw new TypeError(`Couldn't find an operation with name '${ing}'.`);
|
throw new TypeError(`Couldn't find an operation with name '${ing}'.`);
|
||||||
}
|
}
|
||||||
|
// CASE operation given. Check its a chef operation and check its not flowcontrol
|
||||||
} else if (typeof ing === "function") {
|
} else if (typeof ing === "function") {
|
||||||
|
if (ing.flowControl) {
|
||||||
|
throw new TypeError(`flowControl operations like ${ing.opName} are not currently allowed in recipes for chef.bake`);
|
||||||
|
}
|
||||||
|
|
||||||
if (operations.includes(ing)) {
|
if (operations.includes(ing)) {
|
||||||
return ing;
|
return ing;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -177,6 +177,7 @@ export function _wrap(OpClass) {
|
||||||
// Check to see if class's run function is async.
|
// Check to see if class's run function is async.
|
||||||
const opInstance = new OpClass();
|
const opInstance = new OpClass();
|
||||||
const isAsync = opInstance.run.constructor.name === "AsyncFunction";
|
const isAsync = opInstance.run.constructor.name === "AsyncFunction";
|
||||||
|
const isFlowControl = opInstance.flowControl;
|
||||||
|
|
||||||
let wrapped;
|
let wrapped;
|
||||||
|
|
||||||
|
@ -192,8 +193,9 @@ export function _wrap(OpClass) {
|
||||||
wrapped = async (input, args=null) => {
|
wrapped = async (input, args=null) => {
|
||||||
const {transformedInput, transformedArgs} = prepareOp(opInstance, input, args);
|
const {transformedInput, transformedArgs} = prepareOp(opInstance, input, args);
|
||||||
|
|
||||||
// SPECIAL CASE for Magic.
|
// SPECIAL CASE for Magic. Other flowControl operations will
|
||||||
if (opInstance.flowControl) {
|
// not work because the opList is not passed through.
|
||||||
|
if (isFlowControl) {
|
||||||
opInstance.ingValues = transformedArgs;
|
opInstance.ingValues = transformedArgs;
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
|
@ -241,6 +243,8 @@ export function _wrap(OpClass) {
|
||||||
// used in chef.help
|
// used in chef.help
|
||||||
wrapped.opName = OpClass.name;
|
wrapped.opName = OpClass.name;
|
||||||
wrapped.args = createArgInfo(opInstance);
|
wrapped.args = createArgInfo(opInstance);
|
||||||
|
// Used in NodeRecipe to check for flowControl ops
|
||||||
|
wrapped.flowControl = isFlowControl;
|
||||||
|
|
||||||
return wrapped;
|
return wrapped;
|
||||||
}
|
}
|
||||||
|
@ -315,25 +319,18 @@ export function help(input) {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bake [Wrapped] - Perform an array of operations on some input.
|
* bake
|
||||||
* @returns {Function}
|
*
|
||||||
|
* @param {*} input - some input for a recipe.
|
||||||
|
* @param {String | Function | String[] | Function[] | [String | Function]} recipeConfig -
|
||||||
|
* An operation, operation name, or an array of either.
|
||||||
|
* @returns {NodeDish} of the result
|
||||||
|
* @throws {TypeError} if invalid recipe given.
|
||||||
*/
|
*/
|
||||||
export function bake() {
|
export function bake(input, recipeConfig) {
|
||||||
|
const recipe = new NodeRecipe(recipeConfig);
|
||||||
/**
|
const dish = ensureIsDish(input);
|
||||||
* bake
|
return recipe.execute(dish);
|
||||||
*
|
|
||||||
* @param {*} input - some input for a recipe.
|
|
||||||
* @param {String | Function | String[] | Function[] | [String | Function]} recipeConfig -
|
|
||||||
* An operation, operation name, or an array of either.
|
|
||||||
* @returns {SyncDish} of the result
|
|
||||||
* @throws {TypeError} if invalid recipe given.
|
|
||||||
*/
|
|
||||||
return function(input, recipeConfig) {
|
|
||||||
const recipe = new NodeRecipe(recipeConfig);
|
|
||||||
const dish = ensureIsDish(input);
|
|
||||||
return recipe.execute(dish);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -100,8 +100,7 @@ Object.keys(operations).forEach((op) => {
|
||||||
|
|
||||||
code += `];
|
code += `];
|
||||||
|
|
||||||
const prebaked = bake(operations);
|
chef.bake = bake;
|
||||||
chef.bake = prebaked;
|
|
||||||
export default chef;
|
export default chef;
|
||||||
|
|
||||||
// Operations as top level exports.
|
// Operations as top level exports.
|
||||||
|
@ -114,7 +113,7 @@ Object.keys(operations).forEach((op) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
code += " NodeDish as Dish,\n";
|
code += " NodeDish as Dish,\n";
|
||||||
code += " prebaked as bake,\n";
|
code += " bake,\n";
|
||||||
code += " help,\n";
|
code += " help,\n";
|
||||||
code += " OperationError,\n";
|
code += " OperationError,\n";
|
||||||
code += " ExcludedOperationError,\n";
|
code += " ExcludedOperationError,\n";
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import it from "../assertionHandler.mjs";
|
import it from "../assertionHandler.mjs";
|
||||||
import chef from "../../../src/node/index.mjs";
|
import chef from "../../../src/node/index.mjs";
|
||||||
import OperationError from "../../../src/core/errors/OperationError.mjs";
|
import { OperationError, ExcludedOperationError } from "../../../src/core/errors/index.mjs";
|
||||||
import NodeDish from "../../../src/node/NodeDish.mjs";
|
import NodeDish from "../../../src/node/NodeDish.mjs";
|
||||||
|
|
||||||
import { toBase32} from "../../../src/node/index.mjs";
|
import { toBase32, magic} from "../../../src/node/index.mjs";
|
||||||
import TestRegister from "../../lib/TestRegister.mjs";
|
import TestRegister from "../../lib/TestRegister.mjs";
|
||||||
|
|
||||||
TestRegister.addApiTests([
|
TestRegister.addApiTests([
|
||||||
|
@ -181,22 +181,17 @@ TestRegister.addApiTests([
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("chef.bake: should complain if recipe isnt a valid object", () => {
|
it("chef.bake: should complain if recipe isnt a valid object", () => {
|
||||||
try {
|
assert.throws(() => chef.bake("some input", 3264), {
|
||||||
chef.bake("some input", 3264);
|
name: "TypeError",
|
||||||
} catch (e) {
|
message: "Recipe can only contain function names or functions"
|
||||||
assert.strictEqual(e.name, "TypeError");
|
});
|
||||||
assert.strictEqual(e.message, "Recipe can only contain function names or functions");
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("chef.bake: Should complain if string op is invalid", () => {
|
it("chef.bake: Should complain if string op is invalid", () => {
|
||||||
try {
|
assert.throws(() => chef.bake("some input", "not a valid operation"), {
|
||||||
chef.bake("some input", "not a valid operation");
|
name: "TypeError",
|
||||||
assert.fail("Shouldn't be hit");
|
message: "Couldn't find an operation with name 'not a valid operation'."
|
||||||
} catch (e) {
|
});
|
||||||
assert.strictEqual(e.name, "TypeError");
|
|
||||||
assert.strictEqual(e.message, "Couldn't find an operation with name 'not a valid operation'.");
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("chef.bake: Should take an input and an operation and perform it", () => {
|
it("chef.bake: Should take an input and an operation and perform it", () => {
|
||||||
|
@ -205,13 +200,10 @@ TestRegister.addApiTests([
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("chef.bake: Should complain if an invalid operation is inputted", () => {
|
it("chef.bake: Should complain if an invalid operation is inputted", () => {
|
||||||
try {
|
assert.throws(() => chef.bake("https://google.com/search?q=help", () => {}), {
|
||||||
chef.bake("https://google.com/search?q=help", () => {});
|
name: "TypeError",
|
||||||
assert.fail("Shouldn't be hit");
|
message: "Inputted function not a Chef operation."
|
||||||
} catch (e) {
|
});
|
||||||
assert.strictEqual(e.name, "TypeError");
|
|
||||||
assert.strictEqual(e.message, "Inputted function not a Chef operation.");
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("chef.bake: accepts an array of operation names and performs them all in order", () => {
|
it("chef.bake: accepts an array of operation names and performs them all in order", () => {
|
||||||
|
@ -241,12 +233,10 @@ TestRegister.addApiTests([
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("should complain if an invalid operation is inputted as part of array", () => {
|
it("should complain if an invalid operation is inputted as part of array", () => {
|
||||||
try {
|
assert.throws(() => chef.bake("something", [() => {}]), {
|
||||||
chef.bake("something", [() => {}]);
|
name: "TypeError",
|
||||||
} catch (e) {
|
message: "Inputted function not a Chef operation."
|
||||||
assert.strictEqual(e.name, "TypeError");
|
});
|
||||||
assert.strictEqual(e.message, "Inputted function not a Chef operation.");
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("chef.bake: should take single JSON object describing op and args OBJ", () => {
|
it("chef.bake: should take single JSON object describing op and args OBJ", () => {
|
||||||
|
@ -275,15 +265,13 @@ TestRegister.addApiTests([
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("chef.bake: should error if op in JSON is not chef op", () => {
|
it("chef.bake: should error if op in JSON is not chef op", () => {
|
||||||
try {
|
assert.throws(() => chef.bake("some input", {
|
||||||
chef.bake("some input", {
|
op: () => {},
|
||||||
op: () => {},
|
args: ["Colon"],
|
||||||
args: ["Colon"],
|
}), {
|
||||||
});
|
name: "TypeError",
|
||||||
} catch (e) {
|
message: "Inputted function not a Chef operation."
|
||||||
assert.strictEqual(e.name, "TypeError");
|
});
|
||||||
assert.strictEqual(e.message, "Inputted function not a Chef operation.");
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("chef.bake: should take multiple ops in JSON object form, some ops by string", () => {
|
it("chef.bake: should take multiple ops in JSON object form, some ops by string", () => {
|
||||||
|
@ -357,22 +345,38 @@ TestRegister.addApiTests([
|
||||||
assert.strictEqual(result.toString(), "begin_something_aaaaaaaaaaaaaa_end_something");
|
assert.strictEqual(result.toString(), "begin_something_aaaaaaaaaaaaaa_end_something");
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("Excluded operations: throw a sensible error when you try and call one", () => {
|
it("chef.bake: cannot accept flowControl operations in recipe", () => {
|
||||||
try {
|
assert.throws(() => chef.bake("some input", "magic"), {
|
||||||
chef.fork();
|
name: "TypeError",
|
||||||
} catch (e) {
|
message: "flowControl operations like Magic are not currently allowed in recipes for chef.bake"
|
||||||
assert.strictEqual(e.type, "ExcludedOperationError");
|
});
|
||||||
assert.strictEqual(e.message, "Sorry, the Fork operation is not available in the Node.js version of CyberChef.");
|
assert.throws(() => chef.bake("some input", magic), {
|
||||||
}
|
name: "TypeError",
|
||||||
|
message: "flowControl operations like Magic are not currently allowed in recipes for chef.bake"
|
||||||
|
});
|
||||||
|
assert.throws(() => chef.bake("some input", ["to base 64", "magic"]), {
|
||||||
|
name: "TypeError",
|
||||||
|
message: "flowControl operations like Magic are not currently allowed in recipes for chef.bake"
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("Excluded operations: throw a sensible error when you try and call one", () => {
|
it("Excluded operations: throw a sensible error when you try and call one", () => {
|
||||||
try {
|
assert.throws(chef.fork,
|
||||||
chef.renderImage();
|
(err) => {
|
||||||
} catch (e) {
|
assert(err instanceof ExcludedOperationError);
|
||||||
assert.strictEqual(e.type, "ExcludedOperationError");
|
assert.deepEqual(err.message, "Sorry, the Fork operation is not available in the Node.js version of CyberChef.");
|
||||||
assert.strictEqual(e.message, "Sorry, the RenderImage operation is not available in the Node.js version of CyberChef.");
|
return true;
|
||||||
}
|
},
|
||||||
|
"Unexpected error type"
|
||||||
|
);
|
||||||
|
assert.throws(chef.javaScriptBeautify,
|
||||||
|
(err) => {
|
||||||
|
assert(err instanceof ExcludedOperationError);
|
||||||
|
assert.deepEqual(err.message, "Sorry, the JavaScriptBeautify operation is not available in the Node.js version of CyberChef.");
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
"Unexpected error type"
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("Operation arguments: should be accessible from operation object if op has array arg", () => {
|
it("Operation arguments: should be accessible from operation object if op has array arg", () => {
|
||||||
|
@ -405,4 +409,5 @@ TestRegister.addApiTests([
|
||||||
assert.equal(chef.convertDistance.args.inputUnits.options[0], "Nanometres (nm)");
|
assert.equal(chef.convertDistance.args.inputUnits.options[0], "Nanometres (nm)");
|
||||||
assert.equal(chef.defangURL.args.process.options[1], "Only full URLs");
|
assert.equal(chef.defangURL.args.process.options[1], "Only full URLs");
|
||||||
}),
|
}),
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
|
Loading…
Reference in a new issue