Sfoglia il codice sorgente

Formally disallow flowcontrol operations from being used in bake recipes

d98762625 5 anni fa
parent
commit
53e69835ff

+ 7 - 1
src/node/NodeRecipe.mjs

@@ -28,16 +28,22 @@ class NodeRecipe {
      * @param {String | Function | Object} ing
      */
     _validateIngredient(ing) {
+        // CASE operation name given. Find operation and validate
         if (typeof ing === "string") {
             const op = operations.find((op) => {
                 return sanitise(op.opName) === sanitise(ing);
             });
             if (op) {
-                return op;
+                return this._validateIngredient(op);
             } else {
                 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") {
+            if (ing.flowControl) {
+                throw new TypeError(`flowControl operations like ${ing.opName} are not currently allowed in recipes for chef.bake`);
+            }
+
             if (operations.includes(ing)) {
                 return ing;
             } else {

+ 17 - 20
src/node/api.mjs

@@ -177,6 +177,7 @@ export function _wrap(OpClass) {
     // Check to see if class's run function is async.
     const opInstance = new OpClass();
     const isAsync = opInstance.run.constructor.name === "AsyncFunction";
+    const isFlowControl = opInstance.flowControl;
 
     let wrapped;
 
@@ -192,8 +193,9 @@ export function _wrap(OpClass) {
         wrapped = async (input, args=null) => {
             const {transformedInput, transformedArgs} = prepareOp(opInstance, input, args);
 
-            // SPECIAL CASE for Magic.
-            if (opInstance.flowControl) {
+            // SPECIAL CASE for Magic. Other flowControl operations will
+            // not work because the opList is not passed through.
+            if (isFlowControl) {
                 opInstance.ingValues = transformedArgs;
 
                 const state = {
@@ -241,6 +243,8 @@ export function _wrap(OpClass) {
     // used in chef.help
     wrapped.opName = OpClass.name;
     wrapped.args = createArgInfo(opInstance);
+    // Used in NodeRecipe to check for flowControl ops
+    wrapped.flowControl = isFlowControl;
 
     return wrapped;
 }
@@ -315,25 +319,18 @@ export function help(input) {
 
 
 /**
- * bake [Wrapped] - Perform an array of operations on some input.
- * @returns {Function}
+ * bake
+ *
+ * @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() {
-
-    /**
-     * bake
-     *
-     * @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);
-    };
+export function bake(input, recipeConfig) {
+    const recipe =  new NodeRecipe(recipeConfig);
+    const dish = ensureIsDish(input);
+    return recipe.execute(dish);
 }
 
 

+ 2 - 3
src/node/config/scripts/generateNodeIndex.mjs

@@ -100,8 +100,7 @@ Object.keys(operations).forEach((op) => {
 
 code += `];
 
-const prebaked = bake(operations);
-chef.bake = prebaked;
+chef.bake = bake;
 export default chef;
 
 // Operations as top level exports.
@@ -114,7 +113,7 @@ Object.keys(operations).forEach((op) => {
 });
 
 code += "    NodeDish as Dish,\n";
-code += "    prebaked as bake,\n";
+code += "    bake,\n";
 code += "    help,\n";
 code += "    OperationError,\n";
 code += "    ExcludedOperationError,\n";

+ 55 - 50
tests/node/tests/nodeApi.mjs

@@ -13,10 +13,10 @@
 import assert from "assert";
 import it from "../assertionHandler.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 { toBase32} from "../../../src/node/index.mjs";
+import { toBase32, magic} from "../../../src/node/index.mjs";
 import TestRegister from "../../lib/TestRegister.mjs";
 
 TestRegister.addApiTests([
@@ -181,22 +181,17 @@ TestRegister.addApiTests([
     }),
 
     it("chef.bake: should complain if recipe isnt a valid object", () => {
-        try {
-            chef.bake("some input", 3264);
-        } catch (e) {
-            assert.strictEqual(e.name, "TypeError");
-            assert.strictEqual(e.message, "Recipe can only contain function names or functions");
-        }
+        assert.throws(() => chef.bake("some input", 3264), {
+            name: "TypeError",
+            message: "Recipe can only contain function names or functions"
+        });
     }),
 
     it("chef.bake: Should complain if string op is invalid", () => {
-        try {
-            chef.bake("some input", "not a valid operation");
-            assert.fail("Shouldn't be hit");
-        } catch (e) {
-            assert.strictEqual(e.name, "TypeError");
-            assert.strictEqual(e.message, "Couldn't find an operation with name 'not a valid operation'.");
-        }
+        assert.throws(() => chef.bake("some input", "not a valid operation"), {
+            name: "TypeError",
+            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", () => {
@@ -205,13 +200,10 @@ TestRegister.addApiTests([
     }),
 
     it("chef.bake: Should complain if an invalid operation is inputted", () => {
-        try {
-            chef.bake("https://google.com/search?q=help", () => {});
-            assert.fail("Shouldn't be hit");
-        } catch (e) {
-            assert.strictEqual(e.name, "TypeError");
-            assert.strictEqual(e.message, "Inputted function not a Chef operation.");
-        }
+        assert.throws(() => chef.bake("https://google.com/search?q=help", () => {}), {
+            name: "TypeError",
+            message: "Inputted function not a Chef operation."
+        });
     }),
 
     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", () => {
-        try {
-            chef.bake("something", [() => {}]);
-        } catch (e) {
-            assert.strictEqual(e.name, "TypeError");
-            assert.strictEqual(e.message, "Inputted function not a Chef operation.");
-        }
+        assert.throws(() => chef.bake("something", [() => {}]), {
+            name: "TypeError",
+            message: "Inputted function not a Chef operation."
+        });
     }),
 
     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", () => {
-        try {
-            chef.bake("some input", {
-                op: () => {},
-                args: ["Colon"],
-            });
-        } catch (e) {
-            assert.strictEqual(e.name, "TypeError");
-            assert.strictEqual(e.message, "Inputted function not a Chef operation.");
-        }
+        assert.throws(() => chef.bake("some input", {
+            op: () => {},
+            args: ["Colon"],
+        }), {
+            name: "TypeError",
+            message: "Inputted function not a Chef operation."
+        });
     }),
 
     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");
     }),
 
-    it("Excluded operations: throw a sensible error when you try and call one", () => {
-        try {
-            chef.fork();
-        } 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("chef.bake: cannot accept flowControl operations in recipe", () => {
+        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", 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", () => {
-        try {
-            chef.renderImage();
-        } catch (e) {
-            assert.strictEqual(e.type, "ExcludedOperationError");
-            assert.strictEqual(e.message, "Sorry, the RenderImage operation is not available in the Node.js version of CyberChef.");
-        }
+        assert.throws(chef.fork,
+            (err) => {
+                assert(err instanceof ExcludedOperationError);
+                assert.deepEqual(err.message, "Sorry, the Fork 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", () => {
@@ -405,4 +409,5 @@ TestRegister.addApiTests([
         assert.equal(chef.convertDistance.args.inputUnits.options[0], "Nanometres (nm)");
         assert.equal(chef.defangURL.args.process.options[1], "Only full URLs");
     }),
+
 ]);