`;
+
+ for (let i = 0; i < this.ingList.length; i++) {
+ html += this.ingList[i].toHtml();
+ }
+
+ html += `
+ Use the search box to find useful operations.
Both operation names and descriptions are queried using a fuzzy matching algorithm.
"
+ aria-owns="search-results"
/>
diff --git a/src/web/index.js b/src/web/index.js
index 90142b34..110f0d2b 100755
--- a/src/web/index.js
+++ b/src/web/index.js
@@ -17,8 +17,8 @@ import * as CanvasComponents from "../core/lib/CanvasComponents.mjs";
// CyberChef
import App from "./App.mjs";
-import Categories from "../core/config/Categories.json" assert {type: "json"};
-import OperationConfig from "../core/config/OperationConfig.json" assert {type: "json"};
+import Categories from "../core/config/Categories.json" with { type: "json" };
+import OperationConfig from "../core/config/OperationConfig.json" with { type: "json" };
/**
diff --git a/src/web/static/sitemap.mjs b/src/web/static/sitemap.mjs
index 4f8101d4..f373a277 100644
--- a/src/web/static/sitemap.mjs
+++ b/src/web/static/sitemap.mjs
@@ -1,5 +1,5 @@
import sm from "sitemap";
-import OperationConfig from "../../core/config/OperationConfig.json" assert { type: "json" };
+import OperationConfig from "../../core/config/OperationConfig.json" with { type: "json" };
/**
* Generates an XML sitemap for all CyberChef operations and a number of recipes.
diff --git a/src/web/waiters/OperationsWaiter.mjs b/src/web/waiters/OperationsWaiter.mjs
index 9f9971cc..2947f8ae 100755
--- a/src/web/waiters/OperationsWaiter.mjs
+++ b/src/web/waiters/OperationsWaiter.mjs
@@ -28,12 +28,11 @@ class OperationsWaiter {
this.singleTapAlertTimeout = null;
}
-
/**
* Handler for search events.
* Finds operations which match the given search term and displays them under the search box.
*
- * @param {Event} e
+ * @param {KeyboardEvent | ClipboardEvent | Event} e
*/
searchOperations(e) {
let ops, focused;
@@ -58,6 +57,26 @@ class OperationsWaiter {
}
}
+ /**
+ * Sets up the operation element with the correct attributes when focused
+ * @param {HTMLElement} element
+ */
+ const _focusOperation = (element) => {
+ element.classList.add("focused-op");
+ element.scrollIntoView({block: "nearest"});
+ $(element).popover("show");
+ e.target.setAttribute("aria-activedescendant", element.id);
+ };
+
+ /**
+ * Sets up the operation element with the correct attributes when focused
+ * @param {HTMLElement} element
+ */
+ const _defocusOperation = (element) => {
+ element.classList.remove("focused-op");
+ $(element).popover("hide");
+ };
+
if (e.type === "click" && !e.target.value.length) {
this.openOpsDropdown();
} else if (e.key === "Escape") { // Escape
@@ -67,22 +86,18 @@ class OperationsWaiter {
ops = document.querySelectorAll("#search-results c-operation-list c-operation-li li");
if (ops.length) {
focused = this.getFocusedOp(ops);
- if (focused > -1) {
- ops[focused].classList.remove("focused-op");
- }
+ if (focused > -1) _defocusOperation(ops[focused]);
if (focused === ops.length-1) focused = -1;
- ops[focused+1].classList.add("focused-op");
+ _focusOperation(ops[focused+1]);
}
} else if (e.key === "ArrowUp") { // Up
e.preventDefault();
ops = document.querySelectorAll("#search-results c-operation-list c-operation-li li");
if (ops.length) {
focused = this.getFocusedOp(ops);
- if (focused > -1) {
- ops[focused].classList.remove("focused-op");
- }
+ if (focused > -1) _defocusOperation(ops[focused]);
if (focused === 0) focused = ops.length;
- ops[focused-1].classList.add("focused-op");
+ _focusOperation(ops[focused-1]);
}
} else {
const searchResultsEl = document.getElementById("search-results");
@@ -96,6 +111,8 @@ class OperationsWaiter {
searchResultsEl.removeChild(searchResultsEl.firstChild);
}
+ document.querySelector("#search").removeAttribute("aria-activedescendant");
+
$("#categories .show").collapse("hide");
if (str) {
@@ -104,6 +121,7 @@ class OperationsWaiter {
const cOpList = new COperationList(
this.app,
matchedOps,
+ "search-cop-list",
true,
false,
true,
@@ -198,6 +216,7 @@ class OperationsWaiter {
const opList = new COperationList(
this.app,
favCatConfig.ops.map(op => [op]),
+ "favourites-cop-list",
false,
true,
false,
diff --git a/tests/lib/wasmFetchPolyfill.mjs b/tests/lib/wasmFetchPolyfill.mjs
new file mode 100644
index 00000000..cc1a6a39
--- /dev/null
+++ b/tests/lib/wasmFetchPolyfill.mjs
@@ -0,0 +1,31 @@
+/**
+ * Polyfill for Node.js 22+ where globalThis.fetch is built-in but rejects
+ * bare filesystem paths. WASM libraries like argon2-browser call fetch() with
+ * an absolute path (e.g. "/path/to/argon2.wasm") expecting a browser-style
+ * fallback, but Node.js 22's fetch throws synchronously for non-URL strings.
+ *
+ * This wrapper intercepts such calls and serves the file via Node's fs module,
+ * returning a synthetic Response so the WASM module loads correctly.
+ */
+
+import { readFile } from "fs/promises";
+
+if (globalThis.fetch) {
+ const originalFetch = globalThis.fetch;
+ globalThis.fetch = async function patchedFetch(url, options) {
+ const urlStr = typeof url === "string" ?
+ url :
+ url instanceof URL ?
+ url.href :
+ String(url);
+ // Intercept bare filesystem paths (absolute POSIX or Windows)
+ if (urlStr.startsWith("/") || /^[A-Za-z]:[/\\]/.test(urlStr)) {
+ const buffer = await readFile(urlStr);
+ return new Response(buffer, {
+ status: 200,
+ headers: { "Content-Type": "application/wasm" },
+ });
+ }
+ return originalFetch(url, options);
+ };
+}
diff --git a/tests/node/consumers/cjs-consumer.js b/tests/node/consumers/cjs-consumer.js
index 3a759481..f13d9c35 100644
--- a/tests/node/consumers/cjs-consumer.js
+++ b/tests/node/consumers/cjs-consumer.js
@@ -8,9 +8,9 @@
const assert = require("assert");
-require("cyberchef").then(chef => {
+require("cyberchef").then(async chef => {
- const d = chef.bake("Testing, 1 2 3", [
+ const d = await chef.bake("Testing, 1 2 3", [
chef.toHex,
chef.reverse,
{
diff --git a/tests/node/consumers/esm-consumer.mjs b/tests/node/consumers/esm-consumer.mjs
index 2919e533..3a2648e1 100644
--- a/tests/node/consumers/esm-consumer.mjs
+++ b/tests/node/consumers/esm-consumer.mjs
@@ -9,7 +9,7 @@ import assert from "assert";
import chef from "cyberchef";
import { bake, toHex, reverse, unique, multiply } from "cyberchef";
-const a = bake("Testing, 1 2 3", [
+const a = await bake("Testing, 1 2 3", [
toHex,
reverse,
{
@@ -28,7 +28,7 @@ const a = bake("Testing, 1 2 3", [
assert.equal(a.value, "630957449041920");
-const b = chef.bake("Testing, 1 2 3", [
+const b = await chef.bake("Testing, 1 2 3", [
chef.toHex,
chef.reverse,
{
diff --git a/tests/node/tests/Categories.mjs b/tests/node/tests/Categories.mjs
index e6f8bd72..070d78d7 100644
--- a/tests/node/tests/Categories.mjs
+++ b/tests/node/tests/Categories.mjs
@@ -1,6 +1,6 @@
import TestRegister from "../../lib/TestRegister.mjs";
-import Categories from "../../../src/core/config/Categories.json" assert {type: "json"};
-import OperationConfig from "../../../src/core/config/OperationConfig.json" assert {type: "json"};
+import Categories from "../../../src/core/config/Categories.json" with { type: "json" };
+import OperationConfig from "../../../src/core/config/OperationConfig.json" with { type: "json" };
import it from "../assertionHandler.mjs";
import assert from "assert";
diff --git a/tests/node/tests/nodeApi.mjs b/tests/node/tests/nodeApi.mjs
index 92d4d991..2510ef17 100644
--- a/tests/node/tests/nodeApi.mjs
+++ b/tests/node/tests/nodeApi.mjs
@@ -170,77 +170,77 @@ TestRegister.addApiTests([
assert(chef.bake);
}),
- it("chef.bake: should return NodeDish", () => {
- const result = chef.bake("input", "to base 64");
+ it("chef.bake: should return NodeDish", async () => {
+ const result = await chef.bake("input", "to base 64");
assert(result instanceof NodeDish);
}),
- it("chef.bake: should take an input and an op name and perform it", () => {
- const result = chef.bake("some input", "to base 32");
+ it("chef.bake: should take an input and an op name and perform it", async () => {
+ const result = await chef.bake("some input", "to base 32");
assert.strictEqual(result.toString(), "ONXW2ZJANFXHA5LU");
}),
- it("chef.bake: should complain if recipe isnt a valid object", () => {
- assert.throws(() => chef.bake("some input", 3264), {
+ it("chef.bake: should complain if recipe isnt a valid object", async () => {
+ await assert.rejects(() => chef.bake("some input", 3264), {
name: "TypeError",
message: "Recipe can only contain function names or functions"
});
}),
- it("chef.bake: Should complain if string op is invalid", () => {
- assert.throws(() => chef.bake("some input", "not a valid operation"), {
+ it("chef.bake: Should complain if string op is invalid", async () => {
+ await assert.rejects(() => chef.bake("some input", "not a valid operation"), {
name: "TypeError",
message: "Couldn't find an operation with name 'not a valid operation'."
});
}),
- it("chef.bake: Should take an input and an operation and perform it", () => {
- const result = chef.bake("https://google.com/search?q=help", chef.parseURI);
+ it("chef.bake: Should take an input and an operation and perform it", async () => {
+ const result = await chef.bake("https://google.com/search?q=help", chef.parseURI);
assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = help\n");
}),
- it("chef.bake: Should complain if an invalid operation is inputted", () => {
- assert.throws(() => chef.bake("https://google.com/search?q=help", () => {}), {
+ it("chef.bake: Should complain if an invalid operation is inputted", async () => {
+ await assert.rejects(() => chef.bake("https://google.com/search?q=help", () => {}), {
name: "TypeError",
message: "Inputted function not a Chef operation."
});
}),
- it("chef.bake: accepts an array of operation names and performs them all in order", () => {
- const result = chef.bake("https://google.com/search?q=that's a complicated question", ["URL encode", "URL decode", "Parse URI"]);
+ it("chef.bake: accepts an array of operation names and performs them all in order", async () => {
+ const result = await chef.bake("https://google.com/search?q=that's a complicated question", ["URL encode", "URL decode", "Parse URI"]);
assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n");
}),
- it("chef.bake: forgiving with operation names", () =>{
- const result = chef.bake("https://google.com/search?q=that's a complicated question", ["urlencode", "url decode", "parseURI"]);
+ it("chef.bake: forgiving with operation names", async () =>{
+ const result = await chef.bake("https://google.com/search?q=that's a complicated question", ["urlencode", "url decode", "parseURI"]);
assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n");
}),
- it("chef.bake: forgiving with operation names", () =>{
- const result = chef.bake("hello", ["to base 64"]);
+ it("chef.bake: forgiving with operation names", async () =>{
+ const result = await chef.bake("hello", ["to base 64"]);
assert.strictEqual(result.toString(), "aGVsbG8=");
}),
- it("chef.bake: if recipe is empty array, return input as dish", () => {
- const result = chef.bake("some input", []);
+ it("chef.bake: if recipe is empty array, return input as dish", async () => {
+ const result = await chef.bake("some input", []);
assert.strictEqual(result.toString(), "some input");
assert(result instanceof NodeDish, "Result is not instance of NodeDish");
}),
- it("chef.bake: accepts an array of operations as recipe", () => {
- const result = chef.bake("https://google.com/search?q=that's a complicated question", [chef.URLEncode, chef.URLDecode, chef.parseURI]);
+ it("chef.bake: accepts an array of operations as recipe", async () => {
+ const result = await chef.bake("https://google.com/search?q=that's a complicated question", [chef.URLEncode, chef.URLDecode, chef.parseURI]);
assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n");
}),
- it("should complain if an invalid operation is inputted as part of array", () => {
- assert.throws(() => chef.bake("something", [() => {}]), {
+ it("should complain if an invalid operation is inputted as part of array", async () => {
+ await assert.rejects(() => chef.bake("something", [() => {}]), {
name: "TypeError",
message: "Inputted function not a Chef operation."
});
}),
- it("chef.bake: should take single JSON object describing op and args OBJ", () => {
- const result = chef.bake("some input", {
+ it("chef.bake: should take single JSON object describing op and args OBJ", async () => {
+ const result = await chef.bake("some input", {
op: chef.toHex,
args: {
Delimiter: "Colon"
@@ -249,23 +249,23 @@ TestRegister.addApiTests([
assert.strictEqual(result.toString(), "73:6f:6d:65:20:69:6e:70:75:74");
}),
- it("chef.bake: should take single JSON object desribing op with optional args", () => {
- const result = chef.bake("some input", {
+ it("chef.bake: should take single JSON object desribing op with optional args", async () => {
+ const result = await chef.bake("some input", {
op: chef.toHex,
});
assert.strictEqual(result.toString(), "73 6f 6d 65 20 69 6e 70 75 74");
}),
- it("chef.bake: should take single JSON object describing op and args ARRAY", () => {
- const result = chef.bake("some input", {
+ it("chef.bake: should take single JSON object describing op and args ARRAY", async () => {
+ const result = await chef.bake("some input", {
op: chef.toHex,
args: ["Colon"]
});
assert.strictEqual(result.toString(), "73:6f:6d:65:20:69:6e:70:75:74");
}),
- it("chef.bake: should error if op in JSON is not chef op", () => {
- assert.throws(() => chef.bake("some input", {
+ it("chef.bake: should error if op in JSON is not chef op", async () => {
+ await assert.rejects(() => chef.bake("some input", {
op: () => {},
args: ["Colon"],
}), {
@@ -274,8 +274,8 @@ TestRegister.addApiTests([
});
}),
- it("chef.bake: should take multiple ops in JSON object form, some ops by string", () => {
- const result = chef.bake("some input", [
+ it("chef.bake: should take multiple ops in JSON object form, some ops by string", async () => {
+ const result = await chef.bake("some input", [
{
op: chef.toHex,
args: ["Colon"]
@@ -290,8 +290,8 @@ TestRegister.addApiTests([
assert.strictEqual(result.toString(), "67;63;72;66;146;72;66;144;72;66;65;72;62;60;72;66;71;72;66;145;72;67;60;72;67;65;72;67;64");
}),
- it("chef.bake: should take multiple ops in JSON object form, some without args", () => {
- const result = chef.bake("some input", [
+ it("chef.bake: should take multiple ops in JSON object form, some without args", async () => {
+ const result = await chef.bake("some input", [
{
op: chef.toHex,
},
@@ -305,8 +305,8 @@ TestRegister.addApiTests([
assert.strictEqual(result.toString(), "67;63;40;66;146;40;66;144;40;66;65;40;62;60;40;66;71;40;66;145;40;67;60;40;67;65;40;67;64");
}),
- it("chef.bake: should handle op with multiple args", () => {
- const result = chef.bake("some input", {
+ it("chef.bake: should handle op with multiple args", async () => {
+ const result = await chef.bake("some input", {
op: "to morse code",
args: {
formatOptions: "Dash/Dot",
@@ -317,13 +317,13 @@ TestRegister.addApiTests([
assert.strictEqual(result.toString(), "DotDotDot\\DashDashDash\\DashDash\\Dot,DotDot\\DashDot\\DotDashDashDot\\DotDotDash\\Dash");
}),
- it("chef.bake: should take compact JSON format from Chef Website as recipe", () => {
- const result = chef.bake("some input", [{"op": "To Morse Code", "args": ["Dash/Dot", "Backslash", "Comma"]}, {"op": "Hex to PEM", "args": ["SOMETHING"]}, {"op": "To Snake case", "args": [false]}]);
+ it("chef.bake: should take compact JSON format from Chef Website as recipe", async () => {
+ const result = await chef.bake("some input", [{"op": "To Morse Code", "args": ["Dash/Dot", "Backslash", "Comma"]}, {"op": "Hex to PEM", "args": ["SOMETHING"]}, {"op": "To Snake case", "args": [false]}]);
assert.strictEqual(result.toString(), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something");
}),
- it("chef.bake: should accept Clean JSON format from Chef website as recipe", () => {
- const result = chef.bake("some input", [
+ it("chef.bake: should accept Clean JSON format from Chef website as recipe", async () => {
+ const result = await chef.bake("some input", [
{ "op": "To Morse Code",
"args": ["Dash/Dot", "Backslash", "Comma"] },
{ "op": "Hex to PEM",
@@ -334,8 +334,8 @@ TestRegister.addApiTests([
assert.strictEqual(result.toString(), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something");
}),
- it("chef.bake: should accept Clean JSON format from Chef website - args optional", () => {
- const result = chef.bake("some input", [
+ it("chef.bake: should accept Clean JSON format from Chef website - args optional", async () => {
+ const result = await chef.bake("some input", [
{ "op": "To Morse Code" },
{ "op": "Hex to PEM",
"args": ["SOMETHING"] },
@@ -345,31 +345,28 @@ TestRegister.addApiTests([
assert.strictEqual(result.toString(), "begin_something_aaaaaaaaaaaaaa_end_something");
}),
- it("chef.bake: should accept operation names from Chef Website which contain forward slash", () => {
- const result = chef.bake("I'll have the test salmon", [
+ it("chef.bake: should accept operation names from Chef Website which contain forward slash", async () => {
+ const result = await chef.bake("I'll have the test salmon", [
{ "op": "Find / Replace",
"args": [{ "option": "Regex", "string": "test" }, "good", true, false, true, false]}
]);
assert.strictEqual(result.toString(), "I'll have the good salmon");
}),
- it("chef.bake: should accept operation names from Chef Website which contain a hyphen", () => {
- const result = chef.bake("I'll have the test salmon", [
+ it("chef.bake: should accept operation names from Chef Website which contain a hyphen", async () => {
+ const result = await chef.bake("I'll have the test salmon", [
{ "op": "Adler-32 Checksum",
"args": [] }
]);
assert.strictEqual(result.toString(), "6e4208f8");
}),
- it("chef.bake: should accept operation names from Chef Website which contain a period", () => {
- const result = chef.bake("30 13 02 01 05 16 0e 41 6e 79 62 6f 64 79 20 74 68 65 72 65 3f", [
+ it("chef.bake: should accept operation names from Chef Website which contain a period", async () => {
+ const result = await chef.bake("30 13 02 01 05 16 0e 41 6e 79 62 6f 64 79 20 74 68 65 72 65 3f", [
{ "op": "Parse ASN.1 hex string",
"args": [0, 32] }
]);
- assert.strictEqual(result.toString(), `SEQUENCE
- INTEGER 05
- IA5String 'Anybody there?'
-`);
+ assert.strictEqual(result.toString(), `SEQUENCE\n INTEGER 05\n IA5String 'Anybody there?'\n`);
}),
it("Excluded operations: throw a sensible error when you try and call one", () => {
@@ -381,16 +378,16 @@ TestRegister.addApiTests([
}
}),
- it("chef.bake: cannot accept flowControl operations in recipe", () => {
- assert.throws(() => chef.bake("some input", "magic"), {
+ it("chef.bake: cannot accept flowControl operations in recipe", async () => {
+ await assert.rejects(() => chef.bake("some input", "magic"), {
name: "TypeError",
message: "flowControl operations like Magic are not currently allowed in recipes for chef.bake in the Node API"
});
- assert.throws(() => chef.bake("some input", magic), {
+ await assert.rejects(() => chef.bake("some input", magic), {
name: "TypeError",
message: "flowControl operations like Magic are not currently allowed in recipes for chef.bake in the Node API"
});
- assert.throws(() => chef.bake("some input", ["to base 64", "magic"]), {
+ await assert.rejects(() => chef.bake("some input", ["to base 64", "magic"]), {
name: "TypeError",
message: "flowControl operations like Magic are not currently allowed in recipes for chef.bake in the Node API"
});
diff --git a/tests/node/tests/operations.mjs b/tests/node/tests/operations.mjs
index 41eddd82..a97f5ccb 100644
--- a/tests/node/tests/operations.mjs
+++ b/tests/node/tests/operations.mjs
@@ -589,8 +589,7 @@ Password: 282760`;
...[1, 3, 4, 5, 6, 7].map(version => it(`Analyze UUID v${version}`, () => {
const uuid = chef.generateUUID("", { "version": `v${version}` }).toString();
const result = chef.analyseUUID(uuid).toString();
- const expected = `UUID version: ${version}`;
- assert.strictEqual(result, expected);
+ assert.ok(result.startsWith(`Version:\n${version}\n`), `Expected output to start with "Version:\\n${version}\\n", got: ${result}`);
})),
it("Generate UUID using defaults", () => {
@@ -598,7 +597,7 @@ Password: 282760`;
assert.ok(uuid);
const analysis = chef.analyseUUID(uuid).toString();
- assert.strictEqual(analysis, "UUID version: 4");
+ assert.ok(analysis.startsWith("Version:\n4\n"), `Expected output to start with "Version:\\n4\\n", got: ${analysis}`);
}),
it("Gzip, Gunzip", () => {
diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs
index d18fbffe..b25a2c2c 100644
--- a/tests/operations/index.mjs
+++ b/tests/operations/index.mjs
@@ -11,11 +11,13 @@
* @license Apache-2.0
*/
+import "../lib/wasmFetchPolyfill.mjs";
import { setLongTestFailure, logTestReport } from "../lib/utils.mjs";
import TestRegister from "../lib/TestRegister.mjs";
import "./tests/A1Z26CipherDecode.mjs";
import "./tests/AESKeyWrap.mjs";
+import "./tests/AnalyseUUID.mjs";
import "./tests/AlternatingCaps.mjs";
import "./tests/AvroToJSON.mjs";
import "./tests/BaconCipher.mjs";
@@ -71,6 +73,7 @@ import "./tests/ExtractAudioMetadata.mjs";
import "./tests/ExtractEmailAddresses.mjs";
import "./tests/ExtractHashes.mjs";
import "./tests/ExtractIPAddresses.mjs";
+import "./tests/Fernet.mjs";
import "./tests/Float.mjs";
import "./tests/FileTree.mjs";
import "./tests/FletcherChecksum.mjs";
@@ -134,6 +137,7 @@ import "./tests/ParseUDP.mjs";
import "./tests/PEMtoHex.mjs";
import "./tests/PGP.mjs";
import "./tests/PHP.mjs";
+import "./tests/ParityBit.mjs";
import "./tests/PHPSerialize.mjs";
import "./tests/PowerSet.mjs";
import "./tests/Protobuf.mjs";
@@ -144,6 +148,7 @@ import "./tests/RAKE.mjs";
import "./tests/Regex.mjs";
import "./tests/Register.mjs";
import "./tests/RegularExpression.mjs";
+import "./tests/RenderMarkdown.mjs";
import "./tests/RisonEncodeDecode.mjs";
import "./tests/Rotate.mjs";
import "./tests/RSA.mjs";
diff --git a/tests/operations/tests/AnalyseUUID.mjs b/tests/operations/tests/AnalyseUUID.mjs
new file mode 100644
index 00000000..89118421
--- /dev/null
+++ b/tests/operations/tests/AnalyseUUID.mjs
@@ -0,0 +1,66 @@
+/**
+ * Analyse UUID tests
+ *
+ * @author ko80240 [csk.dev@proton.me]
+ * @copyright Crown Copyright 2023
+ * @license Apache-2.0
+ */
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ "name": "Analyse UUID: v1 UUID extracts timestamp, clock, and node",
+ "input": "cefa1760-28ee-11f1-9f95-1fb76af3e239",
+ "expectedOutput": "Version:\n1\n\nTimestamp:\n1774514156502\n\nTimestamp (ISO):\n2026-03-26T08:35:56.502Z\n\nNode:\n1F:B7:6A:F3:E2:39\n\nClock:\n8085\n\nUUID Integer:\n275119515460318071558429785403790975545",
+ "recipeConfig": [
+ {
+ "op": "Analyse UUID",
+ "args": [true]
+ }
+ ]
+ },
+ {
+ "name": "Analyse UUID: v7 UUID extracts timestamp, randA, and randB",
+ "input": "019d294a-af64-7728-9524-26da08f50708",
+ "expectedOutput": "Version:\n7\n\nTimestamp:\n1774514253668\n\nTimestamp (ISO):\n2026-03-26T08:37:33.668Z\n\nRand A:\n1832\n\nRand B:\n952426DA08F50708\n\nUUID Integer:\n2145256098533991595556290452700595976",
+ "recipeConfig": [
+ {
+ "op": "Analyse UUID",
+ "args": [true]
+ }
+ ]
+ },
+ {
+ "name": "Analyse UUID: v4 UUID should show no metadata - not possible",
+ "input": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
+ "expectedOutput": "Version:\n4\n\nNo metadata available. Only versions 1, 6, 7 are supported.\n\nUUID Integer:\n324969006592305634633390616021200786553",
+ "recipeConfig": [
+ {
+ "op": "Analyse UUID",
+ "args": [true]
+ }
+ ]
+ },
+ {
+ "name": "Analyse UUID: if the 'Include Metadata' option is false it should return not metadata",
+ "input": "cefa1760-28ee-11f1-9f95-1fb76af3e239",
+ "expectedOutput": "Version:\n1\n\nUUID Integer:\n275119515460318071558429785403790975545",
+ "recipeConfig": [
+ {
+ "op": "Analyse UUID",
+ "args": [false]
+ }
+ ]
+ },
+ {
+ "name": "Analyse UUID: invalid UUID should return error message",
+ "input": "not-a-uuid",
+ "expectedOutput": "Invalid UUID",
+ "recipeConfig": [
+ {
+ "op": "Analyse UUID",
+ "args": [true]
+ }
+ ]
+ }
+]);
diff --git a/tests/operations/tests/Base64.mjs b/tests/operations/tests/Base64.mjs
index 6e6fa703..5dca403a 100644
--- a/tests/operations/tests/Base64.mjs
+++ b/tests/operations/tests/Base64.mjs
@@ -116,4 +116,15 @@ TestRegister.addTests([
},
],
},
+ {
+ name: "Show Base64 offsets: escapes static output",
+ input: "\x00\x10\x83\x10\x51\x87",
+ expectedOutput: "<script>\n<AQmsBRk66\n<ia1AEIM6",
+ recipeConfig: [
+ {
+ op: "Show Base64 offsets",
+ args: ["