From a289eda7fc7c6fdf5a4d09350b69f06c5f4fcda7 Mon Sep 17 00:00:00 2001 From: d98762625 Date: Thu, 28 May 2020 16:06:08 +0100 Subject: [PATCH] chef.bake can parse chef format recipe --- src/node/NodeRecipe.mjs | 54 ++++++++++++++++++++++------- tests/node/tests/nodeApi.mjs | 66 ++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 12 deletions(-) diff --git a/src/node/NodeRecipe.mjs b/src/node/NodeRecipe.mjs index b623f611..360f4c73 100644 --- a/src/node/NodeRecipe.mjs +++ b/src/node/NodeRecipe.mjs @@ -6,6 +6,8 @@ import {operations} from "./index.mjs"; import { sanitise } from "./apiUtils.mjs"; +import Utils from "../core/Utils"; + /** * Similar to core/Recipe, Recipe controls a list of operations and @@ -34,9 +36,10 @@ class NodeRecipe { }); if (op) { return op; - } else { - throw new TypeError(`Couldn't find an operation with name '${ing}'.`); } + + throw new TypeError(`Couldn't find an operation with name '${ing}'.`); + } else if (typeof ing === "function") { if (operations.includes(ing)) { return ing; @@ -47,7 +50,15 @@ class NodeRecipe { } else if (ing.op) { const sanitisedOp = this._validateIngredient(ing.op); if (ing.args) { - return {op: sanitisedOp, args: ing.args}; + + // disabled, breakpoint options sometimes come from parsed + // recipes pasted from UI + return { + op: sanitisedOp, + args: ing.args, + disabled: ing.disabled, + breakpoint: ing.breakpoint + }; } return sanitisedOp; } else { @@ -55,7 +66,6 @@ class NodeRecipe { } } - /** * Parse config for recipe. * @param {String | Function | String[] | Function[] | [String | Function]} recipeConfig @@ -66,6 +76,14 @@ class NodeRecipe { return; } + // Case for when recipeConfig is a chef format recipe string + if (typeof recipeConfig == "string" || recipeConfig instanceof String) { + const attemptedParseResult = Utils.parseRecipeConfig(recipeConfig); + if (attemptedParseResult.length > 0) { + recipeConfig = attemptedParseResult; + } + } + if (!Array.isArray(recipeConfig)) { recipeConfig = [recipeConfig]; } @@ -79,15 +97,27 @@ class NodeRecipe { * @returns {NodeDish} */ execute(dish) { - return this.opList.reduce((prev, curr) => { - // CASE where opList item is op and args - if (Object.prototype.hasOwnProperty.call(curr, "op") && - Object.prototype.hasOwnProperty.call(curr, "args")) { - return curr.op(prev, curr.args); + for (const op of this.opList) { + if ( + Object.prototype.hasOwnProperty.call(op, "op") && + Object.prototype.hasOwnProperty.call(op, "args") + ) { + + if (op.breakpoint) { + break; + } + + if (op.disabled) { + continue; + } + + dish = op.op(dish, op.args); + } else { + dish = op(dish); } - // CASE opList item is just op. - return curr(prev); - }, dish); + } + + return dish; } } diff --git a/tests/node/tests/nodeApi.mjs b/tests/node/tests/nodeApi.mjs index a4e907b8..2bd464f9 100644 --- a/tests/node/tests/nodeApi.mjs +++ b/tests/node/tests/nodeApi.mjs @@ -357,6 +357,58 @@ TestRegister.addApiTests([ assert.strictEqual(result.toString(), "begin_something_aaaaaaaaaaaaaa_end_something"); }), + it("chef.bake: should accept single operation Chef format recipe as second argument", () => { + const result = chef.bake("throw throw burrito", "To_Hex_Content('All chars',false)"); + assert.strictEqual(result.toString(), "|7468726f77207468726f77206275727269746f|"); + }), + + it("chef.bake: should accept single operation Chef format recipe as second argument with non-default arguments", () => { + const result = chef.bake("Throw Throw Burrito", "ROT13(true,false,14)"); + assert.strictEqual(result.toString(), "Tvfck Tvfck Biffwhc"); + }), + + it("chef.bake: should accept multiple operation Chef format recipe as second argument", () => { + const result = chef.bake("throw throw burrito", "To_Hex('Space',2)Hex_to_Object_Identifier()Extract_IP_addresses(true,false,false,true)"); + assert.strictEqual(result.toString(), `Total found: 5 + +2.36.104.114 +111.119.32.116 +104.114.111.119 +32.98.117.114 +114.105.116.111 +`); + }), + + it("chef.bake: should accept multiple operation Chef format recipe as pasted from UI as second argument", () => { + const result = chef.bake("throw throw burrito", `To_Hex('Space',2) +Hex_to_Object_Identifier() +Extract_IP_addresses(true,false,false,true) +`); + assert.strictEqual(result.toString(), `Total found: 5 + +2.36.104.114 +111.119.32.116 +104.114.111.119 +32.98.117.114 +114.105.116.111 +`); + }), + + it("chef.bake: should accept multiple operation Chef format recipe with a disabled operation", async () => { + const result = await chef.bake("throw throw burrito", `ROT13(true,true,13) +Atbash_Cipher(/disabled) +MD5() +`); + assert.strictEqual(result.toString(), "f859e9e196c4452d2d25f12dffc67355"); + }), + + it("chef.bake: should accept multiple operation Chef format recipe with a breakpoint operation", async () => { + const result = await chef.bake("throw throw burrito", `ROT13(true,true,13) +Atbash_Cipher() +MD5(/breakpoint)`); + assert.strictEqual(result.toString(), "tfvyq tfvyq lsvvety"); + }), + it("Excluded operations: throw a sensible error when you try and call one", () => { try { chef.fork(); @@ -375,6 +427,20 @@ TestRegister.addApiTests([ } }), + it("Excluded operations: throw a sensible error when you try and call one as part of a recipe", () => { + try { + chef.bake(`978346800 +1012651200 +1046696400 +1081087200 +1115305200 +1149609600`, "Fork('\\n','\\n',false)From_UNIX_Timestamp('Seconds (s)')"); + } catch (e) { + assert.strictEqual(e.type, "ExcludedOperationError"); + assert.strictEqual(e.message, "Sorry, the Fork operation is not available in the Node.js version of CyberChef."); + } + }), + it("Operation arguments: should be accessible from operation object if op has array arg", () => { assert.ok(chef.toCharcode.args); assert.deepEqual(chef.unzip.args, {