cyberchef 10.24.0 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/.devcontainer/devcontainer.json +1 -1
  2. package/.nvmrc +1 -1
  3. package/CHANGELOG.md +172 -0
  4. package/Dockerfile +2 -2
  5. package/Gruntfile.js +11 -6
  6. package/README.md +6 -4
  7. package/SECURITY.md +8 -18
  8. package/babel.config.js +4 -1
  9. package/package.json +48 -43
  10. package/src/core/ChefWorker.js +1 -1
  11. package/src/core/Recipe.mjs +1 -1
  12. package/src/core/config/Categories.json +6 -0
  13. package/src/core/config/OperationConfig.json +140 -16
  14. package/src/core/config/modules/Default.mjs +8 -0
  15. package/src/core/config/modules/PGP.mjs +2 -0
  16. package/src/core/config/scripts/generateOpsIndex.mjs +63 -0
  17. package/src/core/config/scripts/newOperation.mjs +31 -4
  18. package/src/core/lib/Magic.mjs +1 -1
  19. package/src/core/operations/AESDecrypt.mjs +61 -16
  20. package/src/core/operations/AESEncrypt.mjs +26 -11
  21. package/src/core/operations/BLAKE3.mjs +13 -7
  22. package/src/core/operations/BSONDeserialise.mjs +2 -2
  23. package/src/core/operations/BSONSerialise.mjs +3 -2
  24. package/src/core/operations/Bcrypt.mjs +1 -1
  25. package/src/core/operations/BcryptCompare.mjs +1 -1
  26. package/src/core/operations/DecodeText.mjs +4 -0
  27. package/src/core/operations/EncodeText.mjs +4 -0
  28. package/src/core/operations/EscapeSmartCharacters.mjs +129 -0
  29. package/src/core/operations/FromPunycode.mjs +1 -1
  30. package/src/core/operations/GenerateLoremIpsum.mjs +34 -3
  31. package/src/core/operations/GeneratePGPKeyPair.mjs +8 -7
  32. package/src/core/operations/PGPSign.mjs +83 -0
  33. package/src/core/operations/ParseEthernetFrame.mjs +1 -1
  34. package/src/core/operations/ParseIPv4Header.mjs +1 -1
  35. package/src/core/operations/ParseObjectIDTimestamp.mjs +2 -2
  36. package/src/core/operations/ParseUserAgent.mjs +1 -1
  37. package/src/core/operations/ROR13.mjs +83 -0
  38. package/src/core/operations/RemoveANSIEscapeCodes.mjs +41 -0
  39. package/src/core/operations/SeriesChart.mjs +16 -0
  40. package/src/core/operations/ShowBase64Offsets.mjs +28 -28
  41. package/src/core/operations/ToPunycode.mjs +1 -1
  42. package/src/core/operations/Wrap.mjs +47 -0
  43. package/src/core/operations/index.mjs +10 -0
  44. package/src/node/NodeRecipe.mjs +8 -7
  45. package/src/node/api.mjs +4 -4
  46. package/src/node/index.mjs +25 -0
  47. package/src/web/App.mjs +19 -1
  48. package/src/web/HTMLIngredient.mjs +1 -0
  49. package/src/web/html/index.html +3 -3
  50. package/src/web/index.js +2 -2
  51. package/src/web/static/sitemap.mjs +4 -4
  52. package/src/web/waiters/RecipeWaiter.mjs +9 -1
  53. package/tests/browser/02_ops.js +7 -7
  54. package/tests/browser/03_recipe_load.js +48 -0
  55. package/tests/browser/browserUtils.js +6 -3
  56. package/tests/lib/wasmFetchPolyfill.mjs +31 -0
  57. package/tests/node/consumers/cjs-consumer.js +2 -2
  58. package/tests/node/consumers/esm-consumer.mjs +2 -2
  59. package/tests/node/index.mjs +1 -0
  60. package/tests/node/tests/Categories.mjs +2 -2
  61. package/tests/node/tests/PGP.mjs +69 -0
  62. package/tests/node/tests/nodeApi.mjs +55 -58
  63. package/tests/node/tests/operations.mjs +41 -2
  64. package/tests/operations/index.mjs +72 -66
  65. package/tests/operations/tests/BLAKE3.mjs +18 -0
  66. package/tests/operations/tests/Base64.mjs +11 -0
  67. package/tests/operations/tests/CharEnc.mjs +26 -0
  68. package/tests/operations/tests/Charts.mjs +11 -0
  69. package/tests/operations/tests/Crypt.mjs +288 -62
  70. package/tests/operations/tests/EscapeSmartCharacters.mjs +132 -0
  71. package/tests/operations/tests/FlaskSession.mjs +11 -8
  72. package/tests/operations/tests/GenerateLoremIpsum.mjs +80 -0
  73. package/tests/operations/tests/IPv6Transition.mjs +4 -4
  74. package/tests/operations/tests/PGP.mjs +178 -154
  75. package/tests/operations/tests/ParseEthernetFrame.mjs +11 -0
  76. package/tests/operations/tests/ParseIPv4Header.mjs +23 -0
  77. package/tests/operations/tests/ParseX509CRL.mjs +16 -16
  78. package/tests/operations/tests/ROR13.mjs +45 -0
  79. package/tests/operations/tests/Register.mjs +3 -1
  80. package/tests/operations/tests/RemoveANSIEscapeCodes.mjs +62 -0
  81. package/tests/operations/tests/Wrap.mjs +44 -0
