diff --git a/.eslintrc.json b/.eslintrc.json index 8e21e9ad..8632d2e6 100755 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,6 @@ { "parserOptions": { - "ecmaVersion": 6, + "ecmaVersion": 8, "ecmaFeatures": { "impliedStrict": true }, diff --git a/docs/jsdoc.conf.json b/docs/jsdoc.conf.json index 00a85cc3..3c247edc 100755 --- a/docs/jsdoc.conf.json +++ b/docs/jsdoc.conf.json @@ -2,7 +2,10 @@ "tags": { "allowUnknownTags": true }, - "plugins": ["plugins/markdown"], + "plugins": [ + "plugins/markdown", + "node_modules/jsdoc-babel" + ], "templates": { "systemName": "CyberChef", "footer": "", diff --git a/package.json b/package.json index efe7851c..fc5df19a 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "html-webpack-plugin": "^2.28.0", "imports-loader": "^0.7.1", "ink-docstrap": "^1.1.4", + "jsdoc-babel": "^0.3.0", "less": "^2.7.2", "less-loader": "^4.0.2", "style-loader": "^0.15.0", diff --git a/src/core/Chef.js b/src/core/Chef.js index 342a85a5..9fa23d22 100755 --- a/src/core/Chef.js +++ b/src/core/Chef.js @@ -34,7 +34,7 @@ const Chef = function() { * @returns {number} response.duration - The number of ms it took to execute the recipe * @returns {number} response.error - The error object thrown by a failed operation (false if no error) */ -Chef.prototype.bake = function(inputText, recipeConfig, options, progress, step) { +Chef.prototype.bake = async function(inputText, recipeConfig, options, progress, step) { let startTime = new Date().getTime(), recipe = new Recipe(recipeConfig), containsFc = recipe.containsFlowControl(), @@ -72,7 +72,7 @@ Chef.prototype.bake = function(inputText, recipeConfig, options, progress, step) } try { - progress = recipe.execute(this.dish, progress); + progress = await recipe.execute(this.dish, progress); } catch (err) { // Return the error in the result so that everything else gets correctly updated // rather than throwing it here and losing state info. diff --git a/src/core/FlowControl.js b/src/core/FlowControl.js index c478edc2..8585a160 100755 --- a/src/core/FlowControl.js +++ b/src/core/FlowControl.js @@ -38,7 +38,7 @@ const FlowControl = { * @param {Operation[]} state.opList - The list of operations in the recipe. * @returns {Object} The updated state of the recipe. */ - runFork: function(state) { + runFork: async function(state) { let opList = state.opList, inputType = opList[state.progress].inputType, outputType = opList[state.progress].outputType, @@ -74,7 +74,7 @@ const FlowControl = { for (i = 0; i < inputs.length; i++) { const dish = new Dish(inputs[i], inputType); try { - progress = recipe.execute(dish, 0); + progress = await recipe.execute(dish, 0); } catch (err) { if (!ignoreErrors) { throw err; diff --git a/src/core/Recipe.js b/src/core/Recipe.js index 0aa8e4f2..1b0e7f73 100755 --- a/src/core/Recipe.js +++ b/src/core/Recipe.js @@ -145,7 +145,7 @@ Recipe.prototype.lastOpIndex = function(startIndex) { * @param {number} [startFrom=0] - The index of the Operation to start executing from * @returns {number} - The final progress through the recipe */ -Recipe.prototype.execute = function(dish, startFrom) { +Recipe.prototype.execute = async function(dish, startFrom) { startFrom = startFrom || 0; let op, input, output, numJumps = 0; @@ -170,11 +170,11 @@ Recipe.prototype.execute = function(dish, startFrom) { "numJumps" : numJumps }; - state = op.run(state); + state = await op.run(state); i = state.progress; numJumps = state.numJumps; } else { - output = op.run(input, op.getIngValues()); + output = await op.run(input, op.getIngValues()); dish.set(output, op.outputType); } } catch (err) { diff --git a/src/web/App.js b/src/web/App.js index 65f9538a..76615acd 100755 --- a/src/web/App.js +++ b/src/web/App.js @@ -30,6 +30,7 @@ const App = function(categories, operations, defaultFavourites, defaultOptions) this.chef = new Chef(); this.manager = new Manager(this); + this.baking = false; this.autoBake_ = false; this.progress = 0; this.ingId = 0; @@ -67,17 +68,47 @@ App.prototype.handleError = function(err) { }; +/** + * Updates the UI to show if baking is in process or not. + * + * @param {bakingStatus} + */ +App.prototype.setBakingStatus = function(bakingStatus) { + this.baking = bakingStatus; + + var inputLoadingIcon = document.querySelector("#input .title .loading-icon"), + outputLoadingIcon = document.querySelector("#output .title .loading-icon"), + outputElement = document.querySelector("#output-text"); + + if (bakingStatus) { + inputLoadingIcon.style.display = "inline-block"; + outputLoadingIcon.style.display = "inline-block"; + outputElement.classList.add("disabled"); + outputElement.disabled = true; + } else { + inputLoadingIcon.style.display = "none"; + outputLoadingIcon.style.display = "none"; + outputElement.classList.remove("disabled"); + outputElement.disabled = false; + } +}; + + /** * Calls the Chef to bake the current input using the current recipe. * * @param {boolean} [step] - Set to true if we should only execute one operation instead of the * whole recipe. */ -App.prototype.bake = function(step) { +App.prototype.bake = async function(step) { let response; + if (this.baking) return; + + this.setBakingStatus(true); + try { - response = this.chef.bake( + response = await this.chef.bake( this.getInput(), // The user's input this.getRecipeConfig(), // The configuration of the recipe this.options, // Options set by the user @@ -88,6 +119,8 @@ App.prototype.bake = function(step) { this.handleError(err); } + this.setBakingStatus(false); + if (!response) return; if (response.error) { diff --git a/src/web/css/structure/layout.css b/src/web/css/structure/layout.css index 8286eb82..3cd5abe7 100755 --- a/src/web/css/structure/layout.css +++ b/src/web/css/structure/layout.css @@ -430,3 +430,36 @@ span.btn img { border-top: none; margin-top: 0; } + + +@-moz-keyframes spinner { + from { -moz-transform: rotate(0deg); } + to { -moz-transform: rotate(359deg); } +} +@-webkit-keyframes spinner { + from { -webkit-transform: rotate(0deg); } + to { -webkit-transform: rotate(359deg); } +} +@keyframes spinner { + from {transform:rotate(0deg);} + to {transform:rotate(359deg);} +} + +.loading-icon::before { + content: "\21bb"; +} + +.loading-icon { + -webkit-animation-name: spinner; + -webkit-animation-duration: 1000ms; + -webkit-animation-iteration-count: infinite; + -webkit-animation-timing-function: linear; + -moz-animation-name: spinner; + -moz-animation-duration: 1000ms; + -moz-animation-iteration-count: infinite; + -moz-animation-timing-function: linear; + -ms-animation-name: spinner; + -ms-animation-duration: 1000ms; + -ms-animation-iteration-count: infinite; + -ms-animation-timing-function: linear; +} diff --git a/src/web/html/index.html b/src/web/html/index.html index 35877e45..afe56fae 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -100,6 +100,7 @@
+
@@ -116,6 +117,7 @@
+
diff --git a/test/TestRegister.js b/test/TestRegister.js index 3088c8fe..d4197fed 100644 --- a/test/TestRegister.js +++ b/test/TestRegister.js @@ -40,13 +40,13 @@ import Chef from "../src/core/Chef.js"; this.tests.map(function(test, i) { let chef = new Chef(); - return Promise.resolve(chef.bake( + return chef.bake( test.input, test.recipeConfig, {}, 0, false - )) + ) .then(function(result) { let ret = { test: test, diff --git a/test/tests/operations/FlowControl.js b/test/tests/operations/FlowControl.js index a48b8bf3..4ade1691 100644 --- a/test/tests/operations/FlowControl.js +++ b/test/tests/operations/FlowControl.js @@ -66,6 +66,62 @@ TestRegister.addTests([ {"op":"To Base64", "args":["A-Za-z0-9+/="]} ] }, + { + name: "Jump: skips 0", + input: [ + "should be changed", + ].join("\n"), + expectedOutput: [ + "should be changed was changed", + ].join("\n"), + recipeConfig: [ + { + op: "Jump", + args: [0, 10], + }, + { + op: "Find / Replace", + args: [ + { + "option": "Regex", + "string": "should be changed" + }, + "should be changed was changed", + true, + true, + true, + ], + }, + ], + }, + { + name: "Jump: skips 1", + input: [ + "shouldnt be changed", + ].join("\n"), + expectedOutput: [ + "shouldnt be changed", + ].join("\n"), + recipeConfig: [ + { + op: "Jump", + args: [1, 10], + }, + { + op: "Find / Replace", + args: [ + { + "option": "Regex", + "string": "shouldnt be changed" + }, + "shouldnt be changed was changed", + true, + true, + true, + ], + }, + ], + }, { name: "Conditional Jump: Skips 0", input: [ @@ -141,4 +197,81 @@ TestRegister.addTests([ } ] }, + { + name: "Conditional Jump: Skips 1", + input: [ + "match", + "should not be changed", + "should be changed", + ].join("\n"), + expectedOutput: [ + "match", + "should not be changed", + "should be changed was changed" + ].join("\n"), + recipeConfig: [ + { + op: "Conditional Jump", + args: ["match", 1, 10], + }, + { + op: "Find / Replace", + args: [ + { + "option": "Regex", + "string": "should not be changed" + }, + "should not be changed was changed", + true, + true, + true, + ], + }, + { + op: "Find / Replace", + args: [ + { + "option": "Regex", + "string": "should be changed" + }, + "should be changed was changed", + true, + true, + true, + ], + }, + ], + }, + { + name: "Conditional Jump: Skips negatively", + input: [ + "match", + ].join("\n"), + expectedOutput: [ + "replaced", + ].join("\n"), + recipeConfig: [ + { + op: "Jump", + args: [1], + }, + { + op: "Find / Replace", + args: [ + { + "option": "Regex", + "string": "match" + }, + "replaced", + true, + true, + true, + ], + }, + { + op: "Conditional Jump", + args: ["match", -2, 10], + }, + ], + }, ]);