Merge branch 'gchq:master' into leon/add-zstd-compress-decompress
This commit is contained in:
commit
074ec98ded
32
package-lock.json
generated
32
package-lock.json
generated
@ -113,12 +113,12 @@
|
|||||||
"@babel/plugin-syntax-import-assertions": "^7.28.6",
|
"@babel/plugin-syntax-import-assertions": "^7.28.6",
|
||||||
"@babel/plugin-transform-runtime": "^7.29.0",
|
"@babel/plugin-transform-runtime": "^7.29.0",
|
||||||
"@babel/preset-env": "^7.29.2",
|
"@babel/preset-env": "^7.29.2",
|
||||||
"@babel/runtime": "^7.28.6",
|
"@babel/runtime": "^7.29.2",
|
||||||
"@codemirror/commands": "^6.10.3",
|
"@codemirror/commands": "^6.10.3",
|
||||||
"@codemirror/language": "^6.12.2",
|
"@codemirror/language": "^6.12.2",
|
||||||
"@codemirror/search": "^6.6.0",
|
"@codemirror/search": "^6.6.0",
|
||||||
"@codemirror/state": "^6.5.4",
|
"@codemirror/state": "^6.5.4",
|
||||||
"@codemirror/view": "^6.39.17",
|
"@codemirror/view": "^6.40.0",
|
||||||
"autoprefixer": "^10.4.27",
|
"autoprefixer": "^10.4.27",
|
||||||
"babel-loader": "^10.1.1",
|
"babel-loader": "^10.1.1",
|
||||||
"base64-loader": "^1.0.0",
|
"base64-loader": "^1.0.0",
|
||||||
@ -127,7 +127,7 @@
|
|||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
"compression-webpack-plugin": "^11.1.0",
|
"compression-webpack-plugin": "^11.1.0",
|
||||||
"copy-webpack-plugin": "^13.0.1",
|
"copy-webpack-plugin": "^13.0.1",
|
||||||
"core-js": "^3.48.0",
|
"core-js": "^3.49.0",
|
||||||
"cspell": "^8.19.4",
|
"cspell": "^8.19.4",
|
||||||
"css-loader": "7.1.4",
|
"css-loader": "7.1.4",
|
||||||
"eslint": "^9.39.4",
|
"eslint": "^9.39.4",
|
||||||
@ -1755,9 +1755,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/runtime": {
|
"node_modules/@babel/runtime": {
|
||||||
"version": "7.28.6",
|
"version": "7.29.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
|
||||||
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
|
"integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1897,13 +1897,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/view": {
|
"node_modules/@codemirror/view": {
|
||||||
"version": "6.39.17",
|
"version": "6.40.0",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.17.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.40.0.tgz",
|
||||||
"integrity": "sha512-Aim4lFqhbijnchl83RLfABWueSGs1oUCSv0mru91QdhpXQeNKprIdRO9LWA4cYkJvuYTKGJN7++9MXx8XW43ag==",
|
"integrity": "sha512-WA0zdU7xfF10+5I3HhUUq3kqOx3KjqmtQ9lqZjfK7jtYk4G72YW9rezcSywpaUMCWOMlq+6E0pO1IWg1TNIhtg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.6.0",
|
||||||
"crelt": "^1.0.6",
|
"crelt": "^1.0.6",
|
||||||
"style-mod": "^4.1.0",
|
"style-mod": "^4.1.0",
|
||||||
"w3c-keyname": "^2.2.4"
|
"w3c-keyname": "^2.2.4"
|
||||||
@ -6820,9 +6820,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/core-js": {
|
"node_modules/core-js": {
|
||||||
"version": "3.48.0",
|
"version": "3.49.0",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz",
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz",
|
||||||
"integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==",
|
"integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -9634,9 +9634,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/flatted": {
|
"node_modules/flatted": {
|
||||||
"version": "3.3.2",
|
"version": "3.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
|
||||||
"integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
|
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -43,12 +43,12 @@
|
|||||||
"@babel/plugin-syntax-import-assertions": "^7.28.6",
|
"@babel/plugin-syntax-import-assertions": "^7.28.6",
|
||||||
"@babel/plugin-transform-runtime": "^7.29.0",
|
"@babel/plugin-transform-runtime": "^7.29.0",
|
||||||
"@babel/preset-env": "^7.29.2",
|
"@babel/preset-env": "^7.29.2",
|
||||||
"@babel/runtime": "^7.28.6",
|
"@babel/runtime": "^7.29.2",
|
||||||
"@codemirror/commands": "^6.10.3",
|
"@codemirror/commands": "^6.10.3",
|
||||||
"@codemirror/language": "^6.12.2",
|
"@codemirror/language": "^6.12.2",
|
||||||
"@codemirror/search": "^6.6.0",
|
"@codemirror/search": "^6.6.0",
|
||||||
"@codemirror/state": "^6.5.4",
|
"@codemirror/state": "^6.5.4",
|
||||||
"@codemirror/view": "^6.39.17",
|
"@codemirror/view": "^6.40.0",
|
||||||
"autoprefixer": "^10.4.27",
|
"autoprefixer": "^10.4.27",
|
||||||
"babel-loader": "^10.1.1",
|
"babel-loader": "^10.1.1",
|
||||||
"base64-loader": "^1.0.0",
|
"base64-loader": "^1.0.0",
|
||||||
@ -57,7 +57,7 @@
|
|||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
"compression-webpack-plugin": "^11.1.0",
|
"compression-webpack-plugin": "^11.1.0",
|
||||||
"copy-webpack-plugin": "^13.0.1",
|
"copy-webpack-plugin": "^13.0.1",
|
||||||
"core-js": "^3.48.0",
|
"core-js": "^3.49.0",
|
||||||
"cspell": "^8.19.4",
|
"cspell": "^8.19.4",
|
||||||
"css-loader": "7.1.4",
|
"css-loader": "7.1.4",
|
||||||
"eslint": "^9.39.4",
|
"eslint": "^9.39.4",
|
||||||
|
|||||||
@ -55,8 +55,15 @@ class Chef {
|
|||||||
progress = await recipe.execute(this.dish, progress);
|
progress = await recipe.execute(this.dish, progress);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.error(err);
|
log.error(err);
|
||||||
|
|
||||||
|
let displayStr;
|
||||||
|
if ("displayStr" in err) {
|
||||||
|
displayStr = err.displayStr;
|
||||||
|
} else {
|
||||||
|
displayStr = err.toString();
|
||||||
|
}
|
||||||
error = {
|
error = {
|
||||||
displayStr: err.displayStr,
|
displayStr: displayStr,
|
||||||
};
|
};
|
||||||
progress = err.progress;
|
progress = err.progress;
|
||||||
}
|
}
|
||||||
|
|||||||
7
src/core/Ingredient.mjs
Executable file → Normal file
7
src/core/Ingredient.mjs
Executable file → Normal file
@ -5,7 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Utils from "./Utils.mjs";
|
import Utils from "./Utils.mjs";
|
||||||
import {fromHex} from "./lib/Hex.mjs";
|
import { fromHex } from "./lib/Hex.mjs";
|
||||||
|
import OperationError from "./errors/OperationError.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The arguments to operations.
|
* The arguments to operations.
|
||||||
@ -119,7 +120,9 @@ class Ingredient {
|
|||||||
number = parseFloat(data);
|
number = parseFloat(data);
|
||||||
if (isNaN(number)) {
|
if (isNaN(number)) {
|
||||||
const sample = Utils.truncate(data.toString(), 10);
|
const sample = Utils.truncate(data.toString(), 10);
|
||||||
throw "Invalid ingredient value. Not a number: " + sample;
|
throw new OperationError(
|
||||||
|
"Invalid ingredient value. Not a number: " + sample,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return number;
|
return number;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Dish from "./Dish.mjs";
|
import Dish from "./Dish.mjs";
|
||||||
|
import OperationError from "./errors/OperationError.mjs";
|
||||||
import Ingredient from "./Ingredient.mjs";
|
import Ingredient from "./Ingredient.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,7 +224,11 @@ class Operation {
|
|||||||
*/
|
*/
|
||||||
set ingValues(ingValues) {
|
set ingValues(ingValues) {
|
||||||
ingValues.forEach((val, i) => {
|
ingValues.forEach((val, i) => {
|
||||||
this._ingList[i].value = val;
|
try {
|
||||||
|
this._ingList[i].value = val;
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(`Failed to set value of ingredient '${this._ingList[i].name}': ${err}`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -70,11 +70,15 @@ class Recipe {
|
|||||||
if (o instanceof Operation) {
|
if (o instanceof Operation) {
|
||||||
return o;
|
return o;
|
||||||
} else {
|
} else {
|
||||||
const op = new modules[o.module][o.name]();
|
try {
|
||||||
op.ingValues = o.ingValues;
|
const op = new modules[o.module][o.name]();
|
||||||
op.breakpoint = o.breakpoint;
|
op.ingValues = o.ingValues;
|
||||||
op.disabled = o.disabled;
|
op.breakpoint = o.breakpoint;
|
||||||
return op;
|
op.disabled = o.disabled;
|
||||||
|
return op;
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(`Failed to hydrate operation '${o.name}': ${err}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -249,6 +249,7 @@
|
|||||||
"DNS over HTTPS",
|
"DNS over HTTPS",
|
||||||
"Strip HTTP headers",
|
"Strip HTTP headers",
|
||||||
"Dechunk HTTP response",
|
"Dechunk HTTP response",
|
||||||
|
"Parse Ethernet frame",
|
||||||
"Parse User Agent",
|
"Parse User Agent",
|
||||||
"Parse IP range",
|
"Parse IP range",
|
||||||
"Parse IPv6 address",
|
"Parse IPv6 address",
|
||||||
|
|||||||
@ -30,7 +30,12 @@ class Jq extends Operation {
|
|||||||
name: "Query",
|
name: "Query",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: ""
|
value: ""
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
name: "Raw",
|
||||||
|
type: "boolean",
|
||||||
|
value: false
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +45,7 @@ class Jq extends Operation {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const [query] = args;
|
const [query, raw] = args;
|
||||||
let result;
|
let result;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -48,8 +53,11 @@ class Jq extends Operation {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Invalid jq expression: ${err.message}`);
|
throw new OperationError(`Invalid jq expression: ${err.message}`);
|
||||||
}
|
}
|
||||||
|
if (raw && typeof result === "string") {
|
||||||
return JSON.stringify(result);
|
return result;
|
||||||
|
} else {
|
||||||
|
return JSON.stringify(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
115
src/core/operations/ParseEthernetFrame.mjs
Normal file
115
src/core/operations/ParseEthernetFrame.mjs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* @author tedk [tedk@ted.do]
|
||||||
|
* @copyright Crown Copyright 2024
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation.mjs";
|
||||||
|
import OperationError from "../errors/OperationError.mjs";
|
||||||
|
import Utils from "../Utils.mjs";
|
||||||
|
import {fromHex, toHex} from "../lib/Hex.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse Ethernet frame operation
|
||||||
|
*/
|
||||||
|
class ParseEthernetFrame extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ParseEthernetFrame constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Parse Ethernet frame";
|
||||||
|
this.module = "Default";
|
||||||
|
this.description = "Parses an Ethernet frame and either shows the deduced values (Source and destination MAC, VLANs) or returns the packet data.<br /><br />Good for use in conjunction with the Parse IPv4, and Parse TCP/UDP recipes.";
|
||||||
|
this.infoURL = "https://en.wikipedia.org/wiki/Ethernet_frame#Frame_%E2%80%93_data_link_layer";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "html";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Input type",
|
||||||
|
type: "option",
|
||||||
|
value: [
|
||||||
|
"Raw", "Hex"
|
||||||
|
],
|
||||||
|
defaultIndex: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Return type",
|
||||||
|
type: "option",
|
||||||
|
value: [
|
||||||
|
"Text output", "Packet data", "Packet data (hex)",
|
||||||
|
],
|
||||||
|
defaultIndex: 0,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {html}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
const format = args[0];
|
||||||
|
const outputFormat = args[1];
|
||||||
|
|
||||||
|
if (format === "Hex") {
|
||||||
|
input = fromHex(input);
|
||||||
|
} else if (format === "Raw") {
|
||||||
|
input = new Uint8Array(Utils.strToArrayBuffer(input));
|
||||||
|
} else {
|
||||||
|
throw new OperationError("Invalid input format selected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const destinationMac = input.slice(0, 6);
|
||||||
|
const sourceMac = input.slice(6, 12);
|
||||||
|
|
||||||
|
let offset = 12;
|
||||||
|
const vlans = [];
|
||||||
|
|
||||||
|
while (offset < input.length) {
|
||||||
|
const ethType = Utils.byteArrayToChars(input.slice(offset, offset+2));
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
|
||||||
|
if (ethType === "\x08\x00") {
|
||||||
|
break;
|
||||||
|
} else if (ethType === "\x81\x00" || ethType === "\x88\xA8") {
|
||||||
|
// Parse the VLAN tag:
|
||||||
|
// [0000] 0000 0000 0000
|
||||||
|
// ^^^ PRIO - Ignored
|
||||||
|
// ^ DEI - Ignored
|
||||||
|
// ^^^^ ^^^^ ^^^^ VLAN ID
|
||||||
|
const vlanTag = input.slice(offset+2, offset+4);
|
||||||
|
vlans.push((vlanTag[0] & 0b00001111) << 4 | vlanTag[1]);
|
||||||
|
|
||||||
|
offset += 2;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const packetData = input.slice(offset);
|
||||||
|
|
||||||
|
if (outputFormat === "Packet data") {
|
||||||
|
return Utils.byteArrayToChars(packetData);
|
||||||
|
} else if (outputFormat === "Packet data (hex)") {
|
||||||
|
return toHex(packetData);
|
||||||
|
} else if (outputFormat === "Text output") {
|
||||||
|
let retval = `Source MAC: ${toHex(sourceMac, ":")}\nDestination MAC: ${toHex(destinationMac, ":")}\n`;
|
||||||
|
if (vlans.length > 0) {
|
||||||
|
retval += `VLAN: ${vlans.join(", ")}\n`;
|
||||||
|
}
|
||||||
|
retval += `Data:\n${toHex(packetData)}`;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ParseEthernetFrame;
|
||||||
@ -33,6 +33,12 @@ class ParseIPv4Header extends Operation {
|
|||||||
"name": "Input format",
|
"name": "Input format",
|
||||||
"type": "option",
|
"type": "option",
|
||||||
"value": ["Hex", "Raw"]
|
"value": ["Hex", "Raw"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Output format",
|
||||||
|
"type": "option",
|
||||||
|
"value": ["Table", "Data (hex)", "Data (raw)"],
|
||||||
|
defaultIndex: 0,
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -44,6 +50,8 @@ class ParseIPv4Header extends Operation {
|
|||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const format = args[0];
|
const format = args[0];
|
||||||
|
const outputFormat = args[1];
|
||||||
|
|
||||||
let output;
|
let output;
|
||||||
|
|
||||||
if (format === "Hex") {
|
if (format === "Hex") {
|
||||||
@ -98,7 +106,10 @@ class ParseIPv4Header extends Operation {
|
|||||||
checksumResult = givenChecksum + " (incorrect, should be " + correctChecksum + ")";
|
checksumResult = givenChecksum + " (incorrect, should be " + correctChecksum + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
output = `<table class='table table-hover table-sm table-bordered table-nonfluid'><tr><th>Field</th><th>Value</th></tr>
|
const data = input.slice(ihl * 4);
|
||||||
|
|
||||||
|
if (outputFormat === "Table") {
|
||||||
|
output = `<table class='table table-hover table-sm table-bordered table-nonfluid'><tr><th>Field</th><th>Value</th></tr>
|
||||||
<tr><td>Version</td><td>${version}</td></tr>
|
<tr><td>Version</td><td>${version}</td></tr>
|
||||||
<tr><td>Internet Header Length (IHL)</td><td>${ihl} (${ihl * 4} bytes)</td></tr>
|
<tr><td>Internet Header Length (IHL)</td><td>${ihl} (${ihl * 4} bytes)</td></tr>
|
||||||
<tr><td>Differentiated Services Code Point (DSCP)</td><td>${dscp}</td></tr>
|
<tr><td>Differentiated Services Code Point (DSCP)</td><td>${dscp}</td></tr>
|
||||||
@ -116,13 +127,19 @@ class ParseIPv4Header extends Operation {
|
|||||||
<tr><td>Protocol</td><td>${protocol}, ${protocolInfo.protocol} (${protocolInfo.keyword})</td></tr>
|
<tr><td>Protocol</td><td>${protocol}, ${protocolInfo.protocol} (${protocolInfo.keyword})</td></tr>
|
||||||
<tr><td>Header checksum</td><td>${checksumResult}</td></tr>
|
<tr><td>Header checksum</td><td>${checksumResult}</td></tr>
|
||||||
<tr><td>Source IP address</td><td>${ipv4ToStr(srcIP)}</td></tr>
|
<tr><td>Source IP address</td><td>${ipv4ToStr(srcIP)}</td></tr>
|
||||||
<tr><td>Destination IP address</td><td>${ipv4ToStr(dstIP)}</td></tr>`;
|
<tr><td>Destination IP address</td><td>${ipv4ToStr(dstIP)}</td></tr>
|
||||||
|
<tr><td>Data (hex)</td><td>${toHex(data)}</td></tr>`;
|
||||||
|
|
||||||
if (ihl > 5) {
|
if (ihl > 5) {
|
||||||
output += `<tr><td>Options</td><td>${toHex(options)}</td></tr>`;
|
output += `<tr><td>Options</td><td>${toHex(options)}</td></tr>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output + "</table>";
|
||||||
|
} else if (outputFormat === "Data (hex)") {
|
||||||
|
return toHex(data);
|
||||||
|
} else if (outputFormat === "Data (raw)") {
|
||||||
|
return Utils.byteArrayToChars(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return output + "</table>";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -130,6 +130,7 @@ class Manager {
|
|||||||
// Controls
|
// Controls
|
||||||
document.getElementById("bake").addEventListener("click", this.controls.bakeClick.bind(this.controls));
|
document.getElementById("bake").addEventListener("click", this.controls.bakeClick.bind(this.controls));
|
||||||
document.getElementById("auto-bake").addEventListener("change", this.controls.autoBakeChange.bind(this.controls));
|
document.getElementById("auto-bake").addEventListener("change", this.controls.autoBakeChange.bind(this.controls));
|
||||||
|
document.getElementById("auto-bake").addEventListener("keydown", this.controls.autoBakeKeyboardHandler.bind(this.controls));
|
||||||
document.getElementById("step").addEventListener("click", this.controls.stepClick.bind(this.controls));
|
document.getElementById("step").addEventListener("click", this.controls.stepClick.bind(this.controls));
|
||||||
document.getElementById("clr-recipe").addEventListener("click", this.controls.clearRecipeClick.bind(this.controls));
|
document.getElementById("clr-recipe").addEventListener("click", this.controls.clearRecipeClick.bind(this.controls));
|
||||||
document.getElementById("save").addEventListener("click", this.controls.saveClick.bind(this.controls));
|
document.getElementById("save").addEventListener("click", this.controls.saveClick.bind(this.controls));
|
||||||
|
|||||||
@ -145,7 +145,7 @@
|
|||||||
<button type="button" aria-label="Edit Favourites" class="btn btn-warning bmd-btn-icon" id="edit-favourites" data-toggle="tooltip" title="Edit favourites">
|
<button type="button" aria-label="Edit Favourites" class="btn btn-warning bmd-btn-icon" id="edit-favourites" data-toggle="tooltip" title="Edit favourites">
|
||||||
<i class="material-icons" aria-hidden="true">star</i>
|
<i class="material-icons" aria-hidden="true">star</i>
|
||||||
</button>
|
</button>
|
||||||
<div id="content-wrapper">
|
<div tabindex="0" id="content-wrapper">
|
||||||
<div id="banner" class="row">
|
<div id="banner" class="row">
|
||||||
<div class="col" style="text-align: left; padding-left: 10px;">
|
<div class="col" style="text-align: left; padding-left: 10px;">
|
||||||
<a href="#" data-toggle="modal" data-target="#download-modal" data-help-title="Downloading CyberChef" data-help="<p>CyberChef can be downloaded to run locally or hosted within your own network. It has no server-side component so all that is required is that the ZIP file is uncompressed and the files are accessible.</p><p>As a user, it is worth noting that unofficial versions of CyberChef could have been modified to introduce Input and/or Recipe exfiltration. We recommend always using the official, open source, up-to-date version of CyberChef hosted at <a href='https://gchq.github.io/CyberChef'>https://gchq.github.io/CyberChef</a> if accessible.</p><p>The Network tab in your browser's Developer console (F12) can be used to inspect the network requests made by a website. This can confirm that no data is uploaded when a CyberChef recipe is baked.</p>">Download CyberChef <i class="material-icons">file_download</i></a>
|
<a href="#" data-toggle="modal" data-target="#download-modal" data-help-title="Downloading CyberChef" data-help="<p>CyberChef can be downloaded to run locally or hosted within your own network. It has no server-side component so all that is required is that the ZIP file is uncompressed and the files are accessible.</p><p>As a user, it is worth noting that unofficial versions of CyberChef could have been modified to introduce Input and/or Recipe exfiltration. We recommend always using the official, open source, up-to-date version of CyberChef hosted at <a href='https://gchq.github.io/CyberChef'>https://gchq.github.io/CyberChef</a> if accessible.</p><p>The Network tab in your browser's Developer console (F12) can be used to inspect the network requests made by a website. This can confirm that no data is uploaded when a CyberChef recipe is baked.</p>">Download CyberChef <i class="material-icons">file_download</i></a>
|
||||||
@ -173,7 +173,7 @@
|
|||||||
Operations
|
Operations
|
||||||
<span class="op-count"></span>
|
<span class="op-count"></span>
|
||||||
</div>
|
</div>
|
||||||
<input id="search" type="search" class="form-control" placeholder="Search..." autocomplete="off" tabindex="2" data-help-title="Searching for operations" data-help="<p>Use the search box to find useful operations.</p><p>Both operation names and descriptions are queried using a fuzzy matching algorithm.</p>">
|
<input id="search" type="search" class="form-control" placeholder="Search..." autocomplete="off" tabindex="0" data-help-title="Searching for operations" data-help="<p>Use the search box to find useful operations.</p><p>Both operation names and descriptions are queried using a fuzzy matching algorithm.</p>">
|
||||||
<ul id="search-results" class="op-list"></ul>
|
<ul id="search-results" class="op-list"></ul>
|
||||||
<div id="categories" class="panel-group no-select"></div>
|
<div id="categories" class="panel-group no-select"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -584,22 +584,22 @@
|
|||||||
|
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link active" href="#faqs" aria-controls="profile" role="tab" data-toggle="tab">
|
<a id="tab-1" class="nav-link active" href="#faqs" aria-controls="profile" role="tab" data-toggle="tab">
|
||||||
FAQs
|
FAQs
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" href="#report-bug" aria-controls="messages" role="tab" data-toggle="tab">
|
<a id="tab-2" class="nav-link" href="#report-bug" aria-controls="messages" role="tab" data-toggle="tab">
|
||||||
Report a bug
|
Report a bug
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" href="#about" aria-controls="messages" role="tab" data-toggle="tab">
|
<a id="tab-3" class="nav-link" href="#about" aria-controls="messages" role="tab" data-toggle="tab">
|
||||||
About
|
About
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" href="#keybindings" aria-controls="messages" role="tab" data-toggle="tab">
|
<a id="tab-4" class="nav-link" href="#keybindings" aria-controls="messages" role="tab" data-toggle="tab">
|
||||||
Keybindings
|
Keybindings
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@ -26,6 +26,36 @@
|
|||||||
color: var(--banner-url-colour);
|
color: var(--banner-url-colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#options:focus {
|
||||||
|
background-color: #eef3ec;
|
||||||
|
border: solid black 2px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#support:focus {
|
||||||
|
background-color: #eef3ec;
|
||||||
|
border: solid black 2px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notice:focus {
|
||||||
|
background-color: #eef3ec;
|
||||||
|
border: solid black 2px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#banner .col a:focus {
|
||||||
|
background-color: #eef3ec;
|
||||||
|
border: solid black 2px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notice-wrapper #notice:focus {
|
||||||
|
background-color: #eef3ec;
|
||||||
|
border: solid black 2px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
#notice-wrapper {
|
#notice-wrapper {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@ -78,6 +78,11 @@
|
|||||||
border-left: 2px solid var(--primary-border-colour);
|
border-left: 2px solid var(--primary-border-colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p a:focus {
|
||||||
|
color: #0a6ebd;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox label input[type=checkbox]+.checkbox-decorator .check,
|
.checkbox label input[type=checkbox]+.checkbox-decorator .check,
|
||||||
.checkbox label input[type=checkbox]+.checkbox-decorator .check::before {
|
.checkbox label input[type=checkbox]+.checkbox-decorator .check::before {
|
||||||
border-color: var(--input-border-colour);
|
border-color: var(--input-border-colour);
|
||||||
|
|||||||
@ -57,6 +57,18 @@ class ControlsWaiter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks or unchecks the Auto Bake checkbox with "Enter"
|
||||||
|
* @param {Event} ev
|
||||||
|
*/
|
||||||
|
autoBakeKeyboardHandler(ev) {
|
||||||
|
const checkBox = document.getElementById("auto-bake");
|
||||||
|
ev.preventDefault();
|
||||||
|
if (ev.key === "Enter" || ev.key === " ") {
|
||||||
|
checkBox.checked = !checkBox.checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler to trigger baking.
|
* Handler to trigger baking.
|
||||||
@ -387,6 +399,18 @@ class ControlsWaiter {
|
|||||||
*/
|
*/
|
||||||
supportButtonClick(e) {
|
supportButtonClick(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
const faqs = document.getElementById("faqs");
|
||||||
|
const faqsAElement = faqs.getElementsByTagName("a");
|
||||||
|
for (let i = 0; i < faqsAElement.length; i++) {
|
||||||
|
faqsAElement[i].setAttribute("tabindex", "0");
|
||||||
|
faqsAElement[i].addEventListener("keydown", this.navigateFAQList, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabs = document.querySelectorAll('[role="tab"]');
|
||||||
|
|
||||||
|
for (let i = 0; i < tabs.length; i++) {
|
||||||
|
tabs[i].addEventListener("keydown", this.changeTabs, false);
|
||||||
|
}
|
||||||
|
|
||||||
const reportBugInfo = document.getElementById("report-bug-info");
|
const reportBugInfo = document.getElementById("report-bug-info");
|
||||||
const saveLink = this.generateStateUrl(true, true, null, null, "https://gchq.github.io/CyberChef/");
|
const saveLink = this.generateStateUrl(true, true, null, null, "https://gchq.github.io/CyberChef/");
|
||||||
@ -403,6 +427,64 @@ ${navigator.userAgent}
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Event} ev
|
||||||
|
*/
|
||||||
|
changeTabs(ev) {
|
||||||
|
const tab = ev.target;
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
if (ev.key === "ArrowRight") {
|
||||||
|
const nextTab = tab.parentElement;
|
||||||
|
if (nextTab.nextElementSibling === null) {
|
||||||
|
tab.parentElement.parentElement.firstElementChild.firstElementChild.focus();
|
||||||
|
} else {
|
||||||
|
nextTab.nextElementSibling.firstElementChild.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (ev.key === "ArrowLeft") {
|
||||||
|
const prevTab = tab.parentElement;
|
||||||
|
|
||||||
|
if (prevTab.previousElementSibling === null) {
|
||||||
|
tab.parentElement.parentElement.lastElementChild.firstElementChild.focus();
|
||||||
|
} else {
|
||||||
|
prevTab.previousElementSibling.firstElementChild.focus();
|
||||||
|
}
|
||||||
|
} else if (ev.key === "Tab" && !ev.shiftKey && ev.target === document.getElementById("tab-1")) {
|
||||||
|
document.getElementById("faqs").querySelector("[class='btn btn-primary']").focus();
|
||||||
|
} else if (ev.key === "Tab" && !ev.shiftKey && ev.target === document.getElementById("tab-2")) {
|
||||||
|
document.getElementById("report-bug").querySelector("[class='btn btn-primary']").focus();
|
||||||
|
} else if (ev.key === "Tab" && !ev.shiftKey && ev.target === document.getElementById("tab-3")) {
|
||||||
|
document.getElementById("about").querySelector("[href]").focus();
|
||||||
|
} else if (ev.key === "Tab" && !ev.shiftKey && ev.target === document.getElementById("tab-4")) {
|
||||||
|
const button = document.getElementById("support-modal").getElementsByClassName("modal-footer");
|
||||||
|
const close = button[0].firstElementChild;
|
||||||
|
close.focus();
|
||||||
|
} else if (ev.key === "Enter" || ev.key === "Space" || ev.key === " ") {
|
||||||
|
tab.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Event} ev
|
||||||
|
*/
|
||||||
|
navigateFAQList(ev) {
|
||||||
|
|
||||||
|
const el = ev.target.nextElementSibling;
|
||||||
|
if (ev.key === "Enter" || ev.key === "Space" || ev.key === " ") {
|
||||||
|
ev.preventDefault();
|
||||||
|
const question = el.classList;
|
||||||
|
if (question !== undefined && question.value) {
|
||||||
|
if (!question.value.includes("show")) {
|
||||||
|
question.add("show");
|
||||||
|
} else if (question.contains("show")) {
|
||||||
|
question.remove("show");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the stale indicator to show that the input or recipe has changed
|
* Shows the stale indicator to show that the input or recipe has changed
|
||||||
* since the last bake.
|
* since the last bake.
|
||||||
|
|||||||
@ -268,7 +268,7 @@ module.exports = {
|
|||||||
testOpHtml(browser, "Parse colour code", "#000", ".colorpicker-preview", "rgb(0, 0, 0)");
|
testOpHtml(browser, "Parse colour code", "#000", ".colorpicker-preview", "rgb(0, 0, 0)");
|
||||||
testOpHtml(browser, "Parse DateTime", "01/12/2000 13:00:00", "", /Date: Friday 1st December 2000/);
|
testOpHtml(browser, "Parse DateTime", "01/12/2000 13:00:00", "", /Date: Friday 1st December 2000/);
|
||||||
// testOp(browser, "Parse IP range", "test input", "test_output");
|
// testOp(browser, "Parse IP range", "test input", "test_output");
|
||||||
testOpHtml(browser, "Parse IPv4 header", "45 c0 00 c4 02 89 00 00 ff 11 1e 8c c0 a8 0c 01 c0 a8 0c 02", "tr:last-child td:last-child", "192.168.12.2");
|
testOpHtml(browser, "Parse IPv4 header", "45 c0 00 c4 02 89 00 00 ff 11 1e 8c c0 a8 0c 01 c0 a8 0c 02", "tr:nth-last-child(2) td:last-child", "192.168.12.2");
|
||||||
// testOp(browser, "Parse IPv6 address", "test input", "test_output");
|
// testOp(browser, "Parse IPv6 address", "test input", "test_output");
|
||||||
// testOp(browser, "Parse ObjectID timestamp", "test input", "test_output");
|
// testOp(browser, "Parse ObjectID timestamp", "test input", "test_output");
|
||||||
// testOp(browser, "Parse QR Code", "test input", "test_output");
|
// testOp(browser, "Parse QR Code", "test input", "test_output");
|
||||||
|
|||||||
@ -122,6 +122,7 @@ import "./tests/NetBIOS.mjs";
|
|||||||
import "./tests/NormaliseUnicode.mjs";
|
import "./tests/NormaliseUnicode.mjs";
|
||||||
import "./tests/NTLM.mjs";
|
import "./tests/NTLM.mjs";
|
||||||
import "./tests/OTP.mjs";
|
import "./tests/OTP.mjs";
|
||||||
|
import "./tests/ParseEthernetFrame.mjs";
|
||||||
import "./tests/ParseIPRange.mjs";
|
import "./tests/ParseIPRange.mjs";
|
||||||
import "./tests/ParseObjectIDTimestamp.mjs";
|
import "./tests/ParseObjectIDTimestamp.mjs";
|
||||||
import "./tests/ParseQRCode.mjs";
|
import "./tests/ParseQRCode.mjs";
|
||||||
|
|||||||
32
tests/operations/tests/Jq.mjs
Normal file
32
tests/operations/tests/Jq.mjs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Jq tests.
|
||||||
|
*
|
||||||
|
* @author rtpt-romankarwacik [roman.karwacik@redteam-pentesting.de]
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import TestRegister from "../../lib/TestRegister.mjs";
|
||||||
|
|
||||||
|
TestRegister.addTests([
|
||||||
|
{
|
||||||
|
name: "Get raw JSON Property",
|
||||||
|
input: '{"data": "testString\\u0000"}',
|
||||||
|
expectedOutput: "testString\u0000",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Jq",
|
||||||
|
args: [".data", true],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Get JSON Property",
|
||||||
|
input: '{"data": "testString\\u0000"}',
|
||||||
|
expectedOutput: "\"testString\\u0000\"",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Jq",
|
||||||
|
args: [".data", false],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
45
tests/operations/tests/ParseEthernetFrame.mjs
Normal file
45
tests/operations/tests/ParseEthernetFrame.mjs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* Parse Ethernet frame tests.
|
||||||
|
*
|
||||||
|
* @author tedk [tedk@ted.do]
|
||||||
|
* @copyright Crown Copyright 2017
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
import TestRegister from "../../lib/TestRegister.mjs";
|
||||||
|
|
||||||
|
TestRegister.addTests([
|
||||||
|
{
|
||||||
|
name: "Parse plain Ethernet frame",
|
||||||
|
input: "000000000000ffffffffffff08004500",
|
||||||
|
expectedOutput: "Source MAC: ff:ff:ff:ff:ff:ff\nDestination MAC: 00:00:00:00:00:00\nData:\n45 00",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Parse Ethernet frame",
|
||||||
|
"args": ["Hex", "Text output"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// Example PCAP data from: https://packetlife.net/captures/protocol/vlan/
|
||||||
|
{
|
||||||
|
name: "Parse Ethernet frame with one VLAN tag (802.1q)",
|
||||||
|
input: "01000ccdcdd00013c3dfae188100a0760165aaaa",
|
||||||
|
expectedOutput: "Source MAC: 00:13:c3:df:ae:18\nDestination MAC: 01:00:0c:cd:cd:d0\nVLAN: 117\nData:\naa aa",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Parse Ethernet frame",
|
||||||
|
"args": ["Hex", "Text output"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Parse Ethernet frame with two VLAN tags (802.1ad)",
|
||||||
|
input: "0019aa7de688002155c8f13c810000d18100001408004500",
|
||||||
|
expectedOutput: "Source MAC: 00:21:55:c8:f1:3c\nDestination MAC: 00:19:aa:7d:e6:88\nVLAN: 16, 128\nData:\n45 00",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Parse Ethernet frame",
|
||||||
|
"args": ["Hex", "Text output"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]);
|
||||||
Loading…
x
Reference in New Issue
Block a user