@@ -1,5 +1,5 @@
1
- import sm from "sitemap";
2
- import OperationConfig from "../../core/config/OperationConfig.json" assert { type: "json" };
1
+ import { SitemapStream, streamToPromise } from "sitemap";
2
+ import OperationConfig from "../../core/config/OperationConfig.json" with { type: "json" };
3
3
 
4
4
  /**
5
5
  * Generates an XML sitemap for all CyberChef operations and a number of recipes.
@@ -11,7 +11,7 @@ import OperationConfig from "../../core/config/OperationConfig.json" assert { ty
11
11
 
12
12
  const baseUrl = "https://gchq.github.io/CyberChef/";
13
13
 
14
- const smStream = new sm.SitemapStream({});
14
+ const smStream = new SitemapStream({});
15
15
 
16
16
  smStream.write({
17
17
  url: baseUrl,
@@ -28,6 +28,6 @@ for (const op in OperationConfig) {
28
28
  }
29
29
  smStream.end();
30
30
 
31
- sm.streamToPromise(smStream).then(
31
+ streamToPromise(smStream).then(
32
32
  (buffer) => console.log(buffer.toString()), // eslint-disable-line no-console
33
33
  );
@@ -487,11 +487,19 @@ class RecipeWaiter {
487
487
  * @param {HTMLElement} op
488
488
  */
489
489
  triggerArgEvents(op) {
490
- // Trigger populateOption and argSelector events
490
+ // Trigger argSelector events and populateOption events only where the target is empty.
491
+ // When loading a saved recipe, arguments are populated before this method is called, so
492
+ // re-triggering populateOption events would overwrite saved custom values with defaults.
493
+ const args = op.querySelectorAll(".arg");
491
494
  const triggerableOptions = op.querySelectorAll(".populate-option, .arg-selector");
492
495
  const evt = new Event("change", {bubbles: true});
496
+
493
497
  if (triggerableOptions.length) {
494
498
  for (const el of triggerableOptions) {
499
+ if (el.classList.contains("populate-option")) {
500
+ const target = args[el.getAttribute("data-target")];
501
+ if (target && target.value !== "") continue;
502
+ }
495
503
  el.dispatchEvent(evt);
496
504
  }
497
505
  }
@@ -30,8 +30,8 @@ module.exports = {
30
30
  testOp(browser, "A1Z26 Cipher Decode", "20 5 19 20 15 21 20 16 21 20", "testoutput");
31
31
  testOp(browser, "A1Z26 Cipher Encode", "test input", "20 5 19 20 9 14 16 21 20");
32
32
  testOp(browser, "ADD", "test input", "Ê»ÉÊv¿ÄÆËÊ", [{ "option": "Hex", "string": "56" }]);
33
- testOp(browser, "AES Decrypt", "b443f7f7c16ac5396a34273f6f639caa", "test output", [{ "option": "Hex", "string": "00112233445566778899aabbccddeeff" }, { "option": "Hex", "string": "00000000000000000000000000000000" }, "CBC", "Hex", "Raw", { "option": "Hex", "string": "" }]);
34
- testOp(browser, "AES Encrypt", "test input", "e42eb8fbfb7a98fff061cd2c1a794d92", [{"option": "Hex", "string": "00112233445566778899aabbccddeeff"}, {"option": "Hex", "string": "00000000000000000000000000000000"}, "CBC", "Raw", "Hex"]);
33
+ testOp(browser, "AES Decrypt", "b443f7f7c16ac5396a34273f6f639caa", "test output", [{ "option": "Hex", "string": "00112233445566778899aabbccddeeff" }, { "option": "Hex", "string": "00000000000000000000000000000000" }, 16, "CBC", "Hex", "Raw", { "option": "Hex", "string": "" }, { "option": "Hex", "string": "" }, "Off"]);
34
+ testOp(browser, "AES Encrypt", "test input", "e42eb8fbfb7a98fff061cd2c1a794d92", [{"option": "Hex", "string": "00112233445566778899aabbccddeeff"}, {"option": "Hex", "string": "00000000000000000000000000000000"}, "CBC", "Raw", "Hex", "Off"]);
35
35
  testOp(browser, "AND", "test input", "4$04 $044", [{ "option": "Hex", "string": "34" }]);
36
36
  testOp(browser, "Add line numbers", "test input", "1 test input");
37
37
  testOp(browser, ["From Hex", "Add Text To Image", "SHA2"], Images.PNG_HEX, "50cdf8ea483c55564a091650c2bccb4586f919b721e5fe9d6a61660505b4346d6ebdb2ef0cf075a7728cd84cb26ea3e477b5bd86a94a49a27d79423994afb60a", [[], ["Chef", "Center", "Middle", 0, 0, 16, "Roboto"], []]);
@@ -64,7 +64,7 @@ module.exports = {
64
64
  testOp(browser, "BSON serialise", '{"a":"test"}', "\u0011\u0000\u0000\u0000\u0002a\u0000\u0005\u0000\u0000\u0000test\u0000\u0000");
65
65
  // testOp(browser, "Bacon Cipher Decode", "test input", "test_output");
66
66
  // testOp(browser, "Bacon Cipher Encode", "test input", "test_output");
67
- testOp(browser, "Bcrypt", "test input", /^\$2a\$06\$.{53}$/, [6]);
67
+ testOp(browser, "Bcrypt", "test input", /^\$2b\$06\$.{53}$/, [6]);
68
68
  testOp(browser, "Bcrypt compare", "test input", "Match: test input", ["$2a$05$FCfBSVX7OeRkK.9kQVFCiOYu9XtwtIbePqUiroD1lkASW9q5QClzG"]);
69
69
  testOp(browser, "Bcrypt parse", "$2a$05$kXWtAIGB/R8VEzInoM5ocOTBtyc0m2YTIwFiBU/0XoW032f9QrkWW", /Rounds: 5/);
70
70
  testOp(browser, "Bifid Cipher Decode", "qblb tfovy", "test input", ["pass"]);
@@ -80,8 +80,8 @@ module.exports = {
80
80
  testOpHtml(browser, "Bombe", "XTSYN WAEUG EZALY NRQIM AMLZX MFUOD AWXLY LZCUZ QOQBQ JLCPK NDDRW F", "table tr:last-child td:first-child", "ECG", ["3-rotor", "LEYJVCNIXWPBQMDRTAKZGFUHOS", "BDFHJLCPRTXVZNYEIWGAKMUSQO<W", "AJDKSIRUXBLHWTMCQGZNPYFVOE<F", "ESOVPZJAYQUIRHXLNFTGKDCMWB<K", "AY BR CU DH EQ FS GL IP JX KN MO TZ VW", "HELLO CYBER CHEFU SER", 0, true]);
81
81
  testOp(browser, ["Bzip2 Compress", "To Hex"], "test input", "42 5a 68 39 31 41 59 26 53 59 cf 96 82 1d 00 00 03 91 80 40 00 02 21 4e 00 20 00 21 90 c2 10 c0 88 33 92 8e df 17 72 45 38 50 90 cf 96 82 1d");
82
82
  testOp(browser, ["From Hex", "Bzip2 Decompress"], "425a68393141592653597b0884b7000003038000008200ce00200021a647a4218013709517c5dc914e14241ec2212dc0", "test_output", [[], [true]]);
83
- // testOp(browser, "CBOR Decode", "test input", "test output");
84
- // testOp(browser, "CBOR Encode", "test input", "test output");
83
+ testOp(browser, ["From Hex", "CBOR Decode"], "f9 3e 00", "1.5");
84
+ testOp(browser, ["CBOR Encode", "To Hex"], "1.5", "f9 3e 00");
85
85
  testOp(browser, "CRC Checksum", "test input", "77c7", ["CRC-16"]);
86
86
  testOp(browser, "CRC Checksum", "test input", "29822bc8", ["CRC-32"]);
87
87
  testOp(browser, "CRC Checksum", "test input", "9d", ["CRC-8"]);
@@ -278,11 +278,11 @@ module.exports = {
278
278
  testOpHtml(browser, "Parse UDP", "04 89 00 35 00 2c 01 01", "tr:last-child td:last-child", "0x0101");
279
279
  // testOp(browser, "Parse UNIX file permissions", "test input", "test_output");
280
280
  // testOp(browser, "Parse URI", "test input", "test_output");
281
- // testOp(browser, "Parse User Agent", "test input", "test_output");
281
+ testOp(browser, "Parse User Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0 ", /Architecture: amd64/);
282
282
  // testOp(browser, "Parse X.509 certificate", "test input", "test_output");
283
283
  testOpFile(browser, "Play Media", "files/mp3example.mp3", "audio", "");
284
284
  // testOp(browser, "Power Set", "test input", "test_output");
285
- // testOp(browser, "Protobuf Decode", "test input", "test_output");
285
+ testOp(browser, ["From Hex", "Protobuf Decode"], "0d1c0000001203596f751a024d65202b2a0a0a066162633132331200", /"1": "abc123"/, [[], ["", false, false]]);
286
286
  // testOp(browser, "Pseudo-Random Number Generator", "test input", "test_output");
287
287
  // testOp(browser, "RC2 Decrypt", "test input", "test_output");
288
288
  // testOp(browser, "RC2 Encrypt", "test input", "test_output");
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Regression tests for recipe loading behaviour.
3
+ *
4
+ * @author C85297 [95289555+C85297@users.noreply.github.com]
5
+ * @copyright Crown Copyright
6
+ * @license Apache-2.0
7
+ */
8
+
9
+ const utils = require("./browserUtils.js");
10
+
11
+ module.exports = {
12
+ before: browser => {
13
+ browser
14
+ .resizeWindow(1280, 800)
15
+ .url(browser.launchUrl)
16
+ .useCss()
17
+ .waitForElementNotPresent("#preloader", 10000);
18
+ },
19
+
20
+ "Recipe load preserves populated arguments": browser => {
21
+ const inputFormat = "HH:mm:ss a MMM DD, YYYY ";
22
+ const input = "10:20:30 pm Sep 26, 2019 ";
23
+
24
+ utils.loadRecipe(
25
+ browser,
26
+ "Translate DateTime Format",
27
+ input,
28
+ [
29
+ "Standard date and time",
30
+ inputFormat,
31
+ "UTC",
32
+ "DD/MM/YYYY HH:mm:ss",
33
+ "UTC"
34
+ ]
35
+ );
36
+
37
+ browser.execute(() => {
38
+ return Array.from(document.querySelectorAll("#rec-list li.operation .arg"))
39
+ .map(arg => arg.value);
40
+ }, [], function({value}) {
41
+ browser.expect(value[1]).to.equal(inputFormat);
42
+ });
43
+ },
44
+
45
+ after: browser => {
46
+ browser.end();
47
+ }
48
+ };
@@ -51,14 +51,17 @@ function setInput(browser, input, type=true) {
51
51
  */
52
52
  function bake(browser) {
53
53
  browser
54
+ // Let any pending debounced inputChange/stateChange (~20ms each) fire so the
55
+ // worker has the latest input buffer before we ask it to bake.
56
+ .pause(50)
54
57
  // Ensure we're not currently busy
55
- .waitForElementNotVisible("#output-loader", 5000)
58
+ .waitForElementNotVisible("#output-loader", 10000)
56
59
  .expect.element("#bake span").text.to.equal("BAKE!");
57
60
 
58
61
  browser
59
62
  .click("#bake")
60
- .waitForElementNotVisible("#stale-indicator", 5000)
61
- .waitForElementNotVisible("#output-loader", 5000);
63
+ .waitForElementNotVisible("#stale-indicator", 10000)
64
+ .waitForElementNotVisible("#output-loader", 10000);
62
65
  }
63
66
 
64
67
  /** @function
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Polyfill for Node.js 22+ where globalThis.fetch is built-in but rejects
3
+ * bare filesystem paths. WASM libraries like argon2-browser call fetch() with
4
+ * an absolute path (e.g. "/path/to/argon2.wasm") expecting a browser-style
5
+ * fallback, but Node.js 22's fetch throws synchronously for non-URL strings.
6
+ *
7
+ * This wrapper intercepts such calls and serves the file via Node's fs module,
8
+ * returning a synthetic Response so the WASM module loads correctly.
9
+ */
10
+
11
+ import { readFile } from "fs/promises";
12
+
13
+ if (globalThis.fetch) {
14
+ const originalFetch = globalThis.fetch;
15
+ globalThis.fetch = async function patchedFetch(url, options) {
16
+ const urlStr = typeof url === "string" ?
17
+ url :
18
+ url instanceof URL ?
19
+ url.href :
20
+ String(url);
21
+ // Intercept bare filesystem paths (absolute POSIX or Windows)
22
+ if (urlStr.startsWith("/") || /^[A-Za-z]:[/\\]/.test(urlStr)) {
23
+ const buffer = await readFile(urlStr);
24
+ return new Response(buffer, {
25
+ status: 200,
26
+ headers: { "Content-Type": "application/wasm" },
27
+ });
28
+ }
29
+ return originalFetch(url, options);
30
+ };
31
+ }
@@ -8,9 +8,9 @@
8
8
 
9
9
  const assert = require("assert");
10
10
 
11
- require("cyberchef").then(chef => {
11
+ require("cyberchef").then(async chef => {
12
12
 
13
- const d = chef.bake("Testing, 1 2 3", [
13
+ const d = await chef.bake("Testing, 1 2 3", [
14
14
  chef.toHex,
15
15
  chef.reverse,
16
16
  {
@@ -9,7 +9,7 @@ import assert from "assert";
9
9
  import chef from "cyberchef";
10
10
  import { bake, toHex, reverse, unique, multiply } from "cyberchef";
11
11
 
12
- const a = bake("Testing, 1 2 3", [
12
+ const a = await bake("Testing, 1 2 3", [
13
13
  toHex,
14
14
  reverse,
15
15
  {
@@ -28,7 +28,7 @@ const a = bake("Testing, 1 2 3", [
28
28
 
29
29
  assert.equal(a.value, "630957449041920");
30
30
 
31
- const b = chef.bake("Testing, 1 2 3", [
31
+ const b = await chef.bake("Testing, 1 2 3", [
32
32
  chef.toHex,
33
33
  chef.reverse,
34
34
  {
@@ -18,6 +18,7 @@ import {
18
18
  import TestRegister from "../lib/TestRegister.mjs";
19
19
  import "./tests/nodeApi.mjs";
20
20
  import "./tests/operations.mjs";
21
+ import "./tests/PGP.mjs";
21
22
  import "./tests/File.mjs";
22
23
  import "./tests/Dish.mjs";
23
24
  import "./tests/NodeDish.mjs";
@@ -1,6 +1,6 @@
1
1
  import TestRegister from "../../lib/TestRegister.mjs";
2
- import Categories from "../../../src/core/config/Categories.json" assert {type: "json"};
3
- import OperationConfig from "../../../src/core/config/OperationConfig.json" assert {type: "json"};
2
+ import Categories from "../../../src/core/config/Categories.json" with { type: "json" };
3
+ import OperationConfig from "../../../src/core/config/OperationConfig.json" with { type: "json" };
4
4
  import it from "../assertionHandler.mjs";
5
5
  import assert from "assert";
6
6
 
@@ -0,0 +1,69 @@
1
+ /**
2
+ * PGP node tests.
3
+ *
4
+ * @author C85297 [95289555+C85297@users.noreply.github.com]
5
+ * @copyright Crown Copyright 2026
6
+ * @license Apache-2.0
7
+ */
8
+
9
+ import assert from "assert";
10
+ import kbpgp from "kbpgp";
11
+ import * as es6promisify from "es6-promisify";
12
+
13
+ import TestRegister from "../../lib/TestRegister.mjs";
14
+ import it from "../assertionHandler.mjs";
15
+ import GeneratePGPKeyPair from "../../../src/core/operations/GeneratePGPKeyPair.mjs";
16
+
17
+ const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
18
+
19
+ const PUBLIC_KEY_BLOCK = /-----BEGIN PGP PUBLIC KEY BLOCK-----[\s\S]*-----END PGP PUBLIC KEY BLOCK-----/;
20
+
21
+ /**
22
+ * Generate a PGP key pair and import the generated public key.
23
+ *
24
+ * @param {string} keyType
25
+ * @returns {Promise<Object>}
26
+ */
27
+ async function generateAndImportPublicKey(keyType) {
28
+ const operation = new GeneratePGPKeyPair();
29
+ const generatedKeyPair = await operation.run("", [
30
+ keyType,
31
+ "",
32
+ "User",
33
+ "akb@notreal.gchq.gov.uk"
34
+ ]);
35
+
36
+ const publicKey = generatedKeyPair.match(PUBLIC_KEY_BLOCK);
37
+
38
+ assert(publicKey, "Generated key pair should contain an ASCII-armoured public key");
39
+
40
+ return promisify(kbpgp.KeyManager.import_from_armored_pgp)({
41
+ armored: publicKey[0],
42
+ opts: {
43
+ "no_check_keys": true
44
+ }
45
+ });
46
+ }
47
+
48
+ TestRegister.addApiTests([
49
+ it("Generate PGP Key Pair: ECC keys should include ECDSA signing and ECDH encryption subkeys", async () => {
50
+ const publicKey = await generateAndImportPublicKey("ECC-256");
51
+ const subkeyAlgorithms = publicKey.subkeys.map(subkey => subkey.key.type);
52
+
53
+ assert.strictEqual(
54
+ publicKey.primary.key.type,
55
+ kbpgp.const.openpgp.public_key_algorithms.ECDSA,
56
+ "Generated ECC PGP primary key should use ECDSA"
57
+ );
58
+
59
+ assert(
60
+ subkeyAlgorithms.includes(kbpgp.const.openpgp.public_key_algorithms.ECDSA),
61
+ "Generated ECC PGP key should include an ECDSA signing subkey"
62
+ );
63
+
64
+ assert(
65
+ subkeyAlgorithms.includes(kbpgp.const.openpgp.public_key_algorithms.ECDH),
66
+ "Generated ECC PGP key should include an ECDH encryption subkey"
67
+ );
68
+ })
69
+ ]);
@@ -170,77 +170,77 @@ TestRegister.addApiTests([
170
170
  assert(chef.bake);
171
171
  }),
172
172
 
173
- it("chef.bake: should return NodeDish", () => {
174
- const result = chef.bake("input", "to base 64");
173
+ it("chef.bake: should return NodeDish", async () => {
174
+ const result = await chef.bake("input", "to base 64");
175
175
  assert(result instanceof NodeDish);
176
176
  }),
177
177
 
178
- it("chef.bake: should take an input and an op name and perform it", () => {
179
- const result = chef.bake("some input", "to base 32");
178
+ it("chef.bake: should take an input and an op name and perform it", async () => {
179
+ const result = await chef.bake("some input", "to base 32");
180
180
  assert.strictEqual(result.toString(), "ONXW2ZJANFXHA5LU");
181
181
  }),
182
182
 
183
- it("chef.bake: should complain if recipe isnt a valid object", () => {
184
- assert.throws(() => chef.bake("some input", 3264), {
183
+ it("chef.bake: should complain if recipe isnt a valid object", async () => {
184
+ await assert.rejects(() => chef.bake("some input", 3264), {
185
185
  name: "TypeError",
186
186
  message: "Recipe can only contain function names or functions"
187
187
  });
188
188
  }),
189
189
 
190
- it("chef.bake: Should complain if string op is invalid", () => {
191
- assert.throws(() => chef.bake("some input", "not a valid operation"), {
190
+ it("chef.bake: Should complain if string op is invalid", async () => {
191
+ await assert.rejects(() => chef.bake("some input", "not a valid operation"), {
192
192
  name: "TypeError",
193
193
  message: "Couldn't find an operation with name 'not a valid operation'."
194
194
  });
195
195
  }),
196
196
 
197
- it("chef.bake: Should take an input and an operation and perform it", () => {
198
- const result = chef.bake("https://google.com/search?q=help", chef.parseURI);
197
+ it("chef.bake: Should take an input and an operation and perform it", async () => {
198
+ const result = await chef.bake("https://google.com/search?q=help", chef.parseURI);
199
199
  assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = help\n");
200
200
  }),
201
201
 
202
- it("chef.bake: Should complain if an invalid operation is inputted", () => {
203
- assert.throws(() => chef.bake("https://google.com/search?q=help", () => {}), {
202
+ it("chef.bake: Should complain if an invalid operation is inputted", async () => {
203
+ await assert.rejects(() => chef.bake("https://google.com/search?q=help", () => {}), {
204
204
  name: "TypeError",
205
205
  message: "Inputted function not a Chef operation."
206
206
  });
207
207
  }),
208
208
 
209
- it("chef.bake: accepts an array of operation names and performs them all in order", () => {
210
- const result = chef.bake("https://google.com/search?q=that's a complicated question", ["URL encode", "URL decode", "Parse URI"]);
209
+ it("chef.bake: accepts an array of operation names and performs them all in order", async () => {
210
+ const result = await chef.bake("https://google.com/search?q=that's a complicated question", ["URL encode", "URL decode", "Parse URI"]);
211
211
  assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n");
212
212
  }),
213
213
 
214
- it("chef.bake: forgiving with operation names", () =>{
215
- const result = chef.bake("https://google.com/search?q=that's a complicated question", ["urlencode", "url decode", "parseURI"]);
214
+ it("chef.bake: forgiving with operation names", async () =>{
215
+ const result = await chef.bake("https://google.com/search?q=that's a complicated question", ["urlencode", "url decode", "parseURI"]);
216
216
  assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n");
217
217
  }),
218
218
 
219
- it("chef.bake: forgiving with operation names", () =>{
220
- const result = chef.bake("hello", ["to base 64"]);
219
+ it("chef.bake: forgiving with operation names", async () =>{
220
+ const result = await chef.bake("hello", ["to base 64"]);
221
221
  assert.strictEqual(result.toString(), "aGVsbG8=");
222
222
  }),
223
223
 
224
- it("chef.bake: if recipe is empty array, return input as dish", () => {
225
- const result = chef.bake("some input", []);
224
+ it("chef.bake: if recipe is empty array, return input as dish", async () => {
225
+ const result = await chef.bake("some input", []);
226
226
  assert.strictEqual(result.toString(), "some input");
227
227
  assert(result instanceof NodeDish, "Result is not instance of NodeDish");
228
228
  }),
229
229
 
230
- it("chef.bake: accepts an array of operations as recipe", () => {
231
- const result = chef.bake("https://google.com/search?q=that's a complicated question", [chef.URLEncode, chef.URLDecode, chef.parseURI]);
230
+ it("chef.bake: accepts an array of operations as recipe", async () => {
231
+ const result = await chef.bake("https://google.com/search?q=that's a complicated question", [chef.URLEncode, chef.URLDecode, chef.parseURI]);
232
232
  assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n");
233
233
  }),
234
234
 
235
- it("should complain if an invalid operation is inputted as part of array", () => {
236
- assert.throws(() => chef.bake("something", [() => {}]), {
235
+ it("should complain if an invalid operation is inputted as part of array", async () => {
236
+ await assert.rejects(() => chef.bake("something", [() => {}]), {
237
237
  name: "TypeError",
238
238
  message: "Inputted function not a Chef operation."
239
239
  });
240
240
  }),
241
241
 
242
- it("chef.bake: should take single JSON object describing op and args OBJ", () => {
243
- const result = chef.bake("some input", {
242
+ it("chef.bake: should take single JSON object describing op and args OBJ", async () => {
243
+ const result = await chef.bake("some input", {
244
244
  op: chef.toHex,
245
245
  args: {
246
246
  Delimiter: "Colon"
@@ -249,23 +249,23 @@ TestRegister.addApiTests([
249
249
  assert.strictEqual(result.toString(), "73:6f:6d:65:20:69:6e:70:75:74");
250
250
  }),
251
251
 
252
- it("chef.bake: should take single JSON object desribing op with optional args", () => {
253
- const result = chef.bake("some input", {
252
+ it("chef.bake: should take single JSON object desribing op with optional args", async () => {
253
+ const result = await chef.bake("some input", {
254
254
  op: chef.toHex,
255
255
  });
256
256
  assert.strictEqual(result.toString(), "73 6f 6d 65 20 69 6e 70 75 74");
257
257
  }),
258
258
 
259
- it("chef.bake: should take single JSON object describing op and args ARRAY", () => {
260
- const result = chef.bake("some input", {
259
+ it("chef.bake: should take single JSON object describing op and args ARRAY", async () => {
260
+ const result = await chef.bake("some input", {
261
261
  op: chef.toHex,
262
262
  args: ["Colon"]
263
263
  });
264
264
  assert.strictEqual(result.toString(), "73:6f:6d:65:20:69:6e:70:75:74");
265
265
  }),
266
266
 
267
- it("chef.bake: should error if op in JSON is not chef op", () => {
268
- assert.throws(() => chef.bake("some input", {
267
+ it("chef.bake: should error if op in JSON is not chef op", async () => {
268
+ await assert.rejects(() => chef.bake("some input", {
269
269
  op: () => {},
270
270
  args: ["Colon"],
271
271
  }), {
@@ -274,8 +274,8 @@ TestRegister.addApiTests([
274
274
  });
275
275
  }),
276
276
 
277
- it("chef.bake: should take multiple ops in JSON object form, some ops by string", () => {
278
- const result = chef.bake("some input", [
277
+ it("chef.bake: should take multiple ops in JSON object form, some ops by string", async () => {
278
+ const result = await chef.bake("some input", [
279
279
  {
280
280
  op: chef.toHex,
281
281
  args: ["Colon"]
@@ -290,8 +290,8 @@ TestRegister.addApiTests([
290
290
  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");
291
291
  }),
292
292
 
293
- it("chef.bake: should take multiple ops in JSON object form, some without args", () => {
294
- const result = chef.bake("some input", [
293
+ it("chef.bake: should take multiple ops in JSON object form, some without args", async () => {
294
+ const result = await chef.bake("some input", [
295
295
  {
296
296
  op: chef.toHex,
297
297
  },
@@ -305,8 +305,8 @@ TestRegister.addApiTests([
305
305
  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");
306
306
  }),
307
307
 
308
- it("chef.bake: should handle op with multiple args", () => {
309
- const result = chef.bake("some input", {
308
+ it("chef.bake: should handle op with multiple args", async () => {
309
+ const result = await chef.bake("some input", {
310
310
  op: "to morse code",
311
311
  args: {
312
312
  formatOptions: "Dash/Dot",
@@ -317,13 +317,13 @@ TestRegister.addApiTests([
317
317
  assert.strictEqual(result.toString(), "DotDotDot\\DashDashDash\\DashDash\\Dot,DotDot\\DashDot\\DotDashDashDot\\DotDotDash\\Dash");
318
318
  }),
319
319
 
320
- it("chef.bake: should take compact JSON format from Chef Website as recipe", () => {
321
- 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]}]);
320
+ it("chef.bake: should take compact JSON format from Chef Website as recipe", async () => {
321
+ 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]}]);
322
322
  assert.strictEqual(result.toString(), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something");
323
323
  }),
324
324
 
325
- it("chef.bake: should accept Clean JSON format from Chef website as recipe", () => {
326
- const result = chef.bake("some input", [
325
+ it("chef.bake: should accept Clean JSON format from Chef website as recipe", async () => {
326
+ const result = await chef.bake("some input", [
327
327
  { "op": "To Morse Code",
328
328
  "args": ["Dash/Dot", "Backslash", "Comma"] },
329
329
  { "op": "Hex to PEM",
@@ -334,8 +334,8 @@ TestRegister.addApiTests([
334
334
  assert.strictEqual(result.toString(), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something");
335
335
  }),
336
336
 
337
- it("chef.bake: should accept Clean JSON format from Chef website - args optional", () => {
338
- const result = chef.bake("some input", [
337
+ it("chef.bake: should accept Clean JSON format from Chef website - args optional", async () => {
338
+ const result = await chef.bake("some input", [
339
339
  { "op": "To Morse Code" },
340
340
  { "op": "Hex to PEM",
341
341
  "args": ["SOMETHING"] },
@@ -345,31 +345,28 @@ TestRegister.addApiTests([
345
345
  assert.strictEqual(result.toString(), "begin_something_aaaaaaaaaaaaaa_end_something");
346
346
  }),
347
347
 
348
- it("chef.bake: should accept operation names from Chef Website which contain forward slash", () => {
349
- const result = chef.bake("I'll have the test salmon", [
348
+ it("chef.bake: should accept operation names from Chef Website which contain forward slash", async () => {
349
+ const result = await chef.bake("I'll have the test salmon", [
350
350
  { "op": "Find / Replace",
351
351
  "args": [{ "option": "Regex", "string": "test" }, "good", true, false, true, false]}
352
352
  ]);
353
353
  assert.strictEqual(result.toString(), "I'll have the good salmon");
354
354
  }),
355
355
 
356
- it("chef.bake: should accept operation names from Chef Website which contain a hyphen", () => {
357
- const result = chef.bake("I'll have the test salmon", [
356
+ it("chef.bake: should accept operation names from Chef Website which contain a hyphen", async () => {
357
+ const result = await chef.bake("I'll have the test salmon", [
358
358
  { "op": "Adler-32 Checksum",
359
359
  "args": [] }
360
360
  ]);
361
361
  assert.strictEqual(result.toString(), "6e4208f8");
362
362
  }),
363
363
 
364
- it("chef.bake: should accept operation names from Chef Website which contain a period", () => {
365
- 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", [
364
+ it("chef.bake: should accept operation names from Chef Website which contain a period", async () => {
365
+ 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", [
366
366
  { "op": "Parse ASN.1 hex string",
367
367
  "args": [0, 32] }
368
368
  ]);
369
- assert.strictEqual(result.toString(), `SEQUENCE
370
- INTEGER 05
371
- IA5String 'Anybody there?'
372
- `);
369
+ assert.strictEqual(result.toString(), `SEQUENCE\n INTEGER 05\n IA5String 'Anybody there?'\n`);
373
370
  }),
374
371
 
375
372
  it("Excluded operations: throw a sensible error when you try and call one", () => {
@@ -381,16 +378,16 @@ TestRegister.addApiTests([
381
378
  }
382
379
  }),
383
380
 
384
- it("chef.bake: cannot accept flowControl operations in recipe", () => {
385
- assert.throws(() => chef.bake("some input", "magic"), {
381
+ it("chef.bake: cannot accept flowControl operations in recipe", async () => {
382
+ await assert.rejects(() => chef.bake("some input", "magic"), {
386
383
  name: "TypeError",
387
384
  message: "flowControl operations like Magic are not currently allowed in recipes for chef.bake in the Node API"
388
385
  });
389
- assert.throws(() => chef.bake("some input", magic), {
386
+ await assert.rejects(() => chef.bake("some input", magic), {
390
387
  name: "TypeError",
391
388
  message: "flowControl operations like Magic are not currently allowed in recipes for chef.bake in the Node API"
392
389
  });
393
- assert.throws(() => chef.bake("some input", ["to base 64", "magic"]), {
390
+ await assert.rejects(() => chef.bake("some input", ["to base 64", "magic"]), {
394
391
  name: "TypeError",
395
392
  message: "flowControl operations like Magic are not currently allowed in recipes for chef.bake in the Node API"
396
393
  });
@@ -79,7 +79,46 @@ TestRegister.addApiTests([
79
79
  string: "some iv some iv1",
80
80
  option: "utf8",
81
81
  },
82
+ ivLength: 16,
82
83
  mode: "OFB",
84
+ inputType: "Hex",
85
+ outputType: "Raw",
86
+ gcmTag: {
87
+ option: "Hex",
88
+ string: ""
89
+ },
90
+ aad: {
91
+ option: "Hex",
92
+ string: ""
93
+ },
94
+ ivFromInput: "Off"
95
+ });
96
+ assert.equal(result.toString(), "a slightly longer sampleinput?");
97
+ }),
98
+
99
+ it("AES decrypt: IV from input", () => {
100
+ const result = AESDecrypt("4a123af235a507bbc9d5871721d61b98504d569a9a5a7847e2d78315fec7736f6d6520697620736f6d6520697631", {
101
+ key: {
102
+ string: "some longer key1",
103
+ option: "utf8",
104
+ },
105
+ iv: {
106
+ string: "",
107
+ option: "Hex",
108
+ },
109
+ ivLength: 16,
110
+ mode: "OFB",
111
+ inputType: "Hex",
112
+ outputType: "Raw",
113
+ gcmTag: {
114
+ option: "Hex",
115
+ string: ""
116
+ },
117
+ aad: {
118
+ option: "Hex",
119
+ string: ""
120
+ },
121
+ ivFromInput: "From end"
83
122
  });
84
123
  assert.equal(result.toString(), "a slightly longer sampleinput?");
85
124
  }),
@@ -136,8 +175,8 @@ Tiger-128`;
136
175
  it("Bcrypt", async () => {
137
176
  const result = await chef.bcrypt("Put a Sock In It");
138
177
  const strResult = result.toString();
139
- assert.equal(strResult.length, 60);
140
- assert.equal(strResult.slice(0, 7), "$2a$10$");
178
+ assert.match(strResult, /^\$2b\$10\$[./A-Za-z0-9]{53}$/);
179
+ assert.equal(strResult.split("$").length, 4);
141
180
  }),
142
181
 
143
182
  it("bcryptCompare", async() => {