From 3e8c5d945cd65e920930ddd6dfb012123cd6a0a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 27 Feb 2023 06:09:30 +0000 Subject: [PATCH 01/46] Add "XOR Checksum" operation --- src/core/config/Categories.json | 3 +- src/core/operations/XORChecksum.mjs | 59 +++++++++++++++++++ tests/operations/tests/Checksum.mjs | 90 ++++++++++++++++++++++++++++- 3 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 src/core/operations/XORChecksum.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 075e8d66..97b20bd7 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -390,7 +390,8 @@ "CRC-8 Checksum", "CRC-16 Checksum", "CRC-32 Checksum", - "TCP/IP Checksum" + "TCP/IP Checksum", + "XOR Checksum" ] }, { diff --git a/src/core/operations/XORChecksum.mjs b/src/core/operations/XORChecksum.mjs new file mode 100644 index 00000000..1603a265 --- /dev/null +++ b/src/core/operations/XORChecksum.mjs @@ -0,0 +1,59 @@ +/** + * @author Thomas Weißschuh [thomas@t-8ch.de] + * @copyright Crown Copyright 2023 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import { toHex } from "../lib/Hex.mjs"; + +/** + * XOR Checksum operation + */ +class XORChecksum extends Operation { + + /** + * XORChecksum constructor + */ + constructor() { + super(); + + this.name = "XOR Checksum"; + this.module = "Crypto"; + this.description = "XOR Checksum splits the input into blocks of a configurable size and performs the XOR operation on these blocks."; + this.infoURL = "https://wikipedia.org/wiki/XOR"; + this.inputType = "ArrayBuffer"; + this.outputType = "string"; + this.args = [ + { + name: "Blocksize", + type: "number", + value: 4 + }, + ]; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const blocksize = args[0]; + input = new Uint8Array(input); + + const res = Array(blocksize); + res.fill(0); + + for (const chunk of Utils.chunked(input, blocksize)) { + for (let i = 0; i < blocksize; i++) { + res[i] ^= chunk[i]; + } + } + + return toHex(res, ""); + } +} + +export default XORChecksum; diff --git a/tests/operations/tests/Checksum.mjs b/tests/operations/tests/Checksum.mjs index 142ee267..5266ab99 100644 --- a/tests/operations/tests/Checksum.mjs +++ b/tests/operations/tests/Checksum.mjs @@ -237,5 +237,93 @@ TestRegister.addTests([ "args": [] } ] - } + }, + { + name: "XOR Checksum (1): nothing", + input: "", + expectedOutput: "00", + recipeConfig: [ + { + "op": "XOR Checksum", + "args": [1] + } + ] + }, + { + name: "XOR Checksum (1): basic string", + input: BASIC_STRING, + expectedOutput: "08", + recipeConfig: [ + { + "op": "XOR Checksum", + "args": [1] + } + ] + }, + { + name: "XOR Checksum (1): UTF-8", + input: UTF8_STR, + expectedOutput: "df", + recipeConfig: [ + { + "op": "XOR Checksum", + "args": [1] + } + ] + }, + { + name: "XOR Checksum (1): all bytes", + input: ALL_BYTES, + expectedOutput: "00", + recipeConfig: [ + { + "op": "XOR Checksum", + "args": [1] + } + ] + }, + { + name: "XOR Checksum (4): nothing", + input: "", + expectedOutput: "00000000", + recipeConfig: [ + { + "op": "XOR Checksum", + "args": [4] + } + ] + }, + { + name: "XOR Checksum (4): basic string", + input: BASIC_STRING, + expectedOutput: "4918421b", + recipeConfig: [ + { + "op": "XOR Checksum", + "args": [4] + } + ] + }, + { + name: "XOR Checksum (4): UTF-8", + input: UTF8_STR, + expectedOutput: "83a424dc", + recipeConfig: [ + { + "op": "XOR Checksum", + "args": [4] + } + ] + }, + { + name: "XOR Checksum (4): all bytes", + input: ALL_BYTES, + expectedOutput: "00000000", + recipeConfig: [ + { + "op": "XOR Checksum", + "args": [4] + } + ] + }, ]); From bf1e708a4cd465b35df6bede05a75f4b81fe1394 Mon Sep 17 00:00:00 2001 From: Brunon Blok <43315279+brun0ne@users.noreply.github.com> Date: Thu, 6 Apr 2023 20:32:50 +0000 Subject: [PATCH 02/46] added PHP Serialize operation --- src/core/operations/PHPSerialize.mjs | 144 ++++++++++++++++++++++++ tests/operations/tests/PHPSerialize.mjs | 112 ++++++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 src/core/operations/PHPSerialize.mjs create mode 100644 tests/operations/tests/PHPSerialize.mjs diff --git a/src/core/operations/PHPSerialize.mjs b/src/core/operations/PHPSerialize.mjs new file mode 100644 index 00000000..94f42cbb --- /dev/null +++ b/src/core/operations/PHPSerialize.mjs @@ -0,0 +1,144 @@ +/** + * @author brun0ne [brunonblok@gmail.com] + * @copyright Crown Copyright 2023 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * PHP Serialize operation + */ +class PHPSerialize extends Operation { + + /** + * PHPSerialize constructor + */ + constructor() { + super(); + + this.name = "PHP Serialize"; + this.module = "Default"; + this.description = "Performs PHP serialization on JSON data.

This function does not support object tags.

Since PHP doesn't distinguish dicts and arrays, this operation is not always symmetric to PHP Deserialize.

Example:
[5,"abc",true]
becomes
a:3:{i:0;i:5;i:1;s:3:"abc";i:2;b:1;}"; + this.infoURL = "https://www.phpinternalsbook.com/php5/classes_objects/serialization.html"; + this.inputType = "JSON"; + this.outputType = "string"; + this.args = []; + } + + /** + * @param {JSON} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + /** + * Determines if a number is an integer + * @param {number} value + * @returns {boolean} + */ + function isInteger(value) { + return typeof value === "number" && parseInt(value.toString(), 10) === value; + } + + /** + * Serialize basic types + * @param {string | number | boolean} content + * @returns {string} + */ + function serializeBasicTypes(content) { + const basicTypes = { + "string": "s", + "integer": "i", + "float": "d", + "boolean": "b" + }; + + /** + * Booleans + * cast to 0 or 1 + */ + if (typeof content === "boolean"){ + return `${basicTypes["boolean"]}:${content ? 1 : 0}`; + } + + /** + * Numbers + */ + if (typeof content === "number"){ + if (isInteger(content)){ + return `${basicTypes["integer"]}:${content.toString()}` + } + else { + return `${basicTypes["float"]}:${content.toString()}` + } + } + + /** + * Strings + */ + if (typeof content === "string") + return `${basicTypes["string"]}:${content.length}:"${content}"`; + + /** This should be unreachable */ + throw new OperationError(`Encountered a non-implemented type: ${typeof content}`); + } + + /** + * Recursively serialize + * @param {*} object + * @returns {string} + */ + function serialize(object) { + /** + * Null + */ + if (object == null) { + return `N;` + } + + /** + * Basic types + */ + if (typeof object !== "object"){ + return `${serializeBasicTypes(object)};`; + } + + /** + * Arrays + */ + else if (object instanceof Array) { + const serializedElements = []; + + for (let i = 0; i < object.length; i++) { + serializedElements.push(`${serialize(i)}${serialize(object[i])}`); + } + + return `a:${object.length}:{${serializedElements.join("")}}` + } + + /** + * Objects + * Note: the output cannot be guaranteed to be in the same order as the input + */ + else if (object instanceof Object) { + const serializedElements = []; + const keys = Object.keys(object); + + for (const key of keys) { + serializedElements.push(`${serialize(key)}${serialize(object[key])}`); + } + + return `a:${keys.length}:{${serializedElements.join("")}}` + } + + /** This should be unreachable */ + throw new OperationError(`Encountered a non-implemented type: ${typeof object}`); + } + + return serialize(input); + } +} + +export default PHPSerialize; diff --git a/tests/operations/tests/PHPSerialize.mjs b/tests/operations/tests/PHPSerialize.mjs new file mode 100644 index 00000000..fa6e87c5 --- /dev/null +++ b/tests/operations/tests/PHPSerialize.mjs @@ -0,0 +1,112 @@ +/** + * PHP Serialization tests. + * + * @author brun0ne [brunonblok@gmail.com] + * + * @copyright Crown Copyright 2023 + * @license Apache-2.0 + */ + +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "PHP Serialize empty array", + input: "[]", + expectedOutput: "a:0:{}", + recipeConfig: [ + { + op: "PHP Serialize", + args: [] + } + ] + }, + { + name: "PHP Serialize empty object", + input: "{}", + expectedOutput: "a:0:{}", + recipeConfig: [ + { + op: "PHP Serialize", + args: [] + } + ] + }, + { + name: "PHP Serialize null", + input: "null", + expectedOutput: "N;", + recipeConfig: [ + { + op: "PHP Serialize", + args: [] + } + ] + }, + { + name: "PHP Serialize integer", + input: "10", + expectedOutput: "i:10;", + recipeConfig: [ + { + op: "PHP Serialize", + args: [] + } + ] + }, + { + name: "PHP Serialize float", + input: "14.523", + expectedOutput: "d:14.523;", + recipeConfig: [ + { + op: "PHP Serialize", + args: [] + } + ] + }, + { + name: "PHP Serialize boolean", + input: "[true, false]", + expectedOutput: "a:2:{i:0;b:1;i:1;b:0;}", + recipeConfig: [ + { + op: "PHP Serialize", + args: [] + } + ] + }, + { + name: "PHP Serialize string", + input: "\"Test string to serialize\"", + expectedOutput: "s:24:\"Test string to serialize\";", + recipeConfig: [ + { + op: "PHP Serialize", + args: [] + } + ] + }, + { + name: "PHP Serialize object", + input: "{\"a\": 10,\"0\": {\"ab\": true}}", + expectedOutput: "a:2:{s:1:\"0\";a:1:{s:2:\"ab\";b:1;}s:1:\"a\";i:10;}", + recipeConfig: [ + { + op: "PHP Serialize", + args: [] + } + ] + }, + { + name: "PHP Serialize array", + input: "[1,\"abc\",true,{\"x\":1,\"y\":2}]", + expectedOutput: "a:4:{i:0;i:1;i:1;s:3:\"abc\";i:2;b:1;i:3;a:2:{s:1:\"x\";i:1;s:1:\"y\";i:2;}}", + recipeConfig: [ + { + op: "PHP Serialize", + args: [] + } + ] + } +]); From 15b426ebb6e18414259f000640f5287c431b5a25 Mon Sep 17 00:00:00 2001 From: Brunon Blok <43315279+brun0ne@users.noreply.github.com> Date: Thu, 6 Apr 2023 21:03:46 +0000 Subject: [PATCH 03/46] clean up code formatting and fix missing entries --- src/core/config/Categories.json | 1 + src/core/operations/PHPSerialize.mjs | 74 +++++++++++++--------------- tests/operations/index.mjs | 1 + 3 files changed, 35 insertions(+), 41 deletions(-) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index ce2f01f5..c95637ef 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -418,6 +418,7 @@ "JPath expression", "CSS selector", "PHP Deserialize", + "PHP Serialize", "Microsoft Script Decoder", "Strip HTML tags", "Diff", diff --git a/src/core/operations/PHPSerialize.mjs b/src/core/operations/PHPSerialize.mjs index 94f42cbb..221af327 100644 --- a/src/core/operations/PHPSerialize.mjs +++ b/src/core/operations/PHPSerialize.mjs @@ -35,7 +35,7 @@ class PHPSerialize extends Operation { run(input, args) { /** * Determines if a number is an integer - * @param {number} value + * @param {number} value * @returns {boolean} */ function isInteger(value) { @@ -44,7 +44,7 @@ class PHPSerialize extends Operation { /** * Serialize basic types - * @param {string | number | boolean} content + * @param {string | number | boolean} content * @returns {string} */ function serializeBasicTypes(content) { @@ -54,40 +54,36 @@ class PHPSerialize extends Operation { "float": "d", "boolean": "b" }; - /** * Booleans * cast to 0 or 1 */ - if (typeof content === "boolean"){ - return `${basicTypes["boolean"]}:${content ? 1 : 0}`; + if (typeof content === "boolean") { + return `${basicTypes.boolean}:${content ? 1 : 0}`; } - /** * Numbers */ - if (typeof content === "number"){ - if (isInteger(content)){ - return `${basicTypes["integer"]}:${content.toString()}` - } - else { - return `${basicTypes["float"]}:${content.toString()}` + if (typeof content === "number") { + if (isInteger(content)) { + return `${basicTypes.integer}:${content.toString()}`; + } else { + return `${basicTypes.float}:${content.toString()}`; } } - /** * Strings */ if (typeof content === "string") - return `${basicTypes["string"]}:${content.length}:"${content}"`; - + return `${basicTypes.string}:${content.length}:"${content}"`; + /** This should be unreachable */ throw new OperationError(`Encountered a non-implemented type: ${typeof content}`); } /** * Recursively serialize - * @param {*} object + * @param {*} object * @returns {string} */ function serialize(object) { @@ -95,42 +91,38 @@ class PHPSerialize extends Operation { * Null */ if (object == null) { - return `N;` + return `N;`; } - - /** - * Basic types - */ - if (typeof object !== "object"){ + + if (typeof object !== "object") { + /** + * Basic types + */ return `${serializeBasicTypes(object)};`; - } - - /** - * Arrays - */ - else if (object instanceof Array) { + } else if (object instanceof Array) { + /** + * Arrays + */ const serializedElements = []; - + for (let i = 0; i < object.length; i++) { serializedElements.push(`${serialize(i)}${serialize(object[i])}`); } - - return `a:${object.length}:{${serializedElements.join("")}}` - } - - /** - * Objects - * Note: the output cannot be guaranteed to be in the same order as the input - */ - else if (object instanceof Object) { + + return `a:${object.length}:{${serializedElements.join("")}}`; + } else if (object instanceof Object) { + /** + * Objects + * Note: the output cannot be guaranteed to be in the same order as the input + */ const serializedElements = []; const keys = Object.keys(object); - + for (const key of keys) { serializedElements.push(`${serialize(key)}${serialize(object[key])}`); } - - return `a:${keys.length}:{${serializedElements.join("")}}` + + return `a:${keys.length}:{${serializedElements.join("")}}`; } /** This should be unreachable */ diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 56f432e0..0bf8fd3b 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -70,6 +70,7 @@ import "./tests/NormaliseUnicode.mjs"; import "./tests/OTP.mjs"; import "./tests/PGP.mjs"; import "./tests/PHP.mjs"; +import "./tests/PHPSerialize.mjs"; import "./tests/ParseIPRange.mjs"; import "./tests/ParseQRCode.mjs"; import "./tests/PEMtoHex.mjs"; From c0e84dcd501d12d0b322b2595f2c2424d6489b0e Mon Sep 17 00:00:00 2001 From: Brunon Blok <43315279+brun0ne@users.noreply.github.com> Date: Sun, 9 Apr 2023 19:06:59 +0000 Subject: [PATCH 04/46] change comments --- src/core/operations/PHPSerialize.mjs | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/core/operations/PHPSerialize.mjs b/src/core/operations/PHPSerialize.mjs index 221af327..00fb1380 100644 --- a/src/core/operations/PHPSerialize.mjs +++ b/src/core/operations/PHPSerialize.mjs @@ -61,9 +61,7 @@ class PHPSerialize extends Operation { if (typeof content === "boolean") { return `${basicTypes.boolean}:${content ? 1 : 0}`; } - /** - * Numbers - */ + /* Numbers */ if (typeof content === "number") { if (isInteger(content)) { return `${basicTypes.integer}:${content.toString()}`; @@ -71,9 +69,7 @@ class PHPSerialize extends Operation { return `${basicTypes.float}:${content.toString()}`; } } - /** - * Strings - */ + /* Strings */ if (typeof content === "string") return `${basicTypes.string}:${content.length}:"${content}"`; @@ -87,22 +83,16 @@ class PHPSerialize extends Operation { * @returns {string} */ function serialize(object) { - /** - * Null - */ + /* Null */ if (object == null) { return `N;`; } if (typeof object !== "object") { - /** - * Basic types - */ + /* Basic types */ return `${serializeBasicTypes(object)};`; } else if (object instanceof Array) { - /** - * Arrays - */ + /* Arrays */ const serializedElements = []; for (let i = 0; i < object.length; i++) { From 95f6c4c5d5600dda8b5ec7c30a58b0e7ed4e6a1e Mon Sep 17 00:00:00 2001 From: sw5678 <151949597+sw5678@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:40:54 +0100 Subject: [PATCH 05/46] Added alternating caps functionality --- src/core/config/Categories.json | 1 + src/core/operations/AlternatingCaps.mjs | 47 ++++++++++++++++++++++ tests/operations/index.mjs | 1 + tests/operations/tests/AlternatingCaps.mjs | 19 +++++++++ 4 files changed, 68 insertions(+) create mode 100644 src/core/operations/AlternatingCaps.mjs create mode 100644 tests/operations/tests/AlternatingCaps.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index bebdd6a5..a0a621ce 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -280,6 +280,7 @@ "To Upper case", "To Lower case", "Swap case", + "Alternating Caps", "To Case Insensitive Regex", "From Case Insensitive Regex", "Add line numbers", diff --git a/src/core/operations/AlternatingCaps.mjs b/src/core/operations/AlternatingCaps.mjs new file mode 100644 index 00000000..17ce3fed --- /dev/null +++ b/src/core/operations/AlternatingCaps.mjs @@ -0,0 +1,47 @@ +/** + * @author sw5678 + * @copyright Crown Copyright 2023 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; + +/** + * Alternating caps operation + */ +class AlternatingCaps extends Operation { + + /** + * AlternatingCaps constructor + */ + constructor() { + super(); + + this.name = "Alternating Caps"; + this.module = "Default"; + this.description = "Alternating caps, also known as studly caps, sticky caps, or spongecase is a form of text notation in which the capitalization of letters varies by some pattern, or arbitrarily. An example of this would be spelling 'alternative caps' as 'aLtErNaTiNg CaPs'."; + this.infoURL = "https://en.wikipedia.org/wiki/Alternating_caps"; + this.inputType = "string"; + this.outputType = "string"; + this.args= []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + let output = ""; + for (let i = 0; i < input.length; i++) { + if (i % 2 === 0) { + output += input[i].toLowerCase(); + } else { + output += input[i].toUpperCase(); + } + } + return output; + } +} + +export default AlternatingCaps; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 40ce7a2e..a7d52dff 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -18,6 +18,7 @@ import { import TestRegister from "../lib/TestRegister.mjs"; import "./tests/AESKeyWrap.mjs"; +import "./tests/AlternatingCaps.mjs"; import "./tests/AvroToJSON.mjs"; import "./tests/BaconCipher.mjs"; import "./tests/Base45.mjs"; diff --git a/tests/operations/tests/AlternatingCaps.mjs b/tests/operations/tests/AlternatingCaps.mjs new file mode 100644 index 00000000..b23768eb --- /dev/null +++ b/tests/operations/tests/AlternatingCaps.mjs @@ -0,0 +1,19 @@ +/* @author sw5678 + * @copyright Crown Copyright 2024 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + "name": "AlternatingCaps: Basic Example", + "input": "Hello, world!", + "expectedOutput": "hElLo, WoRlD!", + "recipeConfig": [ + { + "op": "Alternating Caps", + "args": [] + }, + ], + } +]); From f8b613b4e7cbb8ba8190711b8312bd3077300a2e Mon Sep 17 00:00:00 2001 From: PathToLife <12622625+PathToLife@users.noreply.github.com> Date: Thu, 13 Feb 2025 20:57:45 +1300 Subject: [PATCH 06/46] Docker multiplatform build support. Pending CI workflow test --- .github/workflows/releases.yml | 2 +- Dockerfile | 29 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index a068ffbb..586dba7b 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -61,7 +61,7 @@ jobs: tags: ${{ steps.image-metadata.outputs.tags }} labels: ${{ steps.image-metadata.outputs.labels }} containerfiles: ./Dockerfile - platforms: linux/amd64 + platforms: linux/amd64,linux/arm64,linux/arm/v7 oci: true # Webpack seems to use a lot of open files, increase the max open file limit to accomodate. extra-args: | diff --git a/Dockerfile b/Dockerfile index be4c8bad..09350891 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,30 @@ -FROM node:18-alpine AS build +##################################### +# Build the app to a static website # +##################################### +# Modifier --platform=$BUILDPLATFORM limits the platform to "BUILDPLATFORM" during buildx multi-platform builds +# This is because npm "chromedriver" package is not compatiable with all platforms +# For more info see: https://docs.docker.com/build/building/multi-platform/#cross-compilation +FROM --platform=$BUILDPLATFORM node:18-alpine AS builder +WORKDIR /app + +COPY package.json . +COPY package-lock.json . + +# Install dependencies +# --ignore-scripts do not run grunt postinstall script as it depends on files other than package.json +RUN npm ci --ignore-scripts + +# Build the app COPY . . -RUN npm ci + +# npm postinstall runs grunt, which depends on files other than package.json +RUN npm run postinstall RUN npm run build -FROM nginx:1.25-alpine3.18 AS cyberchef +######################################### +# Package static build files into nginx # +######################################### +FROM nginx:stable-alpine AS cyberchef -COPY --from=build ./build/prod /usr/share/nginx/html/ +COPY --from=builder /app/build/prod /usr/share/nginx/html/ From 5b15e754798d6d2552faf2f68413734cf1ebd505 Mon Sep 17 00:00:00 2001 From: Jon King Date: Sat, 27 May 2023 18:17:31 -0700 Subject: [PATCH 07/46] feat: add jsonata query operation --- package-lock.json | 9 +++++ package.json | 1 + src/core/operations/Jsonata.mjs | 65 +++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/core/operations/Jsonata.mjs diff --git a/package-lock.json b/package-lock.json index 50639ea8..9cdad930 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,6 +53,7 @@ "js-sha3": "^0.9.3", "jsesc": "^3.0.2", "json5": "^2.2.3", + "jsonata": "^2.0.3", "jsonpath-plus": "^9.0.0", "jsonwebtoken": "8.5.1", "jsqr": "^1.4.0", @@ -12447,6 +12448,14 @@ "node": ">=6" } }, + "node_modules/jsonata": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.0.6.tgz", + "integrity": "sha512-WhQB5tXQ32qjkx2GYHFw2XbL90u+LLzjofAYwi+86g6SyZeXHz9F1Q0amy3dWRYczshOC3Haok9J4pOCgHtwyQ==", + "engines": { + "node": ">= 8" + } + }, "node_modules/jsonpath-plus": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-9.0.0.tgz", diff --git a/package.json b/package.json index 20180e05..fd89c425 100644 --- a/package.json +++ b/package.json @@ -139,6 +139,7 @@ "js-sha3": "^0.9.3", "jsesc": "^3.0.2", "json5": "^2.2.3", + "jsonata": "^2.0.3", "jsonpath-plus": "^9.0.0", "jsonwebtoken": "8.5.1", "jsqr": "^1.4.0", diff --git a/src/core/operations/Jsonata.mjs b/src/core/operations/Jsonata.mjs new file mode 100644 index 00000000..41cf2751 --- /dev/null +++ b/src/core/operations/Jsonata.mjs @@ -0,0 +1,65 @@ +/** + * @author Jon K (jon@ajarsoftware.com) + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + */ + +import jsonata from "jsonata"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * Jsonata Query operation + */ +class JsonataQuery extends Operation { + /** + * JsonataQuery constructor + */ + constructor() { + super(); + + this.name = "Jsonata Query"; + this.module = "Code"; + this.description = + "Query and transform JSON data with a jsonata query."; + this.infoURL = "https://docs.jsonata.org/overview.html"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Query", + type: "text", + value: "string", + }, + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {JSON} + */ + async run(input, args) { + const [query] = args; + let result, jsonObj; + + try { + jsonObj = JSON.parse(input); + } catch (err) { + throw new OperationError(`Invalid input JSON: ${err.message}`); + } + + try { + const expression = jsonata(query); + result = await expression.evaluate(jsonObj); + } catch (err) { + throw new OperationError( + `Invalid Jsonata Expression: ${err.message}` + ); + } + + return JSON.stringify(result); + } +} + +export default JsonataQuery; From 4a428e802e47a13fa9ce52f5241922c28c63b1e6 Mon Sep 17 00:00:00 2001 From: Jon King Date: Sat, 27 May 2023 18:39:33 -0700 Subject: [PATCH 08/46] feat: add to category --- src/core/config/Categories.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index de3ea882..022d41b1 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -365,6 +365,7 @@ "Regular expression", "XPath expression", "JPath expression", + "Jsonata Query", "CSS selector", "Extract EXIF", "Extract ID3", From 2b2435210b3cae39518dd82356d75ff1965e01ab Mon Sep 17 00:00:00 2001 From: Jon King Date: Thu, 13 Jul 2023 11:33:07 -0700 Subject: [PATCH 09/46] chore: update return type from JSON to string --- src/core/operations/Jsonata.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/Jsonata.mjs b/src/core/operations/Jsonata.mjs index 41cf2751..1651e093 100644 --- a/src/core/operations/Jsonata.mjs +++ b/src/core/operations/Jsonata.mjs @@ -37,7 +37,7 @@ class JsonataQuery extends Operation { /** * @param {string} input * @param {Object[]} args - * @returns {JSON} + * @returns {string} */ async run(input, args) { const [query] = args; From c4574ff042f27246300d0eac98cf3330134bb6ff Mon Sep 17 00:00:00 2001 From: Jon King Date: Mon, 17 Feb 2025 10:15:13 -0700 Subject: [PATCH 10/46] fix: properly handle undefined results --- src/core/operations/Jsonata.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/Jsonata.mjs b/src/core/operations/Jsonata.mjs index 1651e093..82cc4d39 100644 --- a/src/core/operations/Jsonata.mjs +++ b/src/core/operations/Jsonata.mjs @@ -58,7 +58,7 @@ class JsonataQuery extends Operation { ); } - return JSON.stringify(result); + return JSON.stringify(result === undefined ? "" : result); } } From 4c6b4cd4e76dd19301c77b8670690b8e3419a996 Mon Sep 17 00:00:00 2001 From: Jon King Date: Mon, 17 Feb 2025 10:15:35 -0700 Subject: [PATCH 11/46] added Jsonata Query tests --- tests/operations/index.mjs | 10 +- tests/operations/tests/Jsonata.mjs | 551 +++++++++++++++++++++++++++++ 2 files changed, 555 insertions(+), 6 deletions(-) create mode 100644 tests/operations/tests/Jsonata.mjs diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index a82bc874..ba131f84 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -11,10 +11,7 @@ * @license Apache-2.0 */ -import { - setLongTestFailure, - logTestReport, -} from "../lib/utils.mjs"; +import { setLongTestFailure, logTestReport } from "../lib/utils.mjs"; import TestRegister from "../lib/TestRegister.mjs"; import "./tests/AESKeyWrap.mjs"; @@ -88,6 +85,7 @@ import "./tests/IndexOfCoincidence.mjs"; import "./tests/JA3Fingerprint.mjs"; import "./tests/JA4.mjs"; import "./tests/JA3SFingerprint.mjs"; +import "./tests/Jsonata.mjs"; import "./tests/JSONBeautify.mjs"; import "./tests/JSONMinify.mjs"; import "./tests/JSONtoCSV.mjs"; @@ -168,14 +166,14 @@ const testStatus = { allTestsPassing: true, counts: { total: 0, - } + }, }; setLongTestFailure(); const logOpsTestReport = logTestReport.bind(null, testStatus); -(async function() { +(async function () { const results = await TestRegister.runTests(); logOpsTestReport(results); })(); diff --git a/tests/operations/tests/Jsonata.mjs b/tests/operations/tests/Jsonata.mjs new file mode 100644 index 00000000..d664137f --- /dev/null +++ b/tests/operations/tests/Jsonata.mjs @@ -0,0 +1,551 @@ +/** + * JSON to CSV tests. + * + * @author Jon King [jon@ajarsoftware.com] + * + * @copyright Crown Copyright 2025 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +const INPUT_JSON_OBJECT_WITH_ARRAYS = `{ + "FirstName": "Fred", + "Surname": "Smith", + "Age": 28, + "Address": { + "Street": "Hursley Park", + "City": "Winchester", + "Postcode": "SO21 2JN" + }, + "Phone": [ + { + "type": "home", + "number": "0203 544 1234" + }, + { + "type": "office", + "number": "01962 001234" + }, + { + "type": "office", + "number": "01962 001235" + }, + { + "type": "mobile", + "number": "077 7700 1234" + } + ], + "Email": [ + { + "type": "work", + "address": ["fred.smith@my-work.com", "fsmith@my-work.com"] + }, + { + "type": "home", + "address": ["freddy@my-social.com", "frederic.smith@very-serious.com"] + } + ], + "Other": { + "Over 18 ?": true, + "Misc": null, + "Alternative.Address": { + "Street": "Brick Lane", + "City": "London", + "Postcode": "E1 6RF" + } + } +}`; + +const INPUT_ARRAY_OF_OBJECTS = `[ + { "ref": [ 1,2 ] }, + { "ref": [ 3,4 ] } +]`; + +const INPUT_NUMBER_ARRAY = `{ + "Numbers": [1, 2.4, 3.5, 10, 20.9, 30] +}`; + +TestRegister.addTests([ + { + name: "Jsonata: Returns a JSON string (double quoted)", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '"Smith"', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Surname"], + }, + ], + }, + { + name: "Jsonata: Returns a JSON number", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: "28", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Age"], + }, + ], + }, + { + name: "Jsonata: Field references are separated by '.'", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '"Winchester"', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Address.City"], + }, + ], + }, + { + name: "Jsonata: Matched the path and returns the null value", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: "null", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Other.Misc"], + }, + ], + }, + { + name: "Jsonata: Path not found. Returns nothing", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '""', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Other.DoesntExist"], + }, + ], + }, + { + name: "Jsonata: Field references containing whitespace or reserved tokens can be enclosed in backticks", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: "true", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Other.`Over 18 ?`"], + }, + ], + }, + { + name: "Jsonata: Returns the first item (an object)", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '{"type":"home","number":"0203 544 1234"}', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Phone[0]"], + }, + ], + }, + { + name: "Jsonata: Returns the second item", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '{"type":"office","number":"01962 001234"}', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Phone[1]"], + }, + ], + }, + { + name: "Jsonata: Returns the last item", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '{"type":"mobile","number":"077 7700 1234"}', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Phone[-1]"], + }, + ], + }, + { + name: "Jsonata: Negative indexed count from the end", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '{"type":"office","number":"01962 001235"}', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Phone[-2]"], + }, + ], + }, + { + name: "Jsonata: Doesn't exist - returns nothing", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '""', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Phone[8]"], + }, + ], + }, + { + name: "Jsonata: Selects the number field in the first item", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '"0203 544 1234"', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Phone[0].number"], + }, + ], + }, + { + name: "Jsonata: No index is given to Phone so it selects all of them (the whole array), then it selects all the number fields for each of them", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: + '["0203 544 1234","01962 001234","01962 001235","077 7700 1234"]', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Phone.number"], + }, + ], + }, + { + name: "Jsonata: Might expect it to just return the first number, but it returns the first number of each of the items selected by Phone", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: + '["0203 544 1234","01962 001234","01962 001235","077 7700 1234"]', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Phone.number[0]"], + }, + ], + }, + { + name: "Jsonata: Applies the index to the array returned by Phone.number.", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '"0203 544 1234"', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["(Phone.number)[0]"], + }, + ], + }, + { + name: "Jsonata: Returns a range of items by creating an array of indexes", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: + '[{"type":"home","number":"0203 544 1234"},{"type":"office","number":"01962 001234"}]', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Phone[[0..1]]"], + }, + ], + }, + // Predicates + { + name: "Jsonata: Select the Phone items that have a type field that equals 'mobile'", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '{"type":"mobile","number":"077 7700 1234"}', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Phone[type='mobile']"], + }, + ], + }, + { + name: "Jsonata: Select the mobile phone number", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '"077 7700 1234"', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Phone[type='mobile'].number"], + }, + ], + }, + { + name: "Jsonata: Select the office phone numbers - there are two of them", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '["01962 001234","01962 001235"]', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Phone[type='office'].number"], + }, + ], + }, + // Wildcards + { + name: "Jsonata: Select the values of all the fields of 'Address'", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '["Hursley Park","Winchester","SO21 2JN"]', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Address.*"], + }, + ], + }, + { + name: "Jsonata: Select the 'Postcode' value of any child object", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '"SO21 2JN"', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["*.Postcode"], + }, + ], + }, + { + name: "Jsonata: Select all Postcode values, regardless of how deeply nested they are in the structure", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '["SO21 2JN","E1 6RF"]', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["**.Postcode"], + }, + ], + }, + // String Expressions + { + name: "Jsonata: Concatenate 'FirstName' followed by space followed by 'Surname'", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '"Fred Smith"', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["FirstName & ' ' & Surname"], + }, + ], + }, + { + name: "Jsonata: Concatenates the 'Street' and 'City' from the 'Address' object with a comma separator", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '"Hursley Park, Winchester"', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Address.(Street & ', ' & City)"], + }, + ], + }, + { + name: "Jsonata: Casts the operands to strings, if necessary", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: '"50true"', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["5&0&true"], + }, + ], + }, + // Numeric Expressions + { + name: "Jsonata: Addition", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "3.4", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Numbers[0] + Numbers[1]"], + }, + ], + }, + { + name: "Jsonata: Subtraction", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "-19.9", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Numbers[0] - Numbers[4]"], + }, + ], + }, + { + name: "Jsonata: Multiplication", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "30", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Numbers[0] * Numbers[5]"], + }, + ], + }, + { + name: "Jsonata: Division", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "0.04784688995215311", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Numbers[0] / Numbers[4]"], + }, + ], + }, + { + name: "Jsonata: Modulus", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "3.5", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Numbers[2] % Numbers[5]"], + }, + ], + }, + { + name: "Jsonata: Equality", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "false", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Numbers[0] = Numbers[5]"], + }, + ], + }, + { + name: "Jsonata: Inequality", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "true", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Numbers[0] != Numbers[4]"], + }, + ], + }, + { + name: "Jsonata: Less than", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "true", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Numbers[0] < Numbers[4]"], + }, + ], + }, + { + name: "Jsonata: Less than or equal to", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "true", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Numbers[0] <= Numbers[4]"], + }, + ], + }, + { + name: "Jsonata: Greater than", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "false", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Numbers[0] > Numbers[4]"], + }, + ], + }, + { + name: "Jsonata: Greater than or equal to", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "false", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["Numbers[2] >= Numbers[4]"], + }, + ], + }, + { + name: "Jsonata: Value is contained in", + input: INPUT_JSON_OBJECT_WITH_ARRAYS, + expectedOutput: "true", + recipeConfig: [ + { + op: "Jsonata Query", + args: ['"01962 001234" in Phone.number'], + }, + ], + }, + // Boolean Expressions + { + name: "Jsonata: and", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "true", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["(Numbers[2] != 0) and (Numbers[5] != Numbers[1])"], + }, + ], + }, + { + name: "Jsonata: or", + input: INPUT_NUMBER_ARRAY, + expectedOutput: "true", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["(Numbers[2] != 0) or (Numbers[5] = Numbers[1])"], + }, + ], + }, + // Array tests + { + name: "Jsonata: $ at the start of an expression refers to the entire input document, subscripting it with 0 selects the first item", + input: INPUT_ARRAY_OF_OBJECTS, + expectedOutput: '{"ref":[1,2]}', + recipeConfig: [ + { + op: "Jsonata Query", + args: ["$[0]"], + }, + ], + }, + { + name: "Jsonata: .ref here returns the entire internal array", + input: INPUT_ARRAY_OF_OBJECTS, + expectedOutput: "[1,2]", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["$[0].ref"], + }, + ], + }, + { + name: "Jsonata: returns element on first position of the internal array", + input: INPUT_ARRAY_OF_OBJECTS, + expectedOutput: "1", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["$[0].ref[0]"], + }, + ], + }, + { + name: "Jsonata: $.field_reference flattens the result into a single array", + input: INPUT_ARRAY_OF_OBJECTS, + expectedOutput: "[1,2,3,4]", + recipeConfig: [ + { + op: "Jsonata Query", + args: ["$.ref"], + }, + ], + }, +]); From 91125f00e7abf09298e251ff7106a5ff0e356767 Mon Sep 17 00:00:00 2001 From: Jon King Date: Mon, 17 Feb 2025 10:19:38 -0700 Subject: [PATCH 12/46] updated Jsonata Query tests header title --- tests/operations/tests/Jsonata.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/operations/tests/Jsonata.mjs b/tests/operations/tests/Jsonata.mjs index d664137f..fb46a961 100644 --- a/tests/operations/tests/Jsonata.mjs +++ b/tests/operations/tests/Jsonata.mjs @@ -1,5 +1,5 @@ /** - * JSON to CSV tests. + * Jsonata Query tests. * * @author Jon King [jon@ajarsoftware.com] * From 2c32a64bfed7698d5d4b3daf82c5de1fd8f928a3 Mon Sep 17 00:00:00 2001 From: Odyhibit Date: Fri, 21 Feb 2025 20:40:37 -0600 Subject: [PATCH 13/46] Remove trim from rail fence. --- src/core/operations/RailFenceCipherDecode.mjs | 2 +- src/core/operations/RailFenceCipherEncode.mjs | 2 +- tests/operations/tests/Ciphers.mjs | 11 +++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/core/operations/RailFenceCipherDecode.mjs b/src/core/operations/RailFenceCipherDecode.mjs index be54ee12..39795f21 100644 --- a/src/core/operations/RailFenceCipherDecode.mjs +++ b/src/core/operations/RailFenceCipherDecode.mjs @@ -72,7 +72,7 @@ class RailFenceCipherDecode extends Operation { } } - return plaintext.join("").trim(); + return plaintext.join(""); } } diff --git a/src/core/operations/RailFenceCipherEncode.mjs b/src/core/operations/RailFenceCipherEncode.mjs index 03651f85..89eddde7 100644 --- a/src/core/operations/RailFenceCipherEncode.mjs +++ b/src/core/operations/RailFenceCipherEncode.mjs @@ -66,7 +66,7 @@ class RailFenceCipherEncode extends Operation { rows[rowIdx] += plaintext[pos]; } - return rows.join("").trim(); + return rows.join(""); } } diff --git a/tests/operations/tests/Ciphers.mjs b/tests/operations/tests/Ciphers.mjs index 47453cf7..16aba950 100644 --- a/tests/operations/tests/Ciphers.mjs +++ b/tests/operations/tests/Ciphers.mjs @@ -528,4 +528,15 @@ TestRegister.addTests([ } ], }, + { + name: "Rail Fence Cipher Encode: Normal with Offset with Spaces", + input: "No one expects the spanish Inquisition.", + expectedOutput: " e n ut.ooeepcstesaihIqiiinNnxthpsnso", + recipeConfig: [ + { + "op": "Rail Fence Cipher Encode", + "args": [3, 2] + } + ], + }, ]); From 3057a20791da7385f629ce593fed924219730e0f Mon Sep 17 00:00:00 2001 From: peterc-s Date: Fri, 28 Feb 2025 16:52:15 +0000 Subject: [PATCH 14/46] Add Base32 Hex Extended option. --- src/core/lib/Base32.mjs | 23 ++++ src/core/operations/FromBase32.mjs | 12 +- src/core/operations/ToBase32.mjs | 6 +- tests/operations/index.mjs | 1 + tests/operations/tests/Base32.mjs | 176 +++++++++++++++++++++++++++++ 5 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 src/core/lib/Base32.mjs create mode 100644 tests/operations/tests/Base32.mjs diff --git a/src/core/lib/Base32.mjs b/src/core/lib/Base32.mjs new file mode 100644 index 00000000..92b76eca --- /dev/null +++ b/src/core/lib/Base32.mjs @@ -0,0 +1,23 @@ +// import Utils from "../Utils.mjs"; + +/** + * Base32 resources. + * + * @author Peter C-S [petercs@purelymail.com] + * @license Apache-2.0 + */ + +/** + * Base32 alphabets. + */ +export const ALPHABET_OPTIONS = [ + { + name: "Standard", // https://www.rfc-editor.org/rfc/rfc4648#section-6 + value: "A-Z2-7=", + }, + { + name: "Hex Extended", // https://www.rfc-editor.org/rfc/rfc4648#section-7 + value: "0-9A-V=", + }, +]; + diff --git a/src/core/operations/FromBase32.mjs b/src/core/operations/FromBase32.mjs index 73added6..8ee0f1f8 100644 --- a/src/core/operations/FromBase32.mjs +++ b/src/core/operations/FromBase32.mjs @@ -6,6 +6,8 @@ import Operation from "../Operation.mjs"; import Utils from "../Utils.mjs"; +import {ALPHABET_OPTIONS} from "../lib/Base32.mjs"; + /** * From Base32 operation @@ -27,8 +29,8 @@ class FromBase32 extends Operation { this.args = [ { name: "Alphabet", - type: "binaryString", - value: "A-Z2-7=" + type: "editableOption", + value: ALPHABET_OPTIONS }, { name: "Remove non-alphabet chars", @@ -41,6 +43,11 @@ class FromBase32 extends Operation { pattern: "^(?:[A-Z2-7]{8})+(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}={1})?$", flags: "", args: ["A-Z2-7=", false] + }, + { + pattern: "^(?:[0-9A-V]{8})+(?:[0-9A-V]{2}={6}|[0-9A-V]{4}={4}|[0-9A-V]{5}={3}|[0-9A-V]{7}={1})?$", + flags: "", + args: ["0-9A-V=", false] } ]; } @@ -96,3 +103,4 @@ class FromBase32 extends Operation { } export default FromBase32; + diff --git a/src/core/operations/ToBase32.mjs b/src/core/operations/ToBase32.mjs index fd36f550..44eb8b48 100644 --- a/src/core/operations/ToBase32.mjs +++ b/src/core/operations/ToBase32.mjs @@ -6,6 +6,7 @@ import Operation from "../Operation.mjs"; import Utils from "../Utils.mjs"; +import {ALPHABET_OPTIONS} from "../lib/Base32.mjs"; /** * To Base32 operation @@ -27,8 +28,8 @@ class ToBase32 extends Operation { this.args = [ { name: "Alphabet", - type: "binaryString", - value: "A-Z2-7=" + type: "editableOption", + value: ALPHABET_OPTIONS } ]; } @@ -83,3 +84,4 @@ class ToBase32 extends Operation { } export default ToBase32; + diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index a82bc874..275f76a7 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -20,6 +20,7 @@ import TestRegister from "../lib/TestRegister.mjs"; import "./tests/AESKeyWrap.mjs"; import "./tests/AvroToJSON.mjs"; import "./tests/BaconCipher.mjs"; +import "./tests/Base32.mjs"; import "./tests/Base45.mjs"; import "./tests/Base58.mjs"; import "./tests/Base62.mjs"; diff --git a/tests/operations/tests/Base32.mjs b/tests/operations/tests/Base32.mjs new file mode 100644 index 00000000..760cdf14 --- /dev/null +++ b/tests/operations/tests/Base32.mjs @@ -0,0 +1,176 @@ +/** + * Base32 Tests + * + * @author Peter C-S [petercs@purelymail.com] + * @license Apache-2.0 + */ + +import TestRegister from "../../lib/TestRegister.mjs"; +import {ALPHABET_OPTIONS} from "../../../src/core/lib/Base32.mjs"; + +// Example Standard Base32 Tests +const STANDARD_INP = "HELLO BASE32"; +const STANDARD_OUT = "JBCUYTCPEBBECU2FGMZA===="; + +// Example Hex Extended Base32 Tests +const EXTENDED_INP = "HELLO BASE32 EXTENDED"; +const EXTENDED_OUT = "912KOJ2F41142KQ56CP20HAOAH2KSH258G======"; + +// All Bytes +const ALL_BYTES = [ + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f", + "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f", + "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f", + "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f", + "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f", + "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f", + "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f", + "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f", + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf", + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf", + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf", + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef", + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", +].join(""); + +const ALL_BYTES_EXTENDED_OUT = "000G40O40K30E209185GO38E1S8124GJ2GAHC5OO34D1M70T3OFI08924CI2A9H750KIKAPC5KN2UC1H68PJ8D9M6SS3IEHR7GUJSFQ085146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB9DLONAUBTG62OJ3CHIMCPR8D5L6MR3DDPNN0SBIEDQ7ATJNF1SNKURSFLV7V041GA1O91C6GU48J2KBHI6OT3SGI699754LIQBPH6CQJEE9R7KVK2GQ58T4KMJAFA59LALQPBDELUOB3CLJMIQRDDTON6TBNF5TNQVS1GE2OF2CBHM7P34SLIUCPN7CVK6HQB9T9LEMQVCDJMMRRJETTNV0S7HE7P75SRJUHQFATFMERRNFU3OV5SVKUNRFFU7PVBTVPVFUVS======"; +const ALL_BYTES_STANDARD_OUT = "AAAQEAYEAUDAOCAJBIFQYDIOB4IBCEQTCQKRMFYYDENBWHA5DYPSAIJCEMSCKJRHFAUSUKZMFUXC6MBRGIZTINJWG44DSOR3HQ6T4P2AIFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLJNVYXK6L5QGCYTDMRSWMZ3INFVGW3DNNZXXA4LSON2HK5TXPB4XU634PV7H7AEBQKBYJBMGQ6EITCULRSGY5D4QSGJJHFEVS2LZRGM2TOOJ3HU7UCQ2FI5EUWTKPKFJVKV2ZLNOV6YLDMVTWS23NN5YXG5LXPF5X274BQOCYPCMLRWHZDE4VS6MZXHM7UGR2LJ5JVOW27MNTWW33TO55X7A4HROHZHF43T6R2PK5PWO33XP6DY7F47U6X3PP6HZ7L57Z7P674======"; + +TestRegister.addTests([ + { + name: "To Base32 Standard: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "To Base32", + args: [ALPHABET_OPTIONS[0].value], + }, + ], + }, + { + name: "To Base32 Hex Extended: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "To Base32", + args: [ALPHABET_OPTIONS[1].value], + }, + ], + }, + { + name: "From Base32 Standard: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "From Base32", + args: [ALPHABET_OPTIONS[0].value, false], + }, + ], + }, + { + name: "From Base32 Hex Extended: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "From Base32", + args: [ALPHABET_OPTIONS[1].value, false], + }, + ], + }, + { + name: "To Base32 Standard: " + STANDARD_INP, + input: STANDARD_INP, + expectedOutput: STANDARD_OUT, + recipeConfig: [ + { + op: "To Base32", + args: [ALPHABET_OPTIONS[0].value], + }, + ], + }, + { + name: "To Base32 Hex Extended: " + EXTENDED_INP, + input: EXTENDED_INP, + expectedOutput: EXTENDED_OUT, + recipeConfig: [ + { + op: "To Base32", + args: [ALPHABET_OPTIONS[1].value], + }, + ], + }, + { + name: "From Base32 Standard: " + STANDARD_OUT, + input: STANDARD_OUT, + expectedOutput: STANDARD_INP, + recipeConfig: [ + { + op: "From Base32", + args: [ALPHABET_OPTIONS[0].value, false], + }, + ], + }, + { + name: "From Base32 Hex Extended: " + EXTENDED_OUT, + input: EXTENDED_OUT, + expectedOutput: EXTENDED_INP, + recipeConfig: [ + { + op: "From Base32", + args: [ALPHABET_OPTIONS[1].value, false], + }, + ], + }, + { + name: "To Base32 Hex Standard: All Bytes", + input: ALL_BYTES, + expectedOutput: ALL_BYTES_STANDARD_OUT, + recipeConfig: [ + { + op: "To Base32", + args: [ALPHABET_OPTIONS[0].value], + }, + ], + }, + { + name: "To Base32 Hex Extended: All Bytes", + input: ALL_BYTES, + expectedOutput: ALL_BYTES_EXTENDED_OUT, + recipeConfig: [ + { + op: "To Base32", + args: [ALPHABET_OPTIONS[1].value], + }, + ], + }, + { + name: "From Base32 Hex Standard: All Bytes", + input: ALL_BYTES_STANDARD_OUT, + expectedOutput: ALL_BYTES, + recipeConfig: [ + { + op: "From Base32", + args: [ALPHABET_OPTIONS[0].value, false], + }, + ], + }, + { + name: "From Base32 Hex Extended: All Bytes", + input: ALL_BYTES_EXTENDED_OUT, + expectedOutput: ALL_BYTES, + recipeConfig: [ + { + op: "From Base32", + args: [ALPHABET_OPTIONS[1].value, false], + }, + ], + }, +]); + From 7710c5ef49204c78fa0fe0d5acce35e15223768a Mon Sep 17 00:00:00 2001 From: ken Date: Fri, 28 Feb 2025 19:44:04 +0000 Subject: [PATCH 15/46] Add event listener for message events Add handler for message events --- src/web/Manager.mjs | 1 + src/web/waiters/InputWaiter.mjs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index 2e87210c..ae972a59 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -125,6 +125,7 @@ class Manager { window.addEventListener("focus", this.window.windowFocus.bind(this.window)); window.addEventListener("statechange", this.app.stateChange.bind(this.app)); window.addEventListener("popstate", this.app.popState.bind(this.app)); + window.addEventListener("message", this.input.handlePostMessage.bind(this.input)); // Controls document.getElementById("bake").addEventListener("click", this.controls.bakeClick.bind(this.controls)); diff --git a/src/web/waiters/InputWaiter.mjs b/src/web/waiters/InputWaiter.mjs index 3999fd2f..9e05932f 100644 --- a/src/web/waiters/InputWaiter.mjs +++ b/src/web/waiters/InputWaiter.mjs @@ -1654,6 +1654,22 @@ class InputWaiter { this.changeTab(inputNum, this.app.options.syncTabs); } + /** + * Handler for incoming postMessages + * If the events data has a `type` property set to `dataSubmit` + * the value property is set to the current input + * @param {event} e + * @param {object} e.data + * @param {string} e.data.type - the type of request, currently the only value is "dataSubmit" + * @param {string} e.data.value - the value of the message + */ + handlePostMessage(e) { + if ("data" in e && "type" in e.data && "value" in e.data) { + if (e.data.type === "dataSubmit") { + this.setInput(e.data.value); + } + } + } } export default InputWaiter; From 3b75e13287249a347d24fd821fe2dfb5a6b44aae Mon Sep 17 00:00:00 2001 From: PathToLife <12622625+PathToLife@users.noreply.github.com> Date: Mon, 10 Mar 2025 12:46:34 +1300 Subject: [PATCH 16/46] ci release error fix, apply detect chrome driver version npm install env var --- .github/workflows/releases.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index 586dba7b..8bfb8e2d 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -25,6 +25,7 @@ jobs: - name: Install run: | + export DETECT_CHROMEDRIVER_VERSION=true npm ci npm run setheapsize From b85036b78ffee2ab8cc036480d469156af1e998f Mon Sep 17 00:00:00 2001 From: PathToLife <12622625+PathToLife@users.noreply.github.com> Date: Mon, 10 Mar 2025 17:28:20 +1300 Subject: [PATCH 17/46] Dockerfile manual architecture selection for multiplatform build. Disable NPM Publish for testing --- .github/workflows/releases.yml | 23 +++++++++++++---------- Dockerfile | 11 ++++++++--- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index 8bfb8e2d..e6db697b 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -62,12 +62,22 @@ jobs: tags: ${{ steps.image-metadata.outputs.tags }} labels: ${{ steps.image-metadata.outputs.labels }} containerfiles: ./Dockerfile - platforms: linux/amd64,linux/arm64,linux/arm/v7 + archs: amd64,arm64v8,arm32v7 oci: true + # enable build layer caching between platforms + layers: true # Webpack seems to use a lot of open files, increase the max open file limit to accomodate. extra-args: | --ulimit nofile=10000 + - name: Publish to GHCR + uses: redhat-actions/push-to-registry@v2 + with: + image: ${{ steps.build-image.outputs.image }} + tags: ${{ steps.build-image.outputs.tags }} + registry: ${{ env.REGISTRY }} + username: ${{ env.REGISTRY_USER }} + password: ${{ env.REGISTRY_PASSWORD }} - name: Upload Release Assets id: upload-release-assets @@ -82,13 +92,6 @@ jobs: - name: Publish to NPM uses: JS-DevTools/npm-publish@v1 + if: false with: - token: ${{ secrets.NPM_TOKEN }} - - - name: Publish to GHCR - uses: redhat-actions/push-to-registry@v2 - with: - tags: ${{ steps.build-image.outputs.tags }} - registry: ${{ env.REGISTRY }} - username: ${{ env.REGISTRY_USER }} - password: ${{ env.REGISTRY_PASSWORD }} + token: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 09350891..05f85ceb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,19 +12,24 @@ COPY package.json . COPY package-lock.json . # Install dependencies -# --ignore-scripts do not run grunt postinstall script as it depends on files other than package.json +# --ignore-scripts prevents postinstall script (which runs grunt) as it depends on files other than package.json RUN npm ci --ignore-scripts -# Build the app +# Copy files needed for postinstall and build COPY . . # npm postinstall runs grunt, which depends on files other than package.json RUN npm run postinstall + +# Build the app RUN npm run build ######################################### # Package static build files into nginx # ######################################### -FROM nginx:stable-alpine AS cyberchef +# We are using Github Actions: redhat-actions/buildah-build@v2 which needs manual selection of arch in base image +# Remove TARGETARCH if docker buildx is supported in the CI release as --platform=$TARGETPLATFORM will be automatically set +ARG TARGETARCH +FROM ${TARGETARCH}/nginx:stable-alpine AS cyberchef COPY --from=builder /app/build/prod /usr/share/nginx/html/ From a42c7de112091475fab88ec8b64370ef9697c702 Mon Sep 17 00:00:00 2001 From: PathToLife <12622625+PathToLife@users.noreply.github.com> Date: Mon, 10 Mar 2025 18:00:49 +1300 Subject: [PATCH 18/46] test buildah platforms flag environment variables --- .github/workflows/releases.yml | 2 +- Dockerfile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index e6db697b..20968772 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -62,7 +62,7 @@ jobs: tags: ${{ steps.image-metadata.outputs.tags }} labels: ${{ steps.image-metadata.outputs.labels }} containerfiles: ./Dockerfile - archs: amd64,arm64v8,arm32v7 + platforms: linux/amd64,linux/arm64 oci: true # enable build layer caching between platforms layers: true diff --git a/Dockerfile b/Dockerfile index 05f85ceb..d63a8ca3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,6 +30,7 @@ RUN npm run build # We are using Github Actions: redhat-actions/buildah-build@v2 which needs manual selection of arch in base image # Remove TARGETARCH if docker buildx is supported in the CI release as --platform=$TARGETPLATFORM will be automatically set ARG TARGETARCH +ARG TARGETPLATFORM FROM ${TARGETARCH}/nginx:stable-alpine AS cyberchef COPY --from=builder /app/build/prod /usr/share/nginx/html/ From 23de98f892ae88776e9c7731d80cec232fd3e23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Vi=C3=A9?= Date: Wed, 26 Mar 2025 11:46:47 +0100 Subject: [PATCH 19/46] Add ECB/NoPadding and CBC/NoPadding Encryption --- src/core/operations/AESEncrypt.mjs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/core/operations/AESEncrypt.mjs b/src/core/operations/AESEncrypt.mjs index 7b52ff03..0c5b1689 100644 --- a/src/core/operations/AESEncrypt.mjs +++ b/src/core/operations/AESEncrypt.mjs @@ -66,6 +66,14 @@ class AESEncrypt extends Operation { { name: "ECB", off: [5] + }, + { + name: "CBC/NoPadding", + off: [5] + }, + { + name: "ECB/NoPadding", + off: [5] } ] }, @@ -98,7 +106,8 @@ class AESEncrypt extends Operation { run(input, args) { const key = Utils.convertToByteString(args[0].string, args[0].option), iv = Utils.convertToByteString(args[1].string, args[1].option), - mode = args[2], + mode = args[2].substring(0, 3), + noPadding = args[2].endsWith("NoPadding"), inputType = args[3], outputType = args[4], aad = Utils.convertToByteString(args[5].string, args[5].option); @@ -114,11 +123,20 @@ The following algorithms will be used based on the size of the key: input = Utils.convertToByteString(input, inputType); + // Handle NoPadding modes + if (noPadding && input.length % 16 !== 0) { + throw new OperationError("Input length must be a multiple of 16 bytes for NoPadding modes."); + } const cipher = forge.cipher.createCipher("AES-" + mode, key); cipher.start({ iv: iv, additionalData: mode === "GCM" ? aad : undefined }); + if (noPadding) { + cipher.mode.pad = function(output, options) { + return true; + } + } cipher.update(forge.util.createBuffer(input)); cipher.finish(); From e00a636fc0983d0a6497fc8e36c48b8063f1e829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Vi=C3=A9?= Date: Fri, 4 Apr 2025 18:40:27 +0200 Subject: [PATCH 20/46] fix semicolon --- src/core/operations/AESEncrypt.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/AESEncrypt.mjs b/src/core/operations/AESEncrypt.mjs index 0c5b1689..0bc85303 100644 --- a/src/core/operations/AESEncrypt.mjs +++ b/src/core/operations/AESEncrypt.mjs @@ -135,7 +135,7 @@ The following algorithms will be used based on the size of the key: if (noPadding) { cipher.mode.pad = function(output, options) { return true; - } + }; } cipher.update(forge.util.createBuffer(input)); cipher.finish(); From 857576dbe4e7e1536b594cc64331161618d99dfc Mon Sep 17 00:00:00 2001 From: heaprc <9023404+0xh3xa@users.noreply.github.com> Date: Sat, 5 Apr 2025 00:18:54 +0200 Subject: [PATCH 21/46] fix(RecipeWaiter): sanitize user input in addOperation to prevent XSS --- package-lock.json | 24 +++++++++++++++++------- package.json | 1 + src/web/waiters/RecipeWaiter.mjs | 5 ++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index e731efff..ef2da3f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "d3": "7.9.0", "d3-hexbin": "^0.2.2", "diff": "^5.2.0", + "dompurify": "^3.2.5", "es6-promisify": "^7.0.0", "escodegen": "^2.1.0", "esprima": "^4.0.1", @@ -50,7 +51,6 @@ "jimp": "^0.22.12", "jq-web": "^0.5.1", "jquery": "3.7.1", - "js-crc": "^0.2.0", "js-sha3": "^0.9.3", "jsesc": "^3.0.2", "json5": "^2.2.3", @@ -4365,6 +4365,13 @@ "@types/node": "*" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/ws": { "version": "8.5.13", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", @@ -8411,6 +8418,15 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz", + "integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", @@ -12303,12 +12319,6 @@ "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", "license": "MIT" }, - "node_modules/js-crc": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/js-crc/-/js-crc-0.2.0.tgz", - "integrity": "sha512-8DdCSAOACpF8WDAjyDFBC2rj8OS4HUP9mNZBDfl8jCiPCnJG+2bkuycalxwZh6heFy6PrMvoWTp47lp6gzT65A==", - "license": "MIT" - }, "node_modules/js-sha3": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.9.3.tgz", diff --git a/package.json b/package.json index 337e8679..b3492a8e 100644 --- a/package.json +++ b/package.json @@ -123,6 +123,7 @@ "d3": "7.9.0", "d3-hexbin": "^0.2.2", "diff": "^5.2.0", + "dompurify": "^3.2.5", "es6-promisify": "^7.0.0", "escodegen": "^2.1.0", "esprima": "^4.0.1", diff --git a/src/web/waiters/RecipeWaiter.mjs b/src/web/waiters/RecipeWaiter.mjs index 3f5aa302..35389184 100755 --- a/src/web/waiters/RecipeWaiter.mjs +++ b/src/web/waiters/RecipeWaiter.mjs @@ -8,6 +8,7 @@ import HTMLOperation from "../HTMLOperation.mjs"; import Sortable from "sortablejs"; import Utils from "../../core/Utils.mjs"; import {escapeControlChars} from "../utils/editorUtils.mjs"; +import DOMPurify from 'dompurify'; /** @@ -435,7 +436,9 @@ class RecipeWaiter { const item = document.createElement("li"); item.classList.add("operation"); - item.innerHTML = name; + const clean = DOMPurify.sanitize(name); + item.innerHTML = clean; + this.buildRecipeOperation(item); document.getElementById("rec-list").appendChild(item); From c83e1ac4fb7fe2d68bbf892c08c0790de8685d1f Mon Sep 17 00:00:00 2001 From: heaprc <9023404+0xh3xa@users.noreply.github.com> Date: Sat, 5 Apr 2025 00:42:37 +0200 Subject: [PATCH 22/46] Fix(RecipeWaiter): eslint format error --- src/web/waiters/RecipeWaiter.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/waiters/RecipeWaiter.mjs b/src/web/waiters/RecipeWaiter.mjs index 35389184..93ca1182 100755 --- a/src/web/waiters/RecipeWaiter.mjs +++ b/src/web/waiters/RecipeWaiter.mjs @@ -8,7 +8,7 @@ import HTMLOperation from "../HTMLOperation.mjs"; import Sortable from "sortablejs"; import Utils from "../../core/Utils.mjs"; import {escapeControlChars} from "../utils/editorUtils.mjs"; -import DOMPurify from 'dompurify'; +import DOMPurify from "dompurify"; /** From fa559fdbed6a45772f788d511207c365f9db3a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Vi=C3=A9?= Date: Sat, 5 Apr 2025 14:28:14 +0200 Subject: [PATCH 23/46] split edit --- src/core/operations/AESDecrypt.mjs | 2 +- src/core/operations/AESEncrypt.mjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/operations/AESDecrypt.mjs b/src/core/operations/AESDecrypt.mjs index e24a5119..5e6cec26 100644 --- a/src/core/operations/AESDecrypt.mjs +++ b/src/core/operations/AESDecrypt.mjs @@ -112,7 +112,7 @@ class AESDecrypt extends Operation { run(input, args) { const key = Utils.convertToByteString(args[0].string, args[0].option), iv = Utils.convertToByteString(args[1].string, args[1].option), - mode = args[2].substring(0, 3), + mode = args[2].split("/")[0], noPadding = args[2].endsWith("NoPadding"), inputType = args[3], outputType = args[4], diff --git a/src/core/operations/AESEncrypt.mjs b/src/core/operations/AESEncrypt.mjs index 0bc85303..84e1c540 100644 --- a/src/core/operations/AESEncrypt.mjs +++ b/src/core/operations/AESEncrypt.mjs @@ -106,7 +106,7 @@ class AESEncrypt extends Operation { run(input, args) { const key = Utils.convertToByteString(args[0].string, args[0].option), iv = Utils.convertToByteString(args[1].string, args[1].option), - mode = args[2].substring(0, 3), + mode = args[2].split("/")[0], noPadding = args[2].endsWith("NoPadding"), inputType = args[3], outputType = args[4], From b9ed1fde62fee1dd1e3d574966e8a1497a06823e Mon Sep 17 00:00:00 2001 From: Bart van Andel Date: Sat, 5 Apr 2025 23:01:52 +0200 Subject: [PATCH 24/46] fix: use defaultIndex instead of 0 in transformArgs --- src/node/api.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node/api.mjs b/src/node/api.mjs index 0c9dd8a7..88b3f834 100644 --- a/src/node/api.mjs +++ b/src/node/api.mjs @@ -74,11 +74,11 @@ function transformArgs(opArgsList, newArgs) { return opArgs.map((arg) => { if (arg.type === "option") { // pick default option if not already chosen - return typeof arg.value === "string" ? arg.value : arg.value[0]; + return typeof arg.value === "string" ? arg.value : arg.value[arg.defaultIndex ?? 0]; } if (arg.type === "editableOption") { - return typeof arg.value === "string" ? arg.value : arg.value[0].value; + return typeof arg.value === "string" ? arg.value : arg.value[arg.defaultIndex ?? 0].value; } if (arg.type === "toggleString") { From ad91099d5edde9f9e85bb98f22f708987f2f5faf Mon Sep 17 00:00:00 2001 From: Jeremy Giudicelli Date: Thu, 17 Apr 2025 11:37:35 +0200 Subject: [PATCH 25/46] feat : add Blake3 hashing import hash-wasm to hash inputs in utf-8 with optional key also in utf-8 and outputs in Hex --- package-lock.json | 6 ++++ package.json | 1 + src/core/operations/BLAKE3.mjs | 58 +++++++++++++++++++++++++++++++ tests/operations/tests/BLAKE3.mjs | 55 +++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+) create mode 100644 src/core/operations/BLAKE3.mjs create mode 100644 tests/operations/tests/BLAKE3.mjs diff --git a/package-lock.json b/package-lock.json index ef2da3f0..edc15036 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,7 @@ "file-saver": "^2.0.5", "flat": "^6.0.1", "geodesy": "1.1.3", + "hash-wasm": "^4.12.0", "highlight.js": "^11.9.0", "ieee754": "^1.2.1", "jimp": "^0.22.12", @@ -10940,6 +10941,11 @@ "node": ">= 0.10" } }, + "node_modules/hash-wasm": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.12.0.tgz", + "integrity": "sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ==" + }, "node_modules/hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", diff --git a/package.json b/package.json index b3492a8e..adfdcc86 100644 --- a/package.json +++ b/package.json @@ -132,6 +132,7 @@ "file-saver": "^2.0.5", "flat": "^6.0.1", "geodesy": "1.1.3", + "hash-wasm": "^4.12.0", "highlight.js": "^11.9.0", "ieee754": "^1.2.1", "jimp": "^0.22.12", diff --git a/src/core/operations/BLAKE3.mjs b/src/core/operations/BLAKE3.mjs new file mode 100644 index 00000000..20618acb --- /dev/null +++ b/src/core/operations/BLAKE3.mjs @@ -0,0 +1,58 @@ +/** + * @author xumptex [xumptex@outlook.fr] + * @copyright Crown Copyright 2025 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { blake3 } from 'hash-wasm'; +/** + * BLAKE3 operation + */ +class BLAKE3 extends Operation { + + /** + * BLAKE3 constructor + */ + constructor() { + super(); + + this.name = "BLAKE3"; + this.module = "Hashing"; + this.description = "Hashes the input using BLAKE3 (UTF-8 encoded), with an optional key (also UTF-8), and outputs the result in hexadecimal format."; + this.infoURL = "https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE3"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Size (bytes)", + "type": "number" + }, { + "name": "Key", + "type": "string", + "value": "" + }, + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const key = args[1]; + const size = args[0]; + // Check if the user want a key hash or not + if (key === "") { + return blake3(input, size*8); + }if (key.length !== 32) { + throw new OperationError("The key must be exactly 32 bytes long"); + } + return blake3(input, size*8, key); + } + +} + +export default BLAKE3; diff --git a/tests/operations/tests/BLAKE3.mjs b/tests/operations/tests/BLAKE3.mjs new file mode 100644 index 00000000..0537e467 --- /dev/null +++ b/tests/operations/tests/BLAKE3.mjs @@ -0,0 +1,55 @@ +/** + * BLAKE3 tests. + * @author xumptex [xumptex@outlook.fr] + * @copyright Crown Copyright 2025 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "BLAKE3: 8 - Hello world", + input: "Hello world", + expectedOutput: "e7e6fb7d2869d109", + recipeConfig: [ + { "op": "BLAKE3", + "args": [8, ""] } + ] + }, + { + name: "BLAKE3: 16 - Hello world 2", + input: "Hello world 2", + expectedOutput: "2a3df5fe5f0d3fcdd995fc203c7f7c52", + recipeConfig: [ + { "op": "BLAKE3", + "args": [16, ""] } + ] + }, + { + name: "BLAKE3: 32 - Hello world", + input: "Hello world", + expectedOutput: "e7e6fb7d2869d109b62cdb1227208d4016cdaa0af6603d95223c6a698137d945", + recipeConfig: [ + { "op": "BLAKE3", + "args": [32, ""] } + ] + }, + { + name: "BLAKE3: Key Test", + input: "Hello world", + expectedOutput: "59dd23ac9d025690", + recipeConfig: [ + { "op": "BLAKE3", + "args": [8, "ThiskeyisexactlythirtytwoBytesLo"] } + ] + }, + { + name: "BLAKE3: Key Test 2", + input: "Hello world", + expectedOutput: "c8302c9634c1da42", + recipeConfig: [ + { "op": "BLAKE3", + "args": [8, "ThiskeyisexactlythirtytwoByteslo"] } + ] + } +]); \ No newline at end of file From 26d8fa51b06915bb09ad5553b9d8fb5421a80431 Mon Sep 17 00:00:00 2001 From: Jeremy Giudicelli Date: Thu, 17 Apr 2025 13:27:45 +0200 Subject: [PATCH 26/46] fix : delete space and add new line --- src/core/operations/BLAKE3.mjs | 8 ++++---- tests/operations/tests/BLAKE3.mjs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/operations/BLAKE3.mjs b/src/core/operations/BLAKE3.mjs index 20618acb..0f686120 100644 --- a/src/core/operations/BLAKE3.mjs +++ b/src/core/operations/BLAKE3.mjs @@ -6,7 +6,7 @@ import Operation from "../Operation.mjs"; import OperationError from "../errors/OperationError.mjs"; -import { blake3 } from 'hash-wasm'; +import { blake3 } from "hash-wasm"; /** * BLAKE3 operation */ @@ -21,7 +21,7 @@ class BLAKE3 extends Operation { this.name = "BLAKE3"; this.module = "Hashing"; this.description = "Hashes the input using BLAKE3 (UTF-8 encoded), with an optional key (also UTF-8), and outputs the result in hexadecimal format."; - this.infoURL = "https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE3"; + this.infoURL = "https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE3"; this.inputType = "string"; this.outputType = "string"; this.args = [ @@ -32,7 +32,7 @@ class BLAKE3 extends Operation { "name": "Key", "type": "string", "value": "" - }, + } ]; } @@ -47,7 +47,7 @@ class BLAKE3 extends Operation { // Check if the user want a key hash or not if (key === "") { return blake3(input, size*8); - }if (key.length !== 32) { + } if (key.length !== 32) { throw new OperationError("The key must be exactly 32 bytes long"); } return blake3(input, size*8, key); diff --git a/tests/operations/tests/BLAKE3.mjs b/tests/operations/tests/BLAKE3.mjs index 0537e467..42cb4dc1 100644 --- a/tests/operations/tests/BLAKE3.mjs +++ b/tests/operations/tests/BLAKE3.mjs @@ -52,4 +52,4 @@ TestRegister.addTests([ "args": [8, "ThiskeyisexactlythirtytwoByteslo"] } ] } -]); \ No newline at end of file +]); From bd8906f46b36bd479e1222984c37a534324b2d9d Mon Sep 17 00:00:00 2001 From: Jeremy Giudicelli Date: Thu, 17 Apr 2025 13:40:01 +0200 Subject: [PATCH 27/46] fix : add Categories and index --- src/core/config/Categories.json | 1 + tests/operations/index.mjs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 71b311e6..19e84f76 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -422,6 +422,7 @@ "Snefru", "BLAKE2b", "BLAKE2s", + "BLAKE3", "GOST Hash", "Streebog", "SSDEEP", diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index bb7016bb..a4a27727 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -31,6 +31,7 @@ import "./tests/BCD.mjs"; import "./tests/BitwiseOp.mjs"; import "./tests/BLAKE2b.mjs"; import "./tests/BLAKE2s.mjs"; +import "./tests/BLAKE3.mjs"; import "./tests/Bombe.mjs"; import "./tests/BSON.mjs"; import "./tests/ByteRepr.mjs"; From 64355c9dde5bbb95aaf171f5d455c71e4310458e Mon Sep 17 00:00:00 2001 From: ericli-splunk <139386257+ericli-splunk@users.noreply.github.com> Date: Thu, 17 Apr 2025 15:46:37 -0700 Subject: [PATCH 28/46] Fix email regex --- src/core/operations/ExtractEmailAddresses.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/ExtractEmailAddresses.mjs b/src/core/operations/ExtractEmailAddresses.mjs index f50e1aaf..34b838ab 100644 --- a/src/core/operations/ExtractEmailAddresses.mjs +++ b/src/core/operations/ExtractEmailAddresses.mjs @@ -51,7 +51,7 @@ class ExtractEmailAddresses extends Operation { run(input, args) { const [displayTotal, sort, unique] = args, // email regex from: https://www.regextester.com/98066 - regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}\])/ig; + regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\])/ig; const results = search( input, From a2665ba21380bf1a3e33b5b57e4c7fbc383ff734 Mon Sep 17 00:00:00 2001 From: r4mos Date: Fri, 25 Apr 2025 13:56:10 +0200 Subject: [PATCH 29/46] Add to ECDSA Verify the message format --- src/core/operations/ECDSAVerify.mjs | 11 ++- tests/operations/tests/ECDSA.mjs | 105 +++++++++++++++++++++++----- 2 files changed, 97 insertions(+), 19 deletions(-) diff --git a/src/core/operations/ECDSAVerify.mjs b/src/core/operations/ECDSAVerify.mjs index 7e46e867..1f8a53ea 100644 --- a/src/core/operations/ECDSAVerify.mjs +++ b/src/core/operations/ECDSAVerify.mjs @@ -9,6 +9,7 @@ import OperationError from "../errors/OperationError.mjs"; import { fromBase64 } from "../lib/Base64.mjs"; import { toHexFast } from "../lib/Hex.mjs"; import r from "jsrsasign"; +import Utils from "../Utils.mjs"; /** * ECDSA Verify operation @@ -59,6 +60,11 @@ class ECDSAVerify extends Operation { name: "Message", type: "text", value: "" + }, + { + name: "Message format", + type: "option", + value: ["Raw", "Hex", "Base64"] } ]; } @@ -70,7 +76,7 @@ class ECDSAVerify extends Operation { */ run(input, args) { let inputFormat = args[0]; - const [, mdAlgo, keyPem, msg] = args; + const [, mdAlgo, keyPem, msg, msgFormat] = args; if (keyPem.replace("-----BEGIN PUBLIC KEY-----", "").length === 0) { throw new OperationError("Please enter a public key."); @@ -145,7 +151,8 @@ class ECDSAVerify extends Operation { throw new OperationError("Provided key is not a public key."); } sig.init(key); - sig.updateString(msg); + const messageStr = Utils.convertToByteString(msg, msgFormat); + sig.updateString(messageStr); const result = sig.verify(signatureASN1Hex); return result ? "Verified OK" : "Verification Failure"; } diff --git a/tests/operations/tests/ECDSA.mjs b/tests/operations/tests/ECDSA.mjs index 560afc5c..ffffbb10 100644 --- a/tests/operations/tests/ECDSA.mjs +++ b/tests/operations/tests/ECDSA.mjs @@ -6,7 +6,10 @@ * @license Apache-2.0 */ import TestRegister from "../../lib/TestRegister.mjs"; -import { ASCII_TEXT } from "../../samples/Ciphers.mjs"; +import {ALL_BYTES, ASCII_TEXT, UTF8_TEXT} from "../../samples/Ciphers.mjs"; + +const SOME_HEX_BYTES = "cdb23f958e018418621d9e489b7bba0f0c481f604eba2eb1ea35e38f99490cc0"; +const SOME_BASE64_BYTES = "zbI/lY4BhBhiHZ5Im3u6DwxIH2BOui6x6jXjj5lJDMA="; const P256 = { // openssl ecparam -name prime256v1 -genkey -noout -out p256.priv.key @@ -104,7 +107,7 @@ TestRegister.addTests([ }, { "op": "ECDSA Verify", - "args": ["ASN.1 HEX", "MD5", P256.publicKey, ASCII_TEXT] + "args": ["ASN.1 HEX", "MD5", P256.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -119,7 +122,7 @@ TestRegister.addTests([ }, { "op": "ECDSA Verify", - "args": ["ASN.1 HEX", "SHA-1", P256.publicKey, ASCII_TEXT] + "args": ["ASN.1 HEX", "SHA-1", P256.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -134,7 +137,7 @@ TestRegister.addTests([ }, { "op": "ECDSA Verify", - "args": ["ASN.1 HEX", "SHA-256", P256.publicKey, ASCII_TEXT] + "args": ["ASN.1 HEX", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -149,7 +152,7 @@ TestRegister.addTests([ }, { "op": "ECDSA Verify", - "args": ["ASN.1 HEX", "SHA-384", P256.publicKey, ASCII_TEXT] + "args": ["ASN.1 HEX", "SHA-384", P256.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -164,7 +167,7 @@ TestRegister.addTests([ }, { "op": "ECDSA Verify", - "args": ["ASN.1 HEX", "SHA-512", P256.publicKey, ASCII_TEXT] + "args": ["ASN.1 HEX", "SHA-512", P256.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -179,7 +182,7 @@ TestRegister.addTests([ }, { "op": "ECDSA Verify", - "args": ["ASN.1 HEX", "SHA-256", P256.publicKey, ASCII_TEXT] + "args": ["ASN.1 HEX", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -194,7 +197,7 @@ TestRegister.addTests([ }, { "op": "ECDSA Verify", - "args": ["ASN.1 HEX", "SHA-384", P384.publicKey, ASCII_TEXT] + "args": ["ASN.1 HEX", "SHA-384", P384.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -209,7 +212,7 @@ TestRegister.addTests([ }, { "op": "ECDSA Verify", - "args": ["ASN.1 HEX", "SHA-512", P521.publicKey, ASCII_TEXT] + "args": ["ASN.1 HEX", "SHA-512", P521.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -246,7 +249,7 @@ TestRegister.addTests([ recipeConfig: [ { "op": "ECDSA Verify", - "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT] + "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -257,7 +260,7 @@ TestRegister.addTests([ recipeConfig: [ { "op": "ECDSA Verify", - "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT] + "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -268,7 +271,7 @@ TestRegister.addTests([ recipeConfig: [ { "op": "ECDSA Verify", - "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT] + "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -279,7 +282,7 @@ TestRegister.addTests([ recipeConfig: [ { "op": "ECDSA Verify", - "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT] + "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -290,7 +293,7 @@ TestRegister.addTests([ recipeConfig: [ { "op": "ECDSA Verify", - "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT] + "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -301,7 +304,7 @@ TestRegister.addTests([ recipeConfig: [ { "op": "ECDSA Verify", - "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT] + "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"] } ] }, @@ -312,7 +315,7 @@ TestRegister.addTests([ recipeConfig: [ { "op": "ECDSA Verify", - "args": ["ASN.1 HEX", "SHA-256", P256.privateKeyPkcs1, ASCII_TEXT] + "args": ["ASN.1 HEX", "SHA-256", P256.privateKeyPkcs1, ASCII_TEXT, "Raw"] } ] }, @@ -323,7 +326,7 @@ TestRegister.addTests([ recipeConfig: [ { "op": "ECDSA Verify", - "args": ["ASN.1 HEX", "SHA-256", PEM_PUB_RSA512, ASCII_TEXT] + "args": ["ASN.1 HEX", "SHA-256", PEM_PUB_RSA512, ASCII_TEXT, "Raw"] } ] }, @@ -460,5 +463,73 @@ TestRegister.addTests([ "args": ["Auto", "Raw JSON"] } ] + }, + { + name: "ECDSA Sign/Verify: P-256 with SHA256 UTF8", + input: UTF8_TEXT, + expectedOutput: "Verified OK", + recipeConfig: [ + { + "op": "ECDSA Sign", + "args": [P256.privateKeyPkcs1, "SHA-256", "ASN.1 HEX"] + }, + { + "op": "ECDSA Verify", + "args": ["ASN.1 HEX", "SHA-256", P256.publicKey, UTF8_TEXT, "Raw"] + } + ] + }, + { + name: "ECDSA Sign/Verify: P-256 with SHA256 bytes raw", + input: ALL_BYTES, + expectedOutput: "Verified OK", + recipeConfig: [ + { + "op": "ECDSA Sign", + "args": [P256.privateKeyPkcs1, "SHA-256", "ASN.1 HEX"] + }, + { + "op": "ECDSA Verify", + "args": ["ASN.1 HEX", "SHA-256", P256.publicKey, ALL_BYTES, "Raw"] + } + ] + }, + { + name: "ECDSA Sign/Verify: P-256 with SHA256 bytes hex", + input: SOME_HEX_BYTES, + expectedOutput: "Verified OK", + recipeConfig: [ + { + "op": "From Hex", + "args": ["Auto"] + }, + { + "op": "ECDSA Sign", + "args": [P256.privateKeyPkcs1, "SHA-256", "ASN.1 HEX"] + }, + { + "op": "ECDSA Verify", + "args": ["ASN.1 HEX", "SHA-256", P256.publicKey, SOME_HEX_BYTES, "Hex"] + } + ] + }, + { + name: "ECDSA Sign/Verify: P-256 with SHA256 bytes Base64", + input: SOME_BASE64_BYTES, + expectedOutput: "Verified OK", + recipeConfig: [ + { + "op": "From Base64", + "args": ["A-Za-z0-9+/=", true] + }, + { + "op": "ECDSA Sign", + "args": [P256.privateKeyPkcs1, "SHA-256", "ASN.1 HEX"] + }, + { + "op": "ECDSA Verify", + "args": ["ASN.1 HEX", "SHA-256", P256.publicKey, SOME_BASE64_BYTES, "Base64"] + } + ] } ]); From 4085a4c21970d26e7cf6607c7a759793b3f43d87 Mon Sep 17 00:00:00 2001 From: r4mos Date: Mon, 28 Apr 2025 16:43:22 +0200 Subject: [PATCH 30/46] Remove Checksums from All Hashes --- src/core/operations/GenerateAllHashes.mjs | 24 -------------------- tests/operations/tests/GenerateAllHashes.mjs | 10 -------- 2 files changed, 34 deletions(-) diff --git a/src/core/operations/GenerateAllHashes.mjs b/src/core/operations/GenerateAllHashes.mjs index 06b5f7d9..df09aa85 100644 --- a/src/core/operations/GenerateAllHashes.mjs +++ b/src/core/operations/GenerateAllHashes.mjs @@ -22,12 +22,6 @@ import HAS160 from "./HAS160.mjs"; import Whirlpool from "./Whirlpool.mjs"; import SSDEEP from "./SSDEEP.mjs"; import CTPH from "./CTPH.mjs"; -import Fletcher8Checksum from "./Fletcher8Checksum.mjs"; -import Fletcher16Checksum from "./Fletcher16Checksum.mjs"; -import Fletcher32Checksum from "./Fletcher32Checksum.mjs"; -import Fletcher64Checksum from "./Fletcher64Checksum.mjs"; -import Adler32Checksum from "./Adler32Checksum.mjs"; -import CRCChecksum from "./CRCChecksum.mjs"; import BLAKE2b from "./BLAKE2b.mjs"; import BLAKE2s from "./BLAKE2s.mjs"; import Streebog from "./Streebog.mjs"; @@ -112,16 +106,6 @@ class GenerateAllHashes extends Operation { {name: "SSDEEP", algo: (new SSDEEP()), inputType: "str"}, {name: "CTPH", algo: (new CTPH()), inputType: "str"} ]; - this.checksums = [ - {name: "Fletcher-8", algo: (new Fletcher8Checksum), inputType: "byteArray", params: []}, - {name: "Fletcher-16", algo: (new Fletcher16Checksum), inputType: "byteArray", params: []}, - {name: "Fletcher-32", algo: (new Fletcher32Checksum), inputType: "byteArray", params: []}, - {name: "Fletcher-64", algo: (new Fletcher64Checksum), inputType: "byteArray", params: []}, - {name: "Adler-32", algo: (new Adler32Checksum), inputType: "byteArray", params: []}, - {name: "CRC-8", algo: (new CRCChecksum), inputType: "arrayBuffer", params: ["CRC-8"]}, - {name: "CRC-16", algo: (new CRCChecksum), inputType: "arrayBuffer", params: ["CRC-16"]}, - {name: "CRC-32", algo: (new CRCChecksum), inputType: "arrayBuffer", params: ["CRC-32"]} - ]; } /** @@ -142,14 +126,6 @@ class GenerateAllHashes extends Operation { output += this.formatDigest(digest, length, includeNames, hash.name); }); - if (length === "All") { - output += "\nChecksums:\n"; - this.checksums.forEach(checksum => { - digest = this.executeAlgo(checksum.algo, checksum.inputType, checksum.params || []); - output += this.formatDigest(digest, length, includeNames, checksum.name); - }); - } - return output; } diff --git a/tests/operations/tests/GenerateAllHashes.mjs b/tests/operations/tests/GenerateAllHashes.mjs index 8e4a849a..ab4f1b13 100644 --- a/tests/operations/tests/GenerateAllHashes.mjs +++ b/tests/operations/tests/GenerateAllHashes.mjs @@ -54,16 +54,6 @@ LM Hash: 01FC5A6BE7BC6929AAD3B435B51404EE NT Hash: 0CB6948805F797BF2A82807973B89537 SSDEEP: 3:Hn:Hn CTPH: A:E:E - -Checksums: -Fletcher-8: 3d -Fletcher-16: 5dc1 -Fletcher-32: 3f5cd9e7 -Fletcher-64: 7473657474736574 -Adler-32: 045d01c1 -CRC-8: b9 -CRC-16: f82e -CRC-32: d87f7e0c `, recipeConfig: [ { From 1089c6bfb82d73b3884b6b4960069d133248e4d6 Mon Sep 17 00:00:00 2001 From: r4mos Date: Tue, 29 Apr 2025 17:25:29 +0200 Subject: [PATCH 31/46] Add All Checksums Operation --- src/core/config/Categories.json | 1 + src/core/operations/GenerateAllChecksums.mjs | 254 ++++++ tests/operations/index.mjs | 1 + .../operations/tests/GenerateAllChecksums.mjs | 805 ++++++++++++++++++ 4 files changed, 1061 insertions(+) create mode 100644 src/core/operations/GenerateAllChecksums.mjs create mode 100644 tests/operations/tests/GenerateAllChecksums.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 71b311e6..9bd16516 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -404,6 +404,7 @@ "name": "Hashing", "ops": [ "Analyse hash", + "Generate all checksums", "Generate all hashes", "MD2", "MD4", diff --git a/src/core/operations/GenerateAllChecksums.mjs b/src/core/operations/GenerateAllChecksums.mjs new file mode 100644 index 00000000..0c6de777 --- /dev/null +++ b/src/core/operations/GenerateAllChecksums.mjs @@ -0,0 +1,254 @@ +/** + * @author r4mos [2k95ljkhg@mozmail.com] + * @copyright Crown Copyright 2025 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import Adler32Checksum from "./Adler32Checksum.mjs"; +import CRCChecksum from "./CRCChecksum.mjs"; +import Fletcher8Checksum from "./Fletcher8Checksum.mjs"; +import Fletcher16Checksum from "./Fletcher16Checksum.mjs"; +import Fletcher32Checksum from "./Fletcher32Checksum.mjs"; +import Fletcher64Checksum from "./Fletcher64Checksum.mjs"; + +/** + * Generate all checksums operation + */ +class GenerateAllChecksums extends Operation { + + /** + * GenerateAllChecksums constructor + */ + constructor() { + super(); + + this.name = "Generate all checksums"; + this.module = "Crypto"; + this.description = "Generates all available checksums for the input."; + this.infoURL = "https://wikipedia.org/wiki/Cyclic_redundancy_check"; + this.inputType = "ArrayBuffer"; + this.outputType = "string"; + this.args = [ + { + name: "Length (bits)", + type: "option", + value: [ + "All", "3", "4", "5", "6", "7", "8", "10", "11", "12", "13", "14", "15", "16", "17", "21", "24", "30", "31", "32", "40", "64", "82" + ] + }, + { + name: "Include names", + type: "boolean", + value: true + }, + ]; + + const adler32 = new Adler32Checksum; + const crc = new CRCChecksum; + const fletcher8 = new Fletcher8Checksum; + const fletcher16 = new Fletcher16Checksum; + const fletcher32 = new Fletcher32Checksum; + const fletcher64 = new Fletcher64Checksum; + this.checksums = [ + {name: "CRC-3/GSM", algo: crc, params: ["CRC-3/GSM"]}, + {name: "CRC-3/ROHC", algo: crc, params: ["CRC-3/ROHC"]}, + {name: "CRC-4/G-704", algo: crc, params: ["CRC-4/G-704"]}, + {name: "CRC-4/INTERLAKEN", algo: crc, params: ["CRC-4/INTERLAKEN"]}, + {name: "CRC-4/ITU", algo: crc, params: ["CRC-4/ITU"]}, + {name: "CRC-5/EPC", algo: crc, params: ["CRC-5/EPC"]}, + {name: "CRC-5/EPC-C1G2", algo: crc, params: ["CRC-5/EPC-C1G2"]}, + {name: "CRC-5/G-704", algo: crc, params: ["CRC-5/G-704"]}, + {name: "CRC-5/ITU", algo: crc, params: ["CRC-5/ITU"]}, + {name: "CRC-5/USB", algo: crc, params: ["CRC-5/USB"]}, + {name: "CRC-6/CDMA2000-A", algo: crc, params: ["CRC-6/CDMA2000-A"]}, + {name: "CRC-6/CDMA2000-B", algo: crc, params: ["CRC-6/CDMA2000-B"]}, + {name: "CRC-6/DARC", algo: crc, params: ["CRC-6/DARC"]}, + {name: "CRC-6/G-704", algo: crc, params: ["CRC-6/G-704"]}, + {name: "CRC-6/GSM", algo: crc, params: ["CRC-6/GSM"]}, + {name: "CRC-6/ITU", algo: crc, params: ["CRC-6/ITU"]}, + {name: "CRC-7/MMC", algo: crc, params: ["CRC-7/MMC"]}, + {name: "CRC-7/ROHC", algo: crc, params: ["CRC-7/ROHC"]}, + {name: "CRC-7/UMTS", algo: crc, params: ["CRC-7/UMTS"]}, + {name: "CRC-8", algo: crc, params: ["CRC-8"]}, + {name: "CRC-8/8H2F", algo: crc, params: ["CRC-8/8H2F"]}, + {name: "CRC-8/AES", algo: crc, params: ["CRC-8/AES"]}, + {name: "CRC-8/AUTOSAR", algo: crc, params: ["CRC-8/AUTOSAR"]}, + {name: "CRC-8/BLUETOOTH", algo: crc, params: ["CRC-8/BLUETOOTH"]}, + {name: "CRC-8/CDMA2000", algo: crc, params: ["CRC-8/CDMA2000"]}, + {name: "CRC-8/DARC", algo: crc, params: ["CRC-8/DARC"]}, + {name: "CRC-8/DVB-S2", algo: crc, params: ["CRC-8/DVB-S2"]}, + {name: "CRC-8/EBU", algo: crc, params: ["CRC-8/EBU"]}, + {name: "CRC-8/GSM-A", algo: crc, params: ["CRC-8/GSM-A"]}, + {name: "CRC-8/GSM-B", algo: crc, params: ["CRC-8/GSM-B"]}, + {name: "CRC-8/HITAG", algo: crc, params: ["CRC-8/HITAG"]}, + {name: "CRC-8/I-432-1", algo: crc, params: ["CRC-8/I-432-1"]}, + {name: "CRC-8/I-CODE", algo: crc, params: ["CRC-8/I-CODE"]}, + {name: "CRC-8/ITU", algo: crc, params: ["CRC-8/ITU"]}, + {name: "CRC-8/LTE", algo: crc, params: ["CRC-8/LTE"]}, + {name: "CRC-8/MAXIM", algo: crc, params: ["CRC-8/MAXIM"]}, + {name: "CRC-8/MAXIM-DOW", algo: crc, params: ["CRC-8/MAXIM-DOW"]}, + {name: "CRC-8/MIFARE-MAD", algo: crc, params: ["CRC-8/MIFARE-MAD"]}, + {name: "CRC-8/NRSC-5", algo: crc, params: ["CRC-8/NRSC-5"]}, + {name: "CRC-8/OPENSAFETY", algo: crc, params: ["CRC-8/OPENSAFETY"]}, + {name: "CRC-8/ROHC", algo: crc, params: ["CRC-8/ROHC"]}, + {name: "CRC-8/SAE-J1850", algo: crc, params: ["CRC-8/SAE-J1850"]}, + {name: "CRC-8/SAE-J1850-ZERO", algo: crc, params: ["CRC-8/SAE-J1850-ZERO"]}, + {name: "CRC-8/SMBUS", algo: crc, params: ["CRC-8/SMBUS"]}, + {name: "CRC-8/TECH-3250", algo: crc, params: ["CRC-8/TECH-3250"]}, + {name: "CRC-8/WCDMA", algo: crc, params: ["CRC-8/WCDMA"]}, + {name: "Fletcher-8", algo: fletcher8, params: []}, + {name: "CRC-10/ATM", algo: crc, params: ["CRC-10/ATM"]}, + {name: "CRC-10/CDMA2000", algo: crc, params: ["CRC-10/CDMA2000"]}, + {name: "CRC-10/GSM", algo: crc, params: ["CRC-10/GSM"]}, + {name: "CRC-10/I-610", algo: crc, params: ["CRC-10/I-610"]}, + {name: "CRC-11/FLEXRAY", algo: crc, params: ["CRC-11/FLEXRAY"]}, + {name: "CRC-11/UMTS", algo: crc, params: ["CRC-11/UMTS"]}, + {name: "CRC-12/3GPP", algo: crc, params: ["CRC-12/3GPP"]}, + {name: "CRC-12/CDMA2000", algo: crc, params: ["CRC-12/CDMA2000"]}, + {name: "CRC-12/DECT", algo: crc, params: ["CRC-12/DECT"]}, + {name: "CRC-12/GSM", algo: crc, params: ["CRC-12/GSM"]}, + {name: "CRC-12/UMTS", algo: crc, params: ["CRC-12/UMTS"]}, + {name: "CRC-13/BBC", algo: crc, params: ["CRC-13/BBC"]}, + {name: "CRC-14/DARC", algo: crc, params: ["CRC-14/DARC"]}, + {name: "CRC-14/GSM", algo: crc, params: ["CRC-14/GSM"]}, + {name: "CRC-15/CAN", algo: crc, params: ["CRC-15/CAN"]}, + {name: "CRC-15/MPT1327", algo: crc, params: ["CRC-15/MPT1327"]}, + {name: "CRC-16", algo: crc, params: ["CRC-16"]}, + {name: "CRC-16/A", algo: crc, params: ["CRC-16/A"]}, + {name: "CRC-16/ACORN", algo: crc, params: ["CRC-16/ACORN"]}, + {name: "CRC-16/ARC", algo: crc, params: ["CRC-16/ARC"]}, + {name: "CRC-16/AUG-CCITT", algo: crc, params: ["CRC-16/AUG-CCITT"]}, + {name: "CRC-16/AUTOSAR", algo: crc, params: ["CRC-16/AUTOSAR"]}, + {name: "CRC-16/B", algo: crc, params: ["CRC-16/B"]}, + {name: "CRC-16/BLUETOOTH", algo: crc, params: ["CRC-16/BLUETOOTH"]}, + {name: "CRC-16/BUYPASS", algo: crc, params: ["CRC-16/BUYPASS"]}, + {name: "CRC-16/CCITT", algo: crc, params: ["CRC-16/CCITT"]}, + {name: "CRC-16/CCITT-FALSE", algo: crc, params: ["CRC-16/CCITT-FALSE"]}, + {name: "CRC-16/CCITT-TRUE", algo: crc, params: ["CRC-16/CCITT-TRUE"]}, + {name: "CRC-16/CCITT-ZERO", algo: crc, params: ["CRC-16/CCITT-ZERO"]}, + {name: "CRC-16/CDMA2000", algo: crc, params: ["CRC-16/CDMA2000"]}, + {name: "CRC-16/CMS", algo: crc, params: ["CRC-16/CMS"]}, + {name: "CRC-16/DARC", algo: crc, params: ["CRC-16/DARC"]}, + {name: "CRC-16/DDS-110", algo: crc, params: ["CRC-16/DDS-110"]}, + {name: "CRC-16/DECT-R", algo: crc, params: ["CRC-16/DECT-R"]}, + {name: "CRC-16/DECT-X", algo: crc, params: ["CRC-16/DECT-X"]}, + {name: "CRC-16/DNP", algo: crc, params: ["CRC-16/DNP"]}, + {name: "CRC-16/EN-13757", algo: crc, params: ["CRC-16/EN-13757"]}, + {name: "CRC-16/EPC", algo: crc, params: ["CRC-16/EPC"]}, + {name: "CRC-16/EPC-C1G2", algo: crc, params: ["CRC-16/EPC-C1G2"]}, + {name: "CRC-16/GENIBUS", algo: crc, params: ["CRC-16/GENIBUS"]}, + {name: "CRC-16/GSM", algo: crc, params: ["CRC-16/GSM"]}, + {name: "CRC-16/I-CODE", algo: crc, params: ["CRC-16/I-CODE"]}, + {name: "CRC-16/IBM", algo: crc, params: ["CRC-16/IBM"]}, + {name: "CRC-16/IBM-3740", algo: crc, params: ["CRC-16/IBM-3740"]}, + {name: "CRC-16/IBM-SDLC", algo: crc, params: ["CRC-16/IBM-SDLC"]}, + {name: "CRC-16/IEC-61158-2", algo: crc, params: ["CRC-16/IEC-61158-2"]}, + {name: "CRC-16/ISO-HDLC", algo: crc, params: ["CRC-16/ISO-HDLC"]}, + {name: "CRC-16/ISO-IEC-14443-3-A", algo: crc, params: ["CRC-16/ISO-IEC-14443-3-A"]}, + {name: "CRC-16/ISO-IEC-14443-3-B", algo: crc, params: ["CRC-16/ISO-IEC-14443-3-B"]}, + {name: "CRC-16/KERMIT", algo: crc, params: ["CRC-16/KERMIT"]}, + {name: "CRC-16/LHA", algo: crc, params: ["CRC-16/LHA"]}, + {name: "CRC-16/LJ1200", algo: crc, params: ["CRC-16/LJ1200"]}, + {name: "CRC-16/LTE", algo: crc, params: ["CRC-16/LTE"]}, + {name: "CRC-16/M17", algo: crc, params: ["CRC-16/M17"]}, + {name: "CRC-16/MAXIM", algo: crc, params: ["CRC-16/MAXIM"]}, + {name: "CRC-16/MAXIM-DOW", algo: crc, params: ["CRC-16/MAXIM-DOW"]}, + {name: "CRC-16/MCRF4XX", algo: crc, params: ["CRC-16/MCRF4XX"]}, + {name: "CRC-16/MODBUS", algo: crc, params: ["CRC-16/MODBUS"]}, + {name: "CRC-16/NRSC-5", algo: crc, params: ["CRC-16/NRSC-5"]}, + {name: "CRC-16/OPENSAFETY-A", algo: crc, params: ["CRC-16/OPENSAFETY-A"]}, + {name: "CRC-16/OPENSAFETY-B", algo: crc, params: ["CRC-16/OPENSAFETY-B"]}, + {name: "CRC-16/PROFIBUS", algo: crc, params: ["CRC-16/PROFIBUS"]}, + {name: "CRC-16/RIELLO", algo: crc, params: ["CRC-16/RIELLO"]}, + {name: "CRC-16/SPI-FUJITSU", algo: crc, params: ["CRC-16/SPI-FUJITSU"]}, + {name: "CRC-16/T10-DIF", algo: crc, params: ["CRC-16/T10-DIF"]}, + {name: "CRC-16/TELEDISK", algo: crc, params: ["CRC-16/TELEDISK"]}, + {name: "CRC-16/TMS37157", algo: crc, params: ["CRC-16/TMS37157"]}, + {name: "CRC-16/UMTS", algo: crc, params: ["CRC-16/UMTS"]}, + {name: "CRC-16/USB", algo: crc, params: ["CRC-16/USB"]}, + {name: "CRC-16/V-41-LSB", algo: crc, params: ["CRC-16/V-41-LSB"]}, + {name: "CRC-16/V-41-MSB", algo: crc, params: ["CRC-16/V-41-MSB"]}, + {name: "CRC-16/VERIFONE", algo: crc, params: ["CRC-16/VERIFONE"]}, + {name: "CRC-16/X-25", algo: crc, params: ["CRC-16/X-25"]}, + {name: "CRC-16/XMODEM", algo: crc, params: ["CRC-16/XMODEM"]}, + {name: "CRC-16/ZMODEM", algo: crc, params: ["CRC-16/ZMODEM"]}, + {name: "Fletcher-16", algo: fletcher16, params: []}, + {name: "CRC-17/CAN-FD", algo: crc, params: ["CRC-17/CAN-FD"]}, + {name: "CRC-21/CAN-FD", algo: crc, params: ["CRC-21/CAN-FD"]}, + {name: "CRC-24/BLE", algo: crc, params: ["CRC-24/BLE"]}, + {name: "CRC-24/FLEXRAY-A", algo: crc, params: ["CRC-24/FLEXRAY-A"]}, + {name: "CRC-24/FLEXRAY-B", algo: crc, params: ["CRC-24/FLEXRAY-B"]}, + {name: "CRC-24/INTERLAKEN", algo: crc, params: ["CRC-24/INTERLAKEN"]}, + {name: "CRC-24/LTE-A", algo: crc, params: ["CRC-24/LTE-A"]}, + {name: "CRC-24/LTE-B", algo: crc, params: ["CRC-24/LTE-B"]}, + {name: "CRC-24/OPENPGP", algo: crc, params: ["CRC-24/OPENPGP"]}, + {name: "CRC-24/OS-9", algo: crc, params: ["CRC-24/OS-9"]}, + {name: "CRC-30/CDMA", algo: crc, params: ["CRC-30/CDMA"]}, + {name: "CRC-31/PHILIPS", algo: crc, params: ["CRC-31/PHILIPS"]}, + {name: "Adler-32", algo: adler32, params: []}, + {name: "CRC-32", algo: crc, params: ["CRC-32"]}, + {name: "CRC-32/AAL5", algo: crc, params: ["CRC-32/AAL5"]}, + {name: "CRC-32/ADCCP", algo: crc, params: ["CRC-32/ADCCP"]}, + {name: "CRC-32/AIXM", algo: crc, params: ["CRC-32/AIXM"]}, + {name: "CRC-32/AUTOSAR", algo: crc, params: ["CRC-32/AUTOSAR"]}, + {name: "CRC-32/BASE91-C", algo: crc, params: ["CRC-32/BASE91-C"]}, + {name: "CRC-32/BASE91-D", algo: crc, params: ["CRC-32/BASE91-D"]}, + {name: "CRC-32/BZIP2", algo: crc, params: ["CRC-32/BZIP2"]}, + {name: "CRC-32/C", algo: crc, params: ["CRC-32/C"]}, + {name: "CRC-32/CASTAGNOLI", algo: crc, params: ["CRC-32/CASTAGNOLI"]}, + {name: "CRC-32/CD-ROM-EDC", algo: crc, params: ["CRC-32/CD-ROM-EDC"]}, + {name: "CRC-32/CKSUM", algo: crc, params: ["CRC-32/CKSUM"]}, + {name: "CRC-32/D", algo: crc, params: ["CRC-32/D"]}, + {name: "CRC-32/DECT-B", algo: crc, params: ["CRC-32/DECT-B"]}, + {name: "CRC-32/INTERLAKEN", algo: crc, params: ["CRC-32/INTERLAKEN"]}, + {name: "CRC-32/ISCSI", algo: crc, params: ["CRC-32/ISCSI"]}, + {name: "CRC-32/ISO-HDLC", algo: crc, params: ["CRC-32/ISO-HDLC"]}, + {name: "CRC-32/JAMCRC", algo: crc, params: ["CRC-32/JAMCRC"]}, + {name: "CRC-32/MEF", algo: crc, params: ["CRC-32/MEF"]}, + {name: "CRC-32/MPEG-2", algo: crc, params: ["CRC-32/MPEG-2"]}, + {name: "CRC-32/NVME", algo: crc, params: ["CRC-32/NVME"]}, + {name: "CRC-32/PKZIP", algo: crc, params: ["CRC-32/PKZIP"]}, + {name: "CRC-32/POSIX", algo: crc, params: ["CRC-32/POSIX"]}, + {name: "CRC-32/Q", algo: crc, params: ["CRC-32/Q"]}, + {name: "CRC-32/SATA", algo: crc, params: ["CRC-32/SATA"]}, + {name: "CRC-32/V-42", algo: crc, params: ["CRC-32/V-42"]}, + {name: "CRC-32/XFER", algo: crc, params: ["CRC-32/XFER"]}, + {name: "CRC-32/XZ", algo: crc, params: ["CRC-32/XZ"]}, + {name: "Fletcher-32", algo: fletcher32, params: []}, + {name: "CRC-40/GSM", algo: crc, params: ["CRC-40/GSM"]}, + {name: "CRC-64/ECMA-182", algo: crc, params: ["CRC-64/ECMA-182"]}, + {name: "CRC-64/GO-ECMA", algo: crc, params: ["CRC-64/GO-ECMA"]}, + {name: "CRC-64/GO-ISO", algo: crc, params: ["CRC-64/GO-ISO"]}, + {name: "CRC-64/MS", algo: crc, params: ["CRC-64/MS"]}, + {name: "CRC-64/NVME", algo: crc, params: ["CRC-64/NVME"]}, + {name: "CRC-64/REDIS", algo: crc, params: ["CRC-64/REDIS"]}, + {name: "CRC-64/WE", algo: crc, params: ["CRC-64/WE"]}, + {name: "CRC-64/XZ", algo: crc, params: ["CRC-64/XZ"]}, + {name: "Fletcher-64", algo: fletcher64, params: []}, + {name: "CRC-82/DARC", algo: crc, params: ["CRC-82/DARC"]} + ]; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [length, includeNames] = args; + let output = ""; + this.checksums.forEach(checksum => { + const checksumLength = checksum.name.match(new RegExp("-(\\d{1,2})(\\/|$)"))[1]; + if (length === "All" || length === checksumLength) { + const value = checksum.algo.run(new Uint8Array(input), checksum.params || []); + output += includeNames ? + `${checksum.name}:${" ".repeat(25-checksum.name.length)}${value}\n`: + `${value}\n`; + } + }); + return output; + } +} + +export default GenerateAllChecksums; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index bb7016bb..cb585297 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -72,6 +72,7 @@ import "./tests/FileTree.mjs"; import "./tests/FletcherChecksum.mjs"; import "./tests/Fork.mjs"; import "./tests/FromDecimal.mjs"; +import "./tests/GenerateAllChecksums.mjs"; import "./tests/GenerateAllHashes.mjs"; import "./tests/GenerateDeBruijnSequence.mjs"; import "./tests/GetAllCasings.mjs"; diff --git a/tests/operations/tests/GenerateAllChecksums.mjs b/tests/operations/tests/GenerateAllChecksums.mjs new file mode 100644 index 00000000..46c4e38c --- /dev/null +++ b/tests/operations/tests/GenerateAllChecksums.mjs @@ -0,0 +1,805 @@ +/** + * GenerateAllChecksums tests. + * + * @author r4mos [2k95ljkhg@mozmail.com] + * @copyright Crown Copyright 2025 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +const CHECK_STRING = "123456789"; + +TestRegister.addTests([ + { + name: "Full generate all checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-3/GSM: 4 +CRC-3/ROHC: 6 +CRC-4/G-704: 7 +CRC-4/INTERLAKEN: b +CRC-4/ITU: 7 +CRC-5/EPC: 00 +CRC-5/EPC-C1G2: 00 +CRC-5/G-704: 07 +CRC-5/ITU: 07 +CRC-5/USB: 19 +CRC-6/CDMA2000-A: 0d +CRC-6/CDMA2000-B: 3b +CRC-6/DARC: 26 +CRC-6/G-704: 06 +CRC-6/GSM: 13 +CRC-6/ITU: 06 +CRC-7/MMC: 75 +CRC-7/ROHC: 53 +CRC-7/UMTS: 61 +CRC-8: f4 +CRC-8/8H2F: df +CRC-8/AES: 97 +CRC-8/AUTOSAR: df +CRC-8/BLUETOOTH: 26 +CRC-8/CDMA2000: da +CRC-8/DARC: 15 +CRC-8/DVB-S2: bc +CRC-8/EBU: 97 +CRC-8/GSM-A: 37 +CRC-8/GSM-B: 94 +CRC-8/HITAG: b4 +CRC-8/I-432-1: a1 +CRC-8/I-CODE: 7e +CRC-8/ITU: a1 +CRC-8/LTE: ea +CRC-8/MAXIM: a1 +CRC-8/MAXIM-DOW: a1 +CRC-8/MIFARE-MAD: 99 +CRC-8/NRSC-5: f7 +CRC-8/OPENSAFETY: 3e +CRC-8/ROHC: d0 +CRC-8/SAE-J1850: 4b +CRC-8/SAE-J1850-ZERO: 37 +CRC-8/SMBUS: f4 +CRC-8/TECH-3250: 97 +CRC-8/WCDMA: 25 +Fletcher-8: 0c +CRC-10/ATM: 199 +CRC-10/CDMA2000: 233 +CRC-10/GSM: 12a +CRC-10/I-610: 199 +CRC-11/FLEXRAY: 5a3 +CRC-11/UMTS: 061 +CRC-12/3GPP: daf +CRC-12/CDMA2000: d4d +CRC-12/DECT: f5b +CRC-12/GSM: b34 +CRC-12/UMTS: daf +CRC-13/BBC: 04fa +CRC-14/DARC: 082d +CRC-14/GSM: 30ae +CRC-15/CAN: 059e +CRC-15/MPT1327: 2566 +CRC-16: bb3d +CRC-16/A: bf05 +CRC-16/ACORN: 31c3 +CRC-16/ARC: bb3d +CRC-16/AUG-CCITT: e5cc +CRC-16/AUTOSAR: 29b1 +CRC-16/B: 906e +CRC-16/BLUETOOTH: 2189 +CRC-16/BUYPASS: fee8 +CRC-16/CCITT: 2189 +CRC-16/CCITT-FALSE: 29b1 +CRC-16/CCITT-TRUE: 2189 +CRC-16/CCITT-ZERO: 31c3 +CRC-16/CDMA2000: 4c06 +CRC-16/CMS: aee7 +CRC-16/DARC: d64e +CRC-16/DDS-110: 9ecf +CRC-16/DECT-R: 007e +CRC-16/DECT-X: 007f +CRC-16/DNP: ea82 +CRC-16/EN-13757: c2b7 +CRC-16/EPC: d64e +CRC-16/EPC-C1G2: d64e +CRC-16/GENIBUS: d64e +CRC-16/GSM: ce3c +CRC-16/I-CODE: d64e +CRC-16/IBM: bb3d +CRC-16/IBM-3740: 29b1 +CRC-16/IBM-SDLC: 906e +CRC-16/IEC-61158-2: a819 +CRC-16/ISO-HDLC: 906e +CRC-16/ISO-IEC-14443-3-A: bf05 +CRC-16/ISO-IEC-14443-3-B: 906e +CRC-16/KERMIT: 2189 +CRC-16/LHA: bb3d +CRC-16/LJ1200: bdf4 +CRC-16/LTE: 31c3 +CRC-16/M17: 772b +CRC-16/MAXIM: 44c2 +CRC-16/MAXIM-DOW: 44c2 +CRC-16/MCRF4XX: 6f91 +CRC-16/MODBUS: 4b37 +CRC-16/NRSC-5: a066 +CRC-16/OPENSAFETY-A: 5d38 +CRC-16/OPENSAFETY-B: 20fe +CRC-16/PROFIBUS: a819 +CRC-16/RIELLO: 63d0 +CRC-16/SPI-FUJITSU: e5cc +CRC-16/T10-DIF: d0db +CRC-16/TELEDISK: 0fb3 +CRC-16/TMS37157: 26b1 +CRC-16/UMTS: fee8 +CRC-16/USB: b4c8 +CRC-16/V-41-LSB: 2189 +CRC-16/V-41-MSB: 31c3 +CRC-16/VERIFONE: fee8 +CRC-16/X-25: 906e +CRC-16/XMODEM: 31c3 +CRC-16/ZMODEM: 31c3 +Fletcher-16: 1ede +CRC-17/CAN-FD: 04f03 +CRC-21/CAN-FD: 0ed841 +CRC-24/BLE: c25a56 +CRC-24/FLEXRAY-A: 7979bd +CRC-24/FLEXRAY-B: 1f23b8 +CRC-24/INTERLAKEN: b4f3e6 +CRC-24/LTE-A: cde703 +CRC-24/LTE-B: 23ef52 +CRC-24/OPENPGP: 21cf02 +CRC-24/OS-9: 200fa5 +CRC-30/CDMA: 04c34abf +CRC-31/PHILIPS: 0ce9e46c +Adler-32: 091e01de +CRC-32: cbf43926 +CRC-32/AAL5: fc891918 +CRC-32/ADCCP: cbf43926 +CRC-32/AIXM: 3010bf7f +CRC-32/AUTOSAR: 1697d06a +CRC-32/BASE91-C: e3069283 +CRC-32/BASE91-D: 87315576 +CRC-32/BZIP2: fc891918 +CRC-32/C: e3069283 +CRC-32/CASTAGNOLI: e3069283 +CRC-32/CD-ROM-EDC: 6ec2edc4 +CRC-32/CKSUM: 765e7680 +CRC-32/D: 87315576 +CRC-32/DECT-B: fc891918 +CRC-32/INTERLAKEN: e3069283 +CRC-32/ISCSI: e3069283 +CRC-32/ISO-HDLC: cbf43926 +CRC-32/JAMCRC: 340bc6d9 +CRC-32/MEF: d2c22f51 +CRC-32/MPEG-2: 0376e6e7 +CRC-32/NVME: e3069283 +CRC-32/PKZIP: cbf43926 +CRC-32/POSIX: 765e7680 +CRC-32/Q: 3010bf7f +CRC-32/SATA: cf72afe8 +CRC-32/V-42: cbf43926 +CRC-32/XFER: bd0be338 +CRC-32/XZ: cbf43926 +Fletcher-32: df09d509 +CRC-40/GSM: d4164fc646 +CRC-64/ECMA-182: 6c40df5f0b497347 +CRC-64/GO-ECMA: 995dc9bbdf1939fa +CRC-64/GO-ISO: b90956c775a41001 +CRC-64/MS: 75d4b74f024eceea +CRC-64/NVME: ae8b14860a799888 +CRC-64/REDIS: e9c6d914c4b8d9ca +CRC-64/WE: 62ec59e3f1a4f00a +CRC-64/XZ: 995dc9bbdf1939fa +Fletcher-64: 0d0803376c6a689f +CRC-82/DARC: 09ea83f625023801fd612 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["All", true] + } + ] + }, + { + name: "Full generate all checksums without name", + input: CHECK_STRING, + expectedOutput: `4 +6 +7 +b +7 +00 +00 +07 +07 +19 +0d +3b +26 +06 +13 +06 +75 +53 +61 +f4 +df +97 +df +26 +da +15 +bc +97 +37 +94 +b4 +a1 +7e +a1 +ea +a1 +a1 +99 +f7 +3e +d0 +4b +37 +f4 +97 +25 +0c +199 +233 +12a +199 +5a3 +061 +daf +d4d +f5b +b34 +daf +04fa +082d +30ae +059e +2566 +bb3d +bf05 +31c3 +bb3d +e5cc +29b1 +906e +2189 +fee8 +2189 +29b1 +2189 +31c3 +4c06 +aee7 +d64e +9ecf +007e +007f +ea82 +c2b7 +d64e +d64e +d64e +ce3c +d64e +bb3d +29b1 +906e +a819 +906e +bf05 +906e +2189 +bb3d +bdf4 +31c3 +772b +44c2 +44c2 +6f91 +4b37 +a066 +5d38 +20fe +a819 +63d0 +e5cc +d0db +0fb3 +26b1 +fee8 +b4c8 +2189 +31c3 +fee8 +906e +31c3 +31c3 +1ede +04f03 +0ed841 +c25a56 +7979bd +1f23b8 +b4f3e6 +cde703 +23ef52 +21cf02 +200fa5 +04c34abf +0ce9e46c +091e01de +cbf43926 +fc891918 +cbf43926 +3010bf7f +1697d06a +e3069283 +87315576 +fc891918 +e3069283 +e3069283 +6ec2edc4 +765e7680 +87315576 +fc891918 +e3069283 +e3069283 +cbf43926 +340bc6d9 +d2c22f51 +0376e6e7 +e3069283 +cbf43926 +765e7680 +3010bf7f +cf72afe8 +cbf43926 +bd0be338 +cbf43926 +df09d509 +d4164fc646 +6c40df5f0b497347 +995dc9bbdf1939fa +b90956c775a41001 +75d4b74f024eceea +ae8b14860a799888 +e9c6d914c4b8d9ca +62ec59e3f1a4f00a +995dc9bbdf1939fa +0d0803376c6a689f +09ea83f625023801fd612 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["All", false] + } + ] + }, + { + name: "Full generate 3 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-3/GSM: 4 +CRC-3/ROHC: 6 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["3", true] + } + ] + }, + { + name: "Full generate 4 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-4/G-704: 7 +CRC-4/INTERLAKEN: b +CRC-4/ITU: 7 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["4", true] + } + ] + }, + { + name: "Full generate 5 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-5/EPC: 00 +CRC-5/EPC-C1G2: 00 +CRC-5/G-704: 07 +CRC-5/ITU: 07 +CRC-5/USB: 19 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["5", true] + } + ] + }, + { + name: "Full generate 6 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-6/CDMA2000-A: 0d +CRC-6/CDMA2000-B: 3b +CRC-6/DARC: 26 +CRC-6/G-704: 06 +CRC-6/GSM: 13 +CRC-6/ITU: 06 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["6", true] + } + ] + }, + { + name: "Full generate 7 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-7/MMC: 75 +CRC-7/ROHC: 53 +CRC-7/UMTS: 61 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["7", true] + } + ] + }, + { + name: "Full generate 8 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-8: f4 +CRC-8/8H2F: df +CRC-8/AES: 97 +CRC-8/AUTOSAR: df +CRC-8/BLUETOOTH: 26 +CRC-8/CDMA2000: da +CRC-8/DARC: 15 +CRC-8/DVB-S2: bc +CRC-8/EBU: 97 +CRC-8/GSM-A: 37 +CRC-8/GSM-B: 94 +CRC-8/HITAG: b4 +CRC-8/I-432-1: a1 +CRC-8/I-CODE: 7e +CRC-8/ITU: a1 +CRC-8/LTE: ea +CRC-8/MAXIM: a1 +CRC-8/MAXIM-DOW: a1 +CRC-8/MIFARE-MAD: 99 +CRC-8/NRSC-5: f7 +CRC-8/OPENSAFETY: 3e +CRC-8/ROHC: d0 +CRC-8/SAE-J1850: 4b +CRC-8/SAE-J1850-ZERO: 37 +CRC-8/SMBUS: f4 +CRC-8/TECH-3250: 97 +CRC-8/WCDMA: 25 +Fletcher-8: 0c +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["8", true] + } + ] + }, + { + name: "Full generate 10 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-10/ATM: 199 +CRC-10/CDMA2000: 233 +CRC-10/GSM: 12a +CRC-10/I-610: 199 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["10", true] + } + ] + }, + { + name: "Full generate 11 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-11/FLEXRAY: 5a3 +CRC-11/UMTS: 061 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["11", true] + } + ] + }, + { + name: "Full generate 12 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-12/3GPP: daf +CRC-12/CDMA2000: d4d +CRC-12/DECT: f5b +CRC-12/GSM: b34 +CRC-12/UMTS: daf +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["12", true] + } + ] + }, + { + name: "Full generate 13 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-13/BBC: 04fa +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["13", true] + } + ] + }, + { + name: "Full generate 14 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-14/DARC: 082d +CRC-14/GSM: 30ae +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["14", true] + } + ] + }, + { + name: "Full generate 15 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-15/CAN: 059e +CRC-15/MPT1327: 2566 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["15", true] + } + ] + }, + { + name: "Full generate 16 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-16: bb3d +CRC-16/A: bf05 +CRC-16/ACORN: 31c3 +CRC-16/ARC: bb3d +CRC-16/AUG-CCITT: e5cc +CRC-16/AUTOSAR: 29b1 +CRC-16/B: 906e +CRC-16/BLUETOOTH: 2189 +CRC-16/BUYPASS: fee8 +CRC-16/CCITT: 2189 +CRC-16/CCITT-FALSE: 29b1 +CRC-16/CCITT-TRUE: 2189 +CRC-16/CCITT-ZERO: 31c3 +CRC-16/CDMA2000: 4c06 +CRC-16/CMS: aee7 +CRC-16/DARC: d64e +CRC-16/DDS-110: 9ecf +CRC-16/DECT-R: 007e +CRC-16/DECT-X: 007f +CRC-16/DNP: ea82 +CRC-16/EN-13757: c2b7 +CRC-16/EPC: d64e +CRC-16/EPC-C1G2: d64e +CRC-16/GENIBUS: d64e +CRC-16/GSM: ce3c +CRC-16/I-CODE: d64e +CRC-16/IBM: bb3d +CRC-16/IBM-3740: 29b1 +CRC-16/IBM-SDLC: 906e +CRC-16/IEC-61158-2: a819 +CRC-16/ISO-HDLC: 906e +CRC-16/ISO-IEC-14443-3-A: bf05 +CRC-16/ISO-IEC-14443-3-B: 906e +CRC-16/KERMIT: 2189 +CRC-16/LHA: bb3d +CRC-16/LJ1200: bdf4 +CRC-16/LTE: 31c3 +CRC-16/M17: 772b +CRC-16/MAXIM: 44c2 +CRC-16/MAXIM-DOW: 44c2 +CRC-16/MCRF4XX: 6f91 +CRC-16/MODBUS: 4b37 +CRC-16/NRSC-5: a066 +CRC-16/OPENSAFETY-A: 5d38 +CRC-16/OPENSAFETY-B: 20fe +CRC-16/PROFIBUS: a819 +CRC-16/RIELLO: 63d0 +CRC-16/SPI-FUJITSU: e5cc +CRC-16/T10-DIF: d0db +CRC-16/TELEDISK: 0fb3 +CRC-16/TMS37157: 26b1 +CRC-16/UMTS: fee8 +CRC-16/USB: b4c8 +CRC-16/V-41-LSB: 2189 +CRC-16/V-41-MSB: 31c3 +CRC-16/VERIFONE: fee8 +CRC-16/X-25: 906e +CRC-16/XMODEM: 31c3 +CRC-16/ZMODEM: 31c3 +Fletcher-16: 1ede +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["16", true] + } + ] + }, + { + name: "Full generate 17 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-17/CAN-FD: 04f03 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["17", true] + } + ] + }, + { + name: "Full generate 21 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-21/CAN-FD: 0ed841 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["21", true] + } + ] + }, + { + name: "Full generate 24 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-24/BLE: c25a56 +CRC-24/FLEXRAY-A: 7979bd +CRC-24/FLEXRAY-B: 1f23b8 +CRC-24/INTERLAKEN: b4f3e6 +CRC-24/LTE-A: cde703 +CRC-24/LTE-B: 23ef52 +CRC-24/OPENPGP: 21cf02 +CRC-24/OS-9: 200fa5 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["24", true] + } + ] + }, + { + name: "Full generate 30 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-30/CDMA: 04c34abf +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["30", true] + } + ] + }, + { + name: "Full generate 31 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-31/PHILIPS: 0ce9e46c +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["31", true] + } + ] + }, + { + name: "Full generate 32 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `Adler-32: 091e01de +CRC-32: cbf43926 +CRC-32/AAL5: fc891918 +CRC-32/ADCCP: cbf43926 +CRC-32/AIXM: 3010bf7f +CRC-32/AUTOSAR: 1697d06a +CRC-32/BASE91-C: e3069283 +CRC-32/BASE91-D: 87315576 +CRC-32/BZIP2: fc891918 +CRC-32/C: e3069283 +CRC-32/CASTAGNOLI: e3069283 +CRC-32/CD-ROM-EDC: 6ec2edc4 +CRC-32/CKSUM: 765e7680 +CRC-32/D: 87315576 +CRC-32/DECT-B: fc891918 +CRC-32/INTERLAKEN: e3069283 +CRC-32/ISCSI: e3069283 +CRC-32/ISO-HDLC: cbf43926 +CRC-32/JAMCRC: 340bc6d9 +CRC-32/MEF: d2c22f51 +CRC-32/MPEG-2: 0376e6e7 +CRC-32/NVME: e3069283 +CRC-32/PKZIP: cbf43926 +CRC-32/POSIX: 765e7680 +CRC-32/Q: 3010bf7f +CRC-32/SATA: cf72afe8 +CRC-32/V-42: cbf43926 +CRC-32/XFER: bd0be338 +CRC-32/XZ: cbf43926 +Fletcher-32: df09d509 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["32", true] + } + ] + }, + { + name: "Full generate 40 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-40/GSM: d4164fc646 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["40", true] + } + ] + }, + { + name: "Full generate 64 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-64/ECMA-182: 6c40df5f0b497347 +CRC-64/GO-ECMA: 995dc9bbdf1939fa +CRC-64/GO-ISO: b90956c775a41001 +CRC-64/MS: 75d4b74f024eceea +CRC-64/NVME: ae8b14860a799888 +CRC-64/REDIS: e9c6d914c4b8d9ca +CRC-64/WE: 62ec59e3f1a4f00a +CRC-64/XZ: 995dc9bbdf1939fa +Fletcher-64: 0d0803376c6a689f +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["64", true] + } + ] + }, + { + name: "Full generate 82 bits checksums with name", + input: CHECK_STRING, + expectedOutput: `CRC-82/DARC: 09ea83f625023801fd612 +`, + recipeConfig: [ + { + "op": "Generate all checksums", + "args": ["82", true] + } + ] + } +]); From c475a248549c30b16d7ec835776d1a0ed2bb76c6 Mon Sep 17 00:00:00 2001 From: PathToLife <12622625+PathToLife@users.noreply.github.com> Date: Fri, 2 May 2025 15:04:17 +1200 Subject: [PATCH 32/46] Re-enable Npm Release in github workflows --- .github/workflows/releases.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index 20968772..a77f4984 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -92,6 +92,5 @@ jobs: - name: Publish to NPM uses: JS-DevTools/npm-publish@v1 - if: false with: - token: ${{ secrets.NPM_TOKEN }} \ No newline at end of file + token: ${{ secrets.NPM_TOKEN }} From c6ece4292bf55f64892b577b9850c0946a66c941 Mon Sep 17 00:00:00 2001 From: Ken Duguay Date: Fri, 9 May 2025 14:20:13 +0000 Subject: [PATCH 33/46] Update the postMessage format to be consistent with cyberchef's internal functions --- src/web/waiters/InputWaiter.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/web/waiters/InputWaiter.mjs b/src/web/waiters/InputWaiter.mjs index 9e05932f..d32ed9d1 100644 --- a/src/web/waiters/InputWaiter.mjs +++ b/src/web/waiters/InputWaiter.mjs @@ -1664,8 +1664,9 @@ class InputWaiter { * @param {string} e.data.value - the value of the message */ handlePostMessage(e) { - if ("data" in e && "type" in e.data && "value" in e.data) { - if (e.data.type === "dataSubmit") { + log.debug(e); + if ("data" in e && "id" in e.data && "value" in e.data) { + if (e.data.id === "setInput") { this.setInput(e.data.value); } } From b156fc99298a328e4410a64c616b2f02f9158b5c Mon Sep 17 00:00:00 2001 From: sw5678 <151949597+sw5678@users.noreply.github.com> Date: Mon, 12 May 2025 11:14:23 +0100 Subject: [PATCH 34/46] Fixed bug where spaces were causing the text to not be correct --- src/core/operations/AlternatingCaps.mjs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/operations/AlternatingCaps.mjs b/src/core/operations/AlternatingCaps.mjs index 17ce3fed..bcdf9523 100644 --- a/src/core/operations/AlternatingCaps.mjs +++ b/src/core/operations/AlternatingCaps.mjs @@ -33,11 +33,18 @@ class AlternatingCaps extends Operation { */ run(input, args) { let output = ""; + let previousCaps = true; for (let i = 0; i < input.length; i++) { - if (i % 2 === 0) { + // Check if the element is a letter + if (!RegExp(/^\p{L}/,'u').test(input[i])) { + output += input[i]; + } + else if (previousCaps) { output += input[i].toLowerCase(); + previousCaps = false; } else { output += input[i].toUpperCase(); + previousCaps = true; } } return output; From 953832092884d96d35d78902d32c7f6787893efe Mon Sep 17 00:00:00 2001 From: sw5678 <151949597+sw5678@users.noreply.github.com> Date: Mon, 12 May 2025 11:20:42 +0100 Subject: [PATCH 35/46] Fix linting errors --- src/core/operations/AlternatingCaps.mjs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/operations/AlternatingCaps.mjs b/src/core/operations/AlternatingCaps.mjs index bcdf9523..86482647 100644 --- a/src/core/operations/AlternatingCaps.mjs +++ b/src/core/operations/AlternatingCaps.mjs @@ -36,10 +36,9 @@ class AlternatingCaps extends Operation { let previousCaps = true; for (let i = 0; i < input.length; i++) { // Check if the element is a letter - if (!RegExp(/^\p{L}/,'u').test(input[i])) { + if (!RegExp(/^\p{L}/, 'u').test(input[i])) { output += input[i]; - } - else if (previousCaps) { + } else if (previousCaps) { output += input[i].toLowerCase(); previousCaps = false; } else { From bd9e0142bc2de6c32d38d7138808b93b476431fd Mon Sep 17 00:00:00 2001 From: sw5678 <151949597+sw5678@users.noreply.github.com> Date: Mon, 12 May 2025 11:24:29 +0100 Subject: [PATCH 36/46] Fix linting errors --- src/core/operations/AlternatingCaps.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/AlternatingCaps.mjs b/src/core/operations/AlternatingCaps.mjs index 86482647..2d54867c 100644 --- a/src/core/operations/AlternatingCaps.mjs +++ b/src/core/operations/AlternatingCaps.mjs @@ -36,7 +36,7 @@ class AlternatingCaps extends Operation { let previousCaps = true; for (let i = 0; i < input.length; i++) { // Check if the element is a letter - if (!RegExp(/^\p{L}/, 'u').test(input[i])) { + if (!RegExp(/^\p{L}/, "u").test(input[i])) { output += input[i]; } else if (previousCaps) { output += input[i].toLowerCase(); From 1bc3105002b6ff71a044e473f053cf743ba2d962 Mon Sep 17 00:00:00 2001 From: es45411 <135977478+es45411@users.noreply.github.com> Date: Mon, 12 May 2025 11:34:15 +0000 Subject: [PATCH 37/46] Update GenerateAllChecksums infoURL --- src/core/operations/GenerateAllChecksums.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/GenerateAllChecksums.mjs b/src/core/operations/GenerateAllChecksums.mjs index 0c6de777..b5a3d152 100644 --- a/src/core/operations/GenerateAllChecksums.mjs +++ b/src/core/operations/GenerateAllChecksums.mjs @@ -26,7 +26,7 @@ class GenerateAllChecksums extends Operation { this.name = "Generate all checksums"; this.module = "Crypto"; this.description = "Generates all available checksums for the input."; - this.infoURL = "https://wikipedia.org/wiki/Cyclic_redundancy_check"; + this.infoURL = "https://wikipedia.org/wiki/Checksum"; this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ From e4f4d9c1c55929471f4677a9238bf79aa4d89278 Mon Sep 17 00:00:00 2001 From: GCHQDeveloper94872 <210844849+GCHQDeveloper94872@users.noreply.github.com> Date: Mon, 12 May 2025 12:15:41 +0000 Subject: [PATCH 38/46] Workaround for Safari load bug --- src/web/static/structuredData.json | 39 ++++++++++++++++-------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/web/static/structuredData.json b/src/web/static/structuredData.json index 08677281..e242f70e 100755 --- a/src/web/static/structuredData.json +++ b/src/web/static/structuredData.json @@ -1,23 +1,26 @@ [ { "@context": "http://schema.org", - "@type": "Organization", - "url": "https://gchq.github.io/CyberChef/", - "logo": "https://gchq.github.io/CyberChef/images/cyberchef-128x128.png", - "sameAs": [ - "https://github.com/gchq/CyberChef", - "https://www.npmjs.com/package/cyberchef" + "@graph": [ + { + "@type": "Organization", + "url": "https://gchq.github.io/CyberChef/", + "logo": "https://gchq.github.io/CyberChef/images/cyberchef-128x128.png", + "sameAs": [ + "https://github.com/gchq/CyberChef", + "https://www.npmjs.com/package/cyberchef" + ] + }, + { + "@type": "WebSite", + "url": "https://gchq.github.io/CyberChef/", + "name": "CyberChef", + "potentialAction": { + "@type": "SearchAction", + "target": "https://gchq.github.io/CyberChef/?op={operation_search_term}", + "query-input": "required name=operation_search_term" + } + } ] - }, - { - "@context": "http://schema.org", - "@type": "WebSite", - "url": "https://gchq.github.io/CyberChef/", - "name": "CyberChef", - "potentialAction": { - "@type": "SearchAction", - "target": "https://gchq.github.io/CyberChef/?op={operation_search_term}", - "query-input": "required name=operation_search_term" - } } -] +] \ No newline at end of file From 159b80e8533ade6c3774762dd09e10e8b98f9a5e Mon Sep 17 00:00:00 2001 From: gchqdev364 <40990156+gchqdev364@users.noreply.github.com> Date: Mon, 12 May 2025 13:02:29 +0000 Subject: [PATCH 39/46] Added explicit tests for IPv4 addresses in decimal or octal. --- src/core/operations/ExtractIPAddresses.mjs | 3 +- tests/operations/index.mjs | 1 + tests/operations/tests/ExtractIPAddresses.mjs | 78 +++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 tests/operations/tests/ExtractIPAddresses.mjs diff --git a/src/core/operations/ExtractIPAddresses.mjs b/src/core/operations/ExtractIPAddresses.mjs index 97b52478..1fe9c96d 100644 --- a/src/core/operations/ExtractIPAddresses.mjs +++ b/src/core/operations/ExtractIPAddresses.mjs @@ -65,7 +65,8 @@ class ExtractIPAddresses extends Operation { */ run(input, args) { const [includeIpv4, includeIpv6, removeLocal, displayTotal, sort, unique] = args, - ipv4 = "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?", + // This regex has two major options; decimal values 0-255 or octal values prefixed with 0 up to 377 + ipv4 = "(?:(?:(?:25[0-5]|2[0-4]\\d|1?[1-9]\\d|\\d)\\.){3}(?:(?:25[0-5]|2[0-4]\\d|1?[1-9]\\d|\\d)))|(?:(?:(?:0[1-3]?[0-7]{0,2})\\.){3}(?:0[1-3]?[0-7]{0,2}))", ipv6 = "((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})(([\\dA-F]{1,4}((?!\\3)::|:\\b|(?![\\dA-F])))|(?!\\2\\3)){2}"; let ips = ""; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index ab1ceb8f..7e49104d 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -65,6 +65,7 @@ import "./tests/ELFInfo.mjs"; import "./tests/Enigma.mjs"; import "./tests/ExtractEmailAddresses.mjs"; import "./tests/ExtractHashes.mjs"; +import "./tests/ExtractIPAddresses.mjs"; import "./tests/Float.mjs"; import "./tests/FileTree.mjs"; import "./tests/FletcherChecksum.mjs"; diff --git a/tests/operations/tests/ExtractIPAddresses.mjs b/tests/operations/tests/ExtractIPAddresses.mjs new file mode 100644 index 00000000..d3eee407 --- /dev/null +++ b/tests/operations/tests/ExtractIPAddresses.mjs @@ -0,0 +1,78 @@ +/** + * ExtractIPAddresses tests. + * + * @author gchqdev365 [gchqdev365@outlook.com] + * @copyright Crown Copyright 2025 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "ExtractIPAddress All Zeros", + input: "0.0.0.0", + expectedOutput: "0.0.0.0", + recipeConfig: [ + { + "op": "Extract IP addresses", + "args": [true, true, false, false, false, false] + }, + ], + }, + { + name: "ExtractIPAddress 255s", + input: "255.255.255.255", + expectedOutput: "255.255.255.255", + recipeConfig: [ + { + "op": "Extract IP addresses", + "args": [true, true, false, false, false, false] + }, + ], + }, + { + name: "ExtractIPAddress double digits", + input: "10.10.10.10 25.25.25.25 99.99.99.99", + expectedOutput: "10.10.10.10\n25.25.25.25\n99.99.99.99", + recipeConfig: [ + { + "op": "Extract IP addresses", + "args": [true, true, false, false, false, false] + }, + ], + }, + { + name: "ExtractIPAddress 256 in middle", + input: "255.256.255.255 255.255.256.255", + expectedOutput: "", + recipeConfig: [ + { + "op": "Extract IP addresses", + "args": [true, true, false, false, false, false] + }, + ], + }, + { + name: "ExtractIPAddress octal valid", + input: "01.01.01.01 0123.0123.0123.0123 0377.0377.0377.0377", + expectedOutput: "01.01.01.01\n0123.0123.0123.0123\n0377.0377.0377.0377", + recipeConfig: [ + { + "op": "Extract IP addresses", + "args": [true, true, false, false, false, false] + }, + ], + }, + { + name: "ExtractIPAddress octal invalid", + input: "0378.01.01.01 03.0377.2.3", + expectedOutput: "", + recipeConfig: [ + { + "op": "Extract IP addresses", + "args": [true, true, false, false, false, false] + }, + ], + }, +]); + From 95d5fd1789fd378ac3fcc1d99d90eea6f71b4a31 Mon Sep 17 00:00:00 2001 From: es45411 <135977478+es45411@users.noreply.github.com> Date: Mon, 12 May 2025 13:09:49 +0000 Subject: [PATCH 40/46] Add treat space as plus URLDecode option --- src/core/operations/URLDecode.mjs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/core/operations/URLDecode.mjs b/src/core/operations/URLDecode.mjs index 7d6544ac..bb6c0612 100644 --- a/src/core/operations/URLDecode.mjs +++ b/src/core/operations/URLDecode.mjs @@ -23,7 +23,13 @@ class URLDecode extends Operation { this.infoURL = "https://wikipedia.org/wiki/Percent-encoding"; this.inputType = "string"; this.outputType = "string"; - this.args = []; + this.args = [ + { + "name": "Treat \"+\" as space", + "type": "boolean", + "value": true + }, + ]; this.checks = [ { pattern: ".*(?:%[\\da-f]{2}.*){4}", @@ -39,7 +45,8 @@ class URLDecode extends Operation { * @returns {string} */ run(input, args) { - const data = input.replace(/\+/g, "%20"); + const plusIsSpace = args[0]; + const data = plusIsSpace ? input.replace(/\+/g, "%20") : input; try { return decodeURIComponent(data); } catch (err) { From 3a55b34214d32209ab918c0a05694972c177011a Mon Sep 17 00:00:00 2001 From: es45411 <135977478+es45411@users.noreply.github.com> Date: Mon, 12 May 2025 13:09:59 +0000 Subject: [PATCH 41/46] Add tests for URLDecode and URLEncode --- tests/operations/index.mjs | 1 + tests/operations/tests/URLEncodeDecode.mjs | 92 ++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 tests/operations/tests/URLEncodeDecode.mjs diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index ab1ceb8f..27151c3c 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -163,6 +163,7 @@ import "./tests/TranslateDateTimeFormat.mjs"; import "./tests/Typex.mjs"; import "./tests/UnescapeString.mjs"; import "./tests/Unicode.mjs"; +import "./tests/URLEncodeDecode.mjs"; import "./tests/RSA.mjs"; import "./tests/CBOREncode.mjs"; import "./tests/CBORDecode.mjs"; diff --git a/tests/operations/tests/URLEncodeDecode.mjs b/tests/operations/tests/URLEncodeDecode.mjs new file mode 100644 index 00000000..444f76d3 --- /dev/null +++ b/tests/operations/tests/URLEncodeDecode.mjs @@ -0,0 +1,92 @@ +/** + * URLEncode and URLDecode tests. + * + * @author es45411 [135977478+es45411@users.noreply.github.com] + * + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + // URL Decode + { + name: "URLDecode: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "URL Decode", + args: [], + }, + ], + }, + { + name: "URLDecode: spaces without special chars", + input: "Hello%20world%21", + expectedOutput: "Hello world!", + recipeConfig: [ + { + op: "URL Decode", + args: [], + }, + ], + }, + { + name: "URLDecode: spaces with special chars", + input: "Hello%20world!", + expectedOutput: "Hello world!", + recipeConfig: [ + { + op: "URL Decode", + args: [], + }, + ], + }, + { + name: "URLDecode: decode plus as space", + input: "Hello%20world!", + expectedOutput: "Hello world!", + recipeConfig: [ + { + op: "URL Decode", + args: [], + }, + ], + }, + // URL Encode + { + name: "URLEncode: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "URL Encode", + args: [], + }, + ], + }, + { + name: "URLEncode: spaces without special chars", + input: "Hello world!", + expectedOutput: "Hello%20world!", + recipeConfig: [ + { + op: "URL Encode", + args: [], + }, + ], + }, + { + name: "URLEncode: spaces with special chars", + input: "Hello world!", + expectedOutput: "Hello%20world%21", + recipeConfig: [ + { + op: "URL Encode", + args: [true], + }, + ], + }, +]); From bfec582aee5fa6ff2d920a197bd5fd12747b1b5b Mon Sep 17 00:00:00 2001 From: gchqdev364 <40990156+gchqdev364@users.noreply.github.com> Date: Mon, 12 May 2025 13:35:39 +0000 Subject: [PATCH 42/46] Using code to generate regex string procedurally to improve readability. --- src/core/operations/ExtractIPAddresses.mjs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/core/operations/ExtractIPAddresses.mjs b/src/core/operations/ExtractIPAddresses.mjs index 1fe9c96d..9eac8c89 100644 --- a/src/core/operations/ExtractIPAddresses.mjs +++ b/src/core/operations/ExtractIPAddresses.mjs @@ -65,8 +65,17 @@ class ExtractIPAddresses extends Operation { */ run(input, args) { const [includeIpv4, includeIpv6, removeLocal, displayTotal, sort, unique] = args, - // This regex has two major options; decimal values 0-255 or octal values prefixed with 0 up to 377 - ipv4 = "(?:(?:(?:25[0-5]|2[0-4]\\d|1?[1-9]\\d|\\d)\\.){3}(?:(?:25[0-5]|2[0-4]\\d|1?[1-9]\\d|\\d)))|(?:(?:(?:0[1-3]?[0-7]{0,2})\\.){3}(?:0[1-3]?[0-7]{0,2}))", + + // IPv4 decimal groups can have values 0 to 255. To construct a regex the following sub-regex is reused: + ipv4DecimalByte = "(?:25[0-5]|2[0-4]\\d|1?[1-9]\\d|\\d)", + ipv4OctalByte = "(?:0[1-3]?[0-7]{1,2})", + + // Each variant requires exactly 4 groups with literal . between + ipv4Decimal = "(?:" + ipv4DecimalByte + "\\.){3}" + "(?:" + ipv4DecimalByte + ")", + ipv4Octal = "(?:" + ipv4OctalByte + "\\.){3}" + "(?:" + ipv4OctalByte + ")", + + // Then we allow IPv4 addresses to be expressed either entirely in decimal or entirely in Octal + ipv4 = "(?:" + ipv4Decimal + "|" + ipv4Octal + ")", ipv6 = "((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})(([\\dA-F]{1,4}((?!\\3)::|:\\b|(?![\\dA-F])))|(?!\\2\\3)){2}"; let ips = ""; From 0c01c6a7c38d0e42324d601887e88cd13642c23b Mon Sep 17 00:00:00 2001 From: gchqdev364 <40990156+gchqdev364@users.noreply.github.com> Date: Mon, 12 May 2025 14:07:21 +0000 Subject: [PATCH 43/46] Added important tests that need to pass before merge. --- tests/operations/tests/ExtractIPAddresses.mjs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/operations/tests/ExtractIPAddresses.mjs b/tests/operations/tests/ExtractIPAddresses.mjs index d3eee407..baa6056a 100644 --- a/tests/operations/tests/ExtractIPAddresses.mjs +++ b/tests/operations/tests/ExtractIPAddresses.mjs @@ -19,6 +19,28 @@ TestRegister.addTests([ }, ], }, + { + name: "ExtractIPAddress All 10s", + input: "10.10.10.10", + expectedOutput: "10.10.10.10", + recipeConfig: [ + { + "op": "Extract IP addresses", + "args": [true, true, false, false, false, false] + }, + ], + }, + { + name: "ExtractIPAddress All 10s", + input: "100.100.100.100", + expectedOutput: "100.100.100.100", + recipeConfig: [ + { + "op": "Extract IP addresses", + "args": [true, true, false, false, false, false] + }, + ], + }, { name: "ExtractIPAddress 255s", input: "255.255.255.255", @@ -52,6 +74,17 @@ TestRegister.addTests([ }, ], }, + { + name: "ExtractIPAddress 256 at each end", + input: "256.255.255.255 255.255.255.256", + expectedOutput: "", + recipeConfig: [ + { + "op": "Extract IP addresses", + "args": [true, true, false, false, false, false] + }, + ], + }, { name: "ExtractIPAddress octal valid", input: "01.01.01.01 0123.0123.0123.0123 0377.0377.0377.0377", From e9b182d33b7e437494e5f7045a25bdbac17fcc24 Mon Sep 17 00:00:00 2001 From: gchqdev364 <40990156+gchqdev364@users.noreply.github.com> Date: Mon, 12 May 2025 14:18:01 +0000 Subject: [PATCH 44/46] Added look ahead and look behind to avoid over capture. --- src/core/operations/ExtractIPAddresses.mjs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/core/operations/ExtractIPAddresses.mjs b/src/core/operations/ExtractIPAddresses.mjs index 9eac8c89..d337e80e 100644 --- a/src/core/operations/ExtractIPAddresses.mjs +++ b/src/core/operations/ExtractIPAddresses.mjs @@ -67,12 +67,16 @@ class ExtractIPAddresses extends Operation { const [includeIpv4, includeIpv6, removeLocal, displayTotal, sort, unique] = args, // IPv4 decimal groups can have values 0 to 255. To construct a regex the following sub-regex is reused: - ipv4DecimalByte = "(?:25[0-5]|2[0-4]\\d|1?[1-9]\\d|\\d)", + ipv4DecimalByte = "(?:25[0-5]|2[0-4]\\d|1?[0-9]\\d|\\d)", ipv4OctalByte = "(?:0[1-3]?[0-7]{1,2})", - // Each variant requires exactly 4 groups with literal . between - ipv4Decimal = "(?:" + ipv4DecimalByte + "\\.){3}" + "(?:" + ipv4DecimalByte + ")", - ipv4Octal = "(?:" + ipv4OctalByte + "\\.){3}" + "(?:" + ipv4OctalByte + ")", + // Look behind and ahead will be used to exclude matches with additional decimal digits left and right of IP address + lookBehind = "(? Date: Mon, 12 May 2025 10:22:22 -0400 Subject: [PATCH 45/46] update: dockerfile Updated Dockerfile to correctly build on ARM64 platforms. The previous version failed. --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index d63a8ca3..ba605fd7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,8 +29,7 @@ RUN npm run build ######################################### # We are using Github Actions: redhat-actions/buildah-build@v2 which needs manual selection of arch in base image # Remove TARGETARCH if docker buildx is supported in the CI release as --platform=$TARGETPLATFORM will be automatically set -ARG TARGETARCH ARG TARGETPLATFORM -FROM ${TARGETARCH}/nginx:stable-alpine AS cyberchef +FROM --platform=${TARGETPLATFORM} nginx:stable-alpine AS cyberchef COPY --from=builder /app/build/prod /usr/share/nginx/html/ From 288cd8f06388543df0fd1a3e5f22b5aa52d171d4 Mon Sep 17 00:00:00 2001 From: gchqdev364 <40990156+gchqdev364@users.noreply.github.com> Date: Mon, 12 May 2025 14:24:07 +0000 Subject: [PATCH 46/46] Updated warning now that original issue has been resolved. --- src/core/operations/ExtractIPAddresses.mjs | 2 +- tests/operations/tests/ExtractIPAddresses.mjs | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/core/operations/ExtractIPAddresses.mjs b/src/core/operations/ExtractIPAddresses.mjs index d337e80e..b74ec8fe 100644 --- a/src/core/operations/ExtractIPAddresses.mjs +++ b/src/core/operations/ExtractIPAddresses.mjs @@ -21,7 +21,7 @@ class ExtractIPAddresses extends Operation { this.name = "Extract IP addresses"; this.module = "Regex"; - this.description = "Extracts all IPv4 and IPv6 addresses.

Warning: Given a string 710.65.0.456, this will match 10.65.0.45 so always check the original input!"; + this.description = "Extracts all IPv4 and IPv6 addresses.

Warning: Given a string 1.2.3.4.5.6.7.8, this will match 1.2.3.4 and 5.6.7.8 so always check the original input!"; this.inputType = "string"; this.outputType = "string"; this.args = [ diff --git a/tests/operations/tests/ExtractIPAddresses.mjs b/tests/operations/tests/ExtractIPAddresses.mjs index baa6056a..13922e64 100644 --- a/tests/operations/tests/ExtractIPAddresses.mjs +++ b/tests/operations/tests/ExtractIPAddresses.mjs @@ -85,6 +85,28 @@ TestRegister.addTests([ }, ], }, + { + name: "ExtractIPAddress silly example", + input: "710.65.0.456", + expectedOutput: "", + recipeConfig: [ + { + "op": "Extract IP addresses", + "args": [true, true, false, false, false, false] + }, + ], + }, + { + name: "ExtractIPAddress longer dotted decimal", + input: "1.2.3.4.5.6.7.8", + expectedOutput: "1.2.3.4\n5.6.7.8", + recipeConfig: [ + { + "op": "Extract IP addresses", + "args": [true, true, false, false, false, false] + }, + ], + }, { name: "ExtractIPAddress octal valid", input: "01.01.01.01 0123.0123.0123.0123 0377.0377.0377.0377",