mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-22 07:46:16 -04:00
add other flowcontrol ops. Update tests
This commit is contained in:
parent
046e1ebad9
commit
8ff6596657
15 changed files with 735 additions and 402 deletions
51
src/core/operations/Comment.mjs
Normal file
51
src/core/operations/Comment.mjs
Normal file
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
/**
|
||||
* Comment operation
|
||||
*/
|
||||
class Comment extends Operation {
|
||||
|
||||
/**
|
||||
* Comment constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Comment";
|
||||
this.flowControl = true;
|
||||
this.module = "Default";
|
||||
this.description = "Provides a place to write comments within the flow of the recipe. This operation has no computational effect.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "",
|
||||
"type": "text",
|
||||
"value": ""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Comment operation.
|
||||
*
|
||||
* @param {Object} state - The current state of the recipe.
|
||||
* @param {number} state.progress - The current position in the recipe.
|
||||
* @param {Dish} state.dish - The Dish being operated on.
|
||||
* @param {Operation[]} state.opList - The list of operations in the recipe.
|
||||
* @returns {Object} The updated state of the recipe.
|
||||
*/
|
||||
run(state) {
|
||||
return state;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Comment;
|
89
src/core/operations/ConditionalJump.mjs
Normal file
89
src/core/operations/ConditionalJump.mjs
Normal file
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Dish from "../Dish";
|
||||
import { getLabelIndex } from "../lib/FlowControl";
|
||||
|
||||
/**
|
||||
* Conditional Jump operation
|
||||
*/
|
||||
class ConditionalJump extends Operation {
|
||||
|
||||
/**
|
||||
* ConditionalJump constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Conditional Jump";
|
||||
this.flowControl = true;
|
||||
this.module = "Default";
|
||||
this.description = "Conditionally jump forwards or backwards to the specified Label based on whether the data matches the specified regular expression.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Match (regex)",
|
||||
"type": "string",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "Invert match",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "Label name",
|
||||
"type": "shortString",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "Maximum jumps (if jumping backwards)",
|
||||
"type": "number",
|
||||
"value": 10
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditional Jump operation.
|
||||
*
|
||||
* @param {Object} state - The current state of the recipe.
|
||||
* @param {number} state.progress - The current position in the recipe.
|
||||
* @param {Dish} state.dish - The Dish being operated on.
|
||||
* @param {Operation[]} state.opList - The list of operations in the recipe.
|
||||
* @param {number} state.numJumps - The number of jumps taken so far.
|
||||
* @returns {Object} The updated state of the recipe.
|
||||
*/
|
||||
async run(state) {
|
||||
const ings = state.opList[state.progress].ingValues,
|
||||
dish = state.dish,
|
||||
regexStr = ings[0],
|
||||
invert = ings[1],
|
||||
label = ings[2],
|
||||
maxJumps = ings[3],
|
||||
jmpIndex = getLabelIndex(label, state);
|
||||
|
||||
if (state.numJumps >= maxJumps || jmpIndex === -1) {
|
||||
return state;
|
||||
}
|
||||
|
||||
if (regexStr !== "") {
|
||||
const str = await dish.get(Dish.STRING);
|
||||
const strMatch = str.search(regexStr) > -1;
|
||||
if (!invert && strMatch || invert && !strMatch) {
|
||||
state.progress = jmpIndex;
|
||||
state.numJumps++;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ConditionalJump;
|
121
src/core/operations/Fork.mjs
Normal file
121
src/core/operations/Fork.mjs
Normal file
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Recipe from "../Recipe";
|
||||
import Dish from "../Dish";
|
||||
|
||||
/**
|
||||
* Fork operation
|
||||
*/
|
||||
class Fork extends Operation {
|
||||
|
||||
/**
|
||||
* Fork constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Fork";
|
||||
this.flowControl = true;
|
||||
this.module = "Default";
|
||||
this.description = "Split the input data up based on the specified delimiter and run all subsequent operations on each branch separately.<br><br>For example, to decode multiple Base64 strings, enter them all on separate lines then add the 'Fork' and 'From Base64' operations to the recipe. Each string will be decoded separately.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Split delimiter",
|
||||
"type": "binaryShortString",
|
||||
"value": "\\n"
|
||||
},
|
||||
{
|
||||
"name": "Merge delimiter",
|
||||
"type": "binaryShortString",
|
||||
"value": "\\n"
|
||||
},
|
||||
{
|
||||
"name": "Ignore errors",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fork operation.
|
||||
*
|
||||
* @param {Object} state - The current state of the recipe.
|
||||
* @param {number} state.progress - The current position in the recipe.
|
||||
* @param {Dish} state.dish - The Dish being operated on.
|
||||
* @param {Operation[]} state.opList - The list of operations in the recipe.
|
||||
* @returns {Object} The updated state of the recipe.
|
||||
*/
|
||||
async run(state) {
|
||||
const opList = state.opList,
|
||||
inputType = opList[state.progress].inputType,
|
||||
outputType = opList[state.progress].outputType,
|
||||
input = await state.dish.get(inputType),
|
||||
ings = opList[state.progress].ingValues,
|
||||
splitDelim = ings[0],
|
||||
mergeDelim = ings[1],
|
||||
ignoreErrors = ings[2],
|
||||
subOpList = [];
|
||||
let inputs = [],
|
||||
i;
|
||||
|
||||
if (input)
|
||||
inputs = input.split(splitDelim);
|
||||
|
||||
// Create subOpList for each tranche to operate on
|
||||
// (all remaining operations unless we encounter a Merge)
|
||||
for (i = state.progress + 1; i < opList.length; i++) {
|
||||
if (opList[i].name === "Merge" && !opList[i].disabled) {
|
||||
break;
|
||||
} else {
|
||||
subOpList.push(opList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const recipe = new Recipe();
|
||||
let output = "",
|
||||
progress = 0;
|
||||
|
||||
state.forkOffset += state.progress + 1;
|
||||
|
||||
recipe.addOperations(subOpList);
|
||||
|
||||
// Take a deep(ish) copy of the ingredient values
|
||||
const ingValues = subOpList.map(op => JSON.parse(JSON.stringify(op.ingValues)));
|
||||
|
||||
// Run recipe over each tranche
|
||||
for (i = 0; i < inputs.length; i++) {
|
||||
// Baseline ing values for each tranche so that registers are reset
|
||||
subOpList.forEach((op, i) => {
|
||||
op.ingValues = JSON.parse(JSON.stringify(ingValues[i]));
|
||||
});
|
||||
|
||||
const dish = new Dish();
|
||||
dish.set(inputs[i], inputType);
|
||||
|
||||
try {
|
||||
progress = await recipe.execute(dish, 0, state);
|
||||
} catch (err) {
|
||||
if (!ignoreErrors) {
|
||||
throw err;
|
||||
}
|
||||
progress = err.progress + 1;
|
||||
}
|
||||
output += await dish.get(outputType) + mergeDelim;
|
||||
}
|
||||
|
||||
state.dish.set(output, outputType);
|
||||
state.progress += progress;
|
||||
return state;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Fork;
|
|
@ -50,13 +50,11 @@ class Jump extends Operation {
|
|||
const jmpIndex = getLabelIndex(label, state);
|
||||
|
||||
if (state.numJumps >= maxJumps || jmpIndex === -1) {
|
||||
log.debug("Maximum jumps reached or label cannot be found");
|
||||
return state;
|
||||
}
|
||||
|
||||
state.progress = jmpIndex;
|
||||
state.numJumps++;
|
||||
log.debug(`Jumping to label '${label}' at position ${jmpIndex} (jumps = ${state.numJumps})`);
|
||||
return state;
|
||||
|
||||
}
|
||||
|
|
50
src/core/operations/Label.mjs
Normal file
50
src/core/operations/Label.mjs
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
/**
|
||||
* Label operation
|
||||
*/
|
||||
class Label extends Operation {
|
||||
|
||||
/**
|
||||
* Label constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Label";
|
||||
this.flowControl = true;
|
||||
this.module = "Default";
|
||||
this.description = "Provides a location for conditional and fixed jumps to redirect execution to.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Name",
|
||||
"type": "shortString",
|
||||
"value": ""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Label operation. For use with Jump and Conditional Jump
|
||||
*
|
||||
* @param {Object} state - The current state of the recipe.
|
||||
* @param {number} state.progress - The current position in the recipe.
|
||||
* @param {Dish} state.dish - The Dish being operated on.
|
||||
* @param {Operation[]} state.opList - The list of operations in the recipe.
|
||||
* @returns {Object} The updated state of the recipe.
|
||||
*/
|
||||
run(state) {
|
||||
return state;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Label;
|
45
src/core/operations/Return.mjs
Normal file
45
src/core/operations/Return.mjs
Normal file
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
/**
|
||||
* Return operation
|
||||
*/
|
||||
class Return extends Operation {
|
||||
|
||||
/**
|
||||
* Return constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Return";
|
||||
this.flowControl = true;
|
||||
this.module = "Default";
|
||||
this.description = "End execution of operations at this point in the recipe.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return operation.
|
||||
*
|
||||
* @param {Object} state - The current state of the recipe.
|
||||
* @param {number} state.progress - The current position in the recipe.
|
||||
* @param {Dish} state.dish - The Dish being operated on.
|
||||
* @param {Operation[]} state.opList - The list of operations in the recipe.
|
||||
* @returns {Object} The updated state of the recipe.
|
||||
*/
|
||||
run(state) {
|
||||
state.progress = state.opList.length;
|
||||
return state;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Return;
|
|
@ -227,7 +227,8 @@ const FlowControl = {
|
|||
}
|
||||
|
||||
if (regexStr !== "") {
|
||||
const strMatch = await dish.get(Dish.STRING).search(regexStr) > -1;
|
||||
const str = await dish.get(Dish.STRING)
|
||||
const strMatch = str.search(regexStr) > -1;
|
||||
if (!invert && strMatch || invert && !strMatch) {
|
||||
state.progress = jmpIndex;
|
||||
state.numJumps++;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue