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
@@ -8,6 +8,7 @@ import Operation from "../Operation.mjs";
8
8
  import Utils from "../Utils.mjs";
9
9
  import forge from "node-forge";
10
10
  import OperationError from "../errors/OperationError.mjs";
11
+ import { toHexFast } from "../lib/Hex.mjs";
11
12
 
12
13
  /**
13
14
  * AES Encrypt operation
@@ -92,6 +93,11 @@ class AESEncrypt extends Operation {
92
93
  "type": "toggleString",
93
94
  "value": "",
94
95
  "toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
96
+ },
97
+ {
98
+ "name": "Include IV in output",
99
+ "type": "option",
100
+ "value": ["Off", "Prepend", "Append"]
95
101
  }
96
102
  ];
97
103
  }
@@ -107,10 +113,11 @@ class AESEncrypt extends Operation {
107
113
  const key = Utils.convertToByteString(args[0].string, args[0].option),
108
114
  iv = Utils.convertToByteString(args[1].string, args[1].option),
109
115
  mode = args[2].split("/")[0],
110
- noPadding = args[2].endsWith("NoPadding"),
116
+ noPadding = args[2].endsWith("NoPadding"),
111
117
  inputType = args[3],
112
118
  outputType = args[4],
113
- aad = Utils.convertToByteString(args[5].string, args[5].option);
119
+ aad = Utils.convertToByteString(args[5].string, args[5].option),
120
+ includeIV = args[6];
114
121
 
115
122
  if ([16, 24, 32].indexOf(key.length) < 0) {
116
123
  throw new OperationError(`Invalid key length: ${key.length} bytes
@@ -133,26 +140,34 @@ The following algorithms will be used based on the size of the key:
133
140
  additionalData: mode === "GCM" ? aad : undefined
134
141
  });
135
142
  if (noPadding) {
136
- cipher.mode.pad = function(output, options) {
143
+ cipher.mode.pad = function (output, options) {
137
144
  return true;
138
145
  };
139
146
  }
140
147
  cipher.update(forge.util.createBuffer(input));
141
148
  cipher.finish();
142
149
 
150
+ let output = cipher.output.getBytes();
151
+
152
+ if (includeIV === "Prepend") {
153
+ output = iv + output;
154
+ } else if (includeIV === "Append") {
155
+ output = output + iv;
156
+ }
157
+
143
158
  if (outputType === "Hex") {
159
+ output = toHexFast(Utils.strToByteArray(output));
160
+
144
161
  if (mode === "GCM") {
145
- return cipher.output.toHex() + "\n\n" +
162
+ return output + "\n\n" +
146
163
  "Tag: " + cipher.mode.tag.toHex();
147
164
  }
148
- return cipher.output.toHex();
149
- } else {
150
- if (mode === "GCM") {
151
- return cipher.output.getBytes() + "\n\n" +
152
- "Tag: " + cipher.mode.tag.getBytes();
153
- }
154
- return cipher.output.getBytes();
165
+ } else if (mode === "GCM") {
166
+ return output + "\n\n" +
167
+ "Tag: " + cipher.mode.tag.getBytes();
155
168
  }
169
+
170
+ return output;
156
171
  }
157
172
 
158
173
  }
@@ -6,7 +6,10 @@
6
6
 
7
7
  import Operation from "../Operation.mjs";
8
8
  import OperationError from "../errors/OperationError.mjs";
9
- import { blake3 } from "hash-wasm";
9
+ import Utils from "../Utils.mjs";
10
+ import { blake3 } from "@noble/hashes/blake3.js";
11
+ import { bytesToHex } from "@noble/hashes/utils.js";
12
+
10
13
  /**
11
14
  * BLAKE3 operation
12
15
  */
@@ -44,13 +47,16 @@ class BLAKE3 extends Operation {
44
47
  run(input, args) {
45
48
  const key = args[1];
46
49
  const size = args[0];
47
- // Check if the user want a key hash or not
48
- if (key === "") {
49
- return blake3(input, size*8);
50
- } if (key.length !== 32) {
51
- throw new OperationError("The key must be exactly 32 bytes long");
50
+ const opts = { dkLen: size };
51
+ const inputBytes = new Uint8Array(Utils.strToArrayBuffer(input));
52
+ if (key !== "") {
53
+ const keyBytes = new Uint8Array(Utils.strToArrayBuffer(key));
54
+ if (keyBytes.length !== 32) {
55
+ throw new OperationError("The key must be exactly 32 bytes long");
56
+ }
57
+ opts.key = keyBytes;
52
58
  }
53
- return blake3(input, size*8, key);
59
+ return bytesToHex(blake3(inputBytes, opts));
54
60
  }
55
61
 
56
62
  }
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import Operation from "../Operation.mjs";
8
- import bson from "bson";
8
+ import { deserialize } from "bson";
9
9
  import OperationError from "../errors/OperationError.mjs";
10
10
 
11
11
  /**
@@ -37,7 +37,7 @@ class BSONDeserialise extends Operation {
37
37
  if (!input.byteLength) return "";
38
38
 
39
39
  try {
40
- const data = bson.deserialize(new Buffer(input));
40
+ const data = deserialize(new Uint8Array(input));
41
41
  return JSON.stringify(data, null, 2);
42
42
  } catch (err) {
43
43
  throw new OperationError(err.toString());
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import Operation from "../Operation.mjs";
8
- import bson from "bson";
8
+ import { serialize } from "bson";
9
9
  import OperationError from "../errors/OperationError.mjs";
10
10
 
11
11
  /**
@@ -38,7 +38,8 @@ class BSONSerialise extends Operation {
38
38
 
39
39
  try {
40
40
  const data = JSON.parse(input);
41
- return bson.serialize(data).buffer;
41
+ const result = serialize(data);
42
+ return result.buffer.slice(result.byteOffset, result.byteOffset + result.byteLength);
42
43
  } catch (err) {
43
44
  throw new OperationError(err.toString());
44
45
  }
@@ -43,7 +43,7 @@ class Bcrypt extends Operation {
43
43
  const rounds = args[0];
44
44
  const salt = await bcrypt.genSalt(rounds);
45
45
 
46
- return await bcrypt.hash(input, salt, null, p => {
46
+ return await bcrypt.hash(input, salt, undefined, p => {
47
47
  // Progress callback
48
48
  if (isWorkerEnvironment())
49
49
  self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
@@ -43,7 +43,7 @@ class BcryptCompare extends Operation {
43
43
  async run(input, args) {
44
44
  const hash = args[0];
45
45
 
46
- const match = await bcrypt.compare(input, hash, null, p => {
46
+ const match = await bcrypt.compare(input, hash, undefined, p => {
47
47
  // Progress callback
48
48
  if (isWorkerEnvironment())
49
49
  self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import Operation from "../Operation.mjs";
8
+ import OperationError from "../errors/OperationError.mjs";
8
9
  import cptable from "codepage";
9
10
  import {CHR_ENC_CODE_PAGES} from "../lib/ChrEnc.mjs";
10
11
 
@@ -48,6 +49,9 @@ class DecodeText extends Operation {
48
49
  */
49
50
  run(input, args) {
50
51
  const format = CHR_ENC_CODE_PAGES[args[0]];
52
+ if (!format) {
53
+ throw new OperationError("Invalid encoding");
54
+ }
51
55
  return cptable.utils.decode(format, new Uint8Array(input));
52
56
  }
53
57
 
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import Operation from "../Operation.mjs";
8
+ import OperationError from "../errors/OperationError.mjs";
8
9
  import cptable from "codepage";
9
10
  import {CHR_ENC_CODE_PAGES} from "../lib/ChrEnc.mjs";
10
11
 
@@ -48,6 +49,9 @@ class EncodeText extends Operation {
48
49
  */
49
50
  run(input, args) {
50
51
  const format = CHR_ENC_CODE_PAGES[args[0]];
52
+ if (!format) {
53
+ throw new OperationError("Invalid encoding");
54
+ }
51
55
  const encoded = cptable.utils.encode(format, input);
52
56
  return new Uint8Array(encoded).buffer;
53
57
  }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * @author HarelKatz [github.com/HarelKatz]
3
+ * @copyright Crown Copyright 2026
4
+ * @license Apache-2.0
5
+ */
6
+
7
+ import Operation from "../Operation.mjs";
8
+
9
+ /**
10
+ * Escape Smart Characters operation
11
+ */
12
+ class EscapeSmartCharacters extends Operation {
13
+
14
+ /**
15
+ * EscapeSmartCharacters constructor
16
+ */
17
+ constructor() {
18
+ super();
19
+
20
+ this.name = "Escape Smart Characters";
21
+ this.module = "Default";
22
+ this.description = "Converts smart (typographic) Unicode characters — e.g. smart quotes, em/en dashes, ellipses, ©, ®, ™, arrows — into their plain ASCII equivalents.<br><br>Characters with no ASCII mapping (e.g. <code>☣</code>) are handled according to the 'Unmappable characters' option.<br><br>e.g. <code>“Hello” — world…</code> becomes <code>\"Hello\" -- world...</code>";
23
+ this.infoURL = "";
24
+ this.inputType = "string";
25
+ this.outputType = "string";
26
+ this.args = [
27
+ {
28
+ name: "Unmappable characters",
29
+ type: "option",
30
+ value: ["Include", "Remove", "Replace with '.'"]
31
+ }
32
+ ];
33
+ }
34
+
35
+ /**
36
+ * @param {string} input
37
+ * @param {Object[]} args
38
+ * @returns {string}
39
+ */
40
+ run(input, args) {
41
+ const [unmappable] = args;
42
+ let result = "";
43
+ for (const ch of input) {
44
+ if (ch.codePointAt(0) < 128) {
45
+ result += ch;
46
+ } else if (Object.prototype.hasOwnProperty.call(SMART_MAP, ch)) {
47
+ result += SMART_MAP[ch];
48
+ } else {
49
+ switch (unmappable) {
50
+ case "Remove":
51
+ break;
52
+ case "Replace with '.'":
53
+ result += ".";
54
+ break;
55
+ case "Include":
56
+ default:
57
+ result += ch;
58
+ break;
59
+ }
60
+ }
61
+ }
62
+ return result;
63
+ }
64
+
65
+ }
66
+
67
+ const SMART_MAP = {
68
+ // Smart double quotes
69
+ "“": "\"", // “ left double quotation mark
70
+ "”": "\"", // ” right double quotation mark
71
+ "„": "\"", // „ double low-9 quotation mark
72
+ "‟": "\"", // ‟ double high-reversed-9 quotation mark
73
+ "″": "\"", // ″ double prime
74
+
75
+ // Smart single quotes / apostrophes
76
+ "‘": "'", // ‘ left single quotation mark
77
+ "’": "'", // ’ right single quotation mark / apostrophe
78
+ "‚": "'", // ‚ single low-9 quotation mark
79
+ "‛": "'", // ‛ single high-reversed-9 quotation mark
80
+ "′": "'", // ′ prime
81
+
82
+ // Dashes & hyphens
83
+ "‐": "-", // ‐ hyphen
84
+ "‑": "-", // ‑ non-breaking hyphen
85
+ "‒": "-", // ‒ figure dash
86
+ "–": "-", // – en dash
87
+ "—": "--", // — em dash
88
+ "―": "--", // ― horizontal bar
89
+
90
+ // Ellipsis
91
+ "…": "...", // …
92
+
93
+ // Trademark / copyright symbols
94
+ "©": "(c)", // ©
95
+ "®": "(r)", // ®
96
+ "™": "(tm)", // ™
97
+
98
+ // Arrows
99
+ "←": "<--", // ←
100
+ "→": "-->", // →
101
+ "↑": "^", // ↑
102
+ "↓": "v", // ↓
103
+ "↔": "<->", // ↔
104
+ "⇐": "<==", // ⇐
105
+ "⇒": "==>", // ⇒
106
+ "⇔": "<=>", // ⇔
107
+
108
+ // Guillemets
109
+ "«": "<<", // «
110
+ "»": ">>", // »
111
+ "‹": "<", // ‹
112
+ "›": ">", // ›
113
+
114
+ // Math & misc symbols
115
+ "×": "x", // ×
116
+ "÷": "/", // ÷
117
+ "±": "+/-", // ±
118
+ "•": "*", // •
119
+ "·": ".", // ·
120
+
121
+ // Non-ASCII spaces
122
+ "\u00A0": " ", // NBSP
123
+ "\u2002": " ", // en space
124
+ "\u2003": " ", // em space
125
+ "\u2009": " ", // thin space
126
+ "\u200A": " " // hair space
127
+ };
128
+
129
+ export default EscapeSmartCharacters;
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import Operation from "../Operation.mjs";
8
- import punycode from "punycode";
8
+ import punycode from "punycode.js";
9
9
 
10
10
  /**
11
11
  * From Punycode operation
@@ -8,6 +8,10 @@ import Operation from "../Operation.mjs";
8
8
  import OperationError from "../errors/OperationError.mjs";
9
9
  import { GenerateParagraphs, GenerateSentences, GenerateWords, GenerateBytes } from "../lib/LoremIpsum.mjs";
10
10
 
11
+ // arbitrary limits set to avoid DoS by requesting ridiculous amounts of data
12
+ const maxLoremWords = 100_000; // same limit also used for paragraphs/sentences
13
+ const maxLoremCharacters = 1_000_000;
14
+
11
15
  /**
12
16
  * Generate Lorem Ipsum operation
13
17
  */
@@ -47,9 +51,7 @@ class GenerateLoremIpsum extends Operation {
47
51
  */
48
52
  run(input, args) {
49
53
  const [length, lengthType] = args;
50
- if (length < 1) {
51
- throw new OperationError("Length must be greater than 0");
52
- }
54
+ checkLimits(lengthType, length);
53
55
  switch (lengthType) {
54
56
  case "Paragraphs":
55
57
  return GenerateParagraphs(length);
@@ -68,3 +70,32 @@ class GenerateLoremIpsum extends Operation {
68
70
  }
69
71
 
70
72
  export default GenerateLoremIpsum;
73
+
74
+ /**
75
+ * check combined validity of lengthType and length arguments
76
+ * @param {string} lengthType
77
+ * @param {number} length
78
+ * @throws {OperationError}
79
+ */
80
+ function checkLimits(lengthType, length) {
81
+ if (length < 1) {
82
+ throw new OperationError("Length must be greater than 0");
83
+ }
84
+
85
+ switch (lengthType) {
86
+ case "Paragraphs":
87
+ case "Sentences":
88
+ case "Words":
89
+ if (length > maxLoremWords) {
90
+ throw new OperationError("Length must be less than " + maxLoremWords);
91
+ }
92
+ break;
93
+ case "Bytes":
94
+ if (length > maxLoremCharacters) {
95
+ throw new OperationError("Length must be less than " + maxLoremCharacters);
96
+ }
97
+ break;
98
+ default:
99
+ throw new OperationError("Invalid length type");
100
+ }
101
+ }
@@ -12,6 +12,7 @@ import { getSubkeySize, ASP } from "../lib/PGP.mjs";
12
12
  import { cryptNotice } from "../lib/Crypt.mjs";
13
13
  import * as es6promisify from "es6-promisify";
14
14
  const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
15
+ const KEY_FLAGS = kbpgp.const.openpgp.key_flags;
15
16
 
16
17
 
17
18
  /**
@@ -73,11 +74,11 @@ class GeneratePGPKeyPair extends Operation {
73
74
  if (name) userIdentifier += name;
74
75
  if (email) userIdentifier += ` <${email}>`;
75
76
 
76
- let flags = kbpgp.const.openpgp.certify_keys;
77
- flags |= kbpgp.const.openpgp.sign_data;
78
- flags |= kbpgp.const.openpgp.auth;
79
- flags |= kbpgp.const.openpgp.encrypt_comm;
80
- flags |= kbpgp.const.openpgp.encrypt_storage;
77
+ let flags = KEY_FLAGS.certify_keys;
78
+ flags |= KEY_FLAGS.sign_data;
79
+ flags |= KEY_FLAGS.auth;
80
+ flags |= KEY_FLAGS.encrypt_comm;
81
+ flags |= KEY_FLAGS.encrypt_storage;
81
82
 
82
83
  const keyGenerationOptions = {
83
84
  userid: userIdentifier,
@@ -89,11 +90,11 @@ class GeneratePGPKeyPair extends Operation {
89
90
  },
90
91
  subkeys: [{
91
92
  "nbits": getSubkeySize(keySize),
92
- "flags": kbpgp.const.openpgp.sign_data,
93
+ "flags": KEY_FLAGS.sign_data,
93
94
  "expire_in": 86400 * 365 * 8
94
95
  }, {
95
96
  "nbits": getSubkeySize(keySize),
96
- "flags": kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage,
97
+ "flags": KEY_FLAGS.encrypt_comm | KEY_FLAGS.encrypt_storage,
97
98
  "expire_in": 86400 * 365 * 2
98
99
  }],
99
100
  asp: ASP
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @author GCHQDeveloper581
3
+ * @copyright Crown Copyright 2026
4
+ * @license Apache-2.0
5
+ */
6
+
7
+ import Operation from "../Operation.mjs";
8
+ import kbpgp from "kbpgp";
9
+ import { ASP, importPrivateKey } from "../lib/PGP.mjs";
10
+ import OperationError from "../errors/OperationError.mjs";
11
+ import * as es6promisify from "es6-promisify";
12
+ const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
13
+
14
+ /**
15
+ * PGP Sign operation
16
+ */
17
+ class PGPSign extends Operation {
18
+
19
+ /**
20
+ * PGPSign constructor
21
+ */
22
+ constructor() {
23
+ super();
24
+
25
+ this.name = "PGP Sign";
26
+ this.module = "PGP";
27
+ this.description = [
28
+ "Input: the message you want to sign",
29
+ "<br><br>",
30
+ "Arguments: the ASCII-armoured PGP private key of the sender.",
31
+ "<br><br>",
32
+ "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
33
+ "<br><br>",
34
+ "This function uses the Keybase implementation of PGP.",
35
+ ].join("\n");
36
+ this.infoURL = "https://wikipedia.org/wiki/Pretty_Good_Privacy"; // Usually a Wikipedia link. Remember to remove localisation (i.e. https://wikipedia.org/etc rather than https://en.wikipedia.org/etc)
37
+ this.inputType = "string";
38
+ this.outputType = "string";
39
+ this.args = [
40
+ {
41
+ "name": "Private key of signer",
42
+ "type": "text",
43
+ "value": ""
44
+ },
45
+ {
46
+ "name": "Private key passphrase (optional)",
47
+ "type": "string",
48
+ "value": ""
49
+ }
50
+ ];
51
+ }
52
+
53
+ /**
54
+ * @param {string} input
55
+ * @param {Object[]} args
56
+ * @returns {string}
57
+ *
58
+ * @throws {OperationError} if failed private key import or failed encryption
59
+ */
60
+ async run(input, args) {
61
+ const message = input,
62
+ [privateKey, passphrase] = args;
63
+ let signedMessage;
64
+
65
+ if (!privateKey) throw new OperationError("Enter the private key of the signer.");
66
+ const privKey = await importPrivateKey(privateKey, passphrase);
67
+
68
+ try {
69
+ signedMessage = await promisify(kbpgp.box)({
70
+ "msg": message,
71
+ "sign_with": privKey,
72
+ "asp": ASP
73
+ });
74
+ } catch (err) {
75
+ throw new OperationError(`Couldn't sign message: ${err}`);
76
+ }
77
+
78
+ return signedMessage;
79
+ }
80
+
81
+ }
82
+
83
+ export default PGPSign;
@@ -92,7 +92,7 @@ class ParseEthernetFrame extends Operation {
92
92
  const packetData = input.slice(offset);
93
93
 
94
94
  if (outputFormat === "Packet data") {
95
- return Utils.byteArrayToChars(packetData);
95
+ return Utils.escapeHtml(Utils.byteArrayToChars(packetData));
96
96
  } else if (outputFormat === "Packet data (hex)") {
97
97
  return toHex(packetData);
98
98
  } else if (outputFormat === "Text output") {
@@ -138,7 +138,7 @@ class ParseIPv4Header extends Operation {
138
138
  } else if (outputFormat === "Data (hex)") {
139
139
  return toHex(data);
140
140
  } else if (outputFormat === "Data (raw)") {
141
- return Utils.byteArrayToChars(data);
141
+ return Utils.escapeHtml(Utils.byteArrayToChars(data));
142
142
  }
143
143
  }
144
144
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  import Operation from "../Operation.mjs";
8
8
  import OperationError from "../errors/OperationError.mjs";
9
- import BSON from "bson";
9
+ import { ObjectId } from "bson";
10
10
 
11
11
  /**
12
12
  * Parse ObjectID timestamp operation
@@ -35,7 +35,7 @@ class ParseObjectIDTimestamp extends Operation {
35
35
  */
36
36
  run(input, args) {
37
37
  try {
38
- const objectId = new BSON.ObjectID(input);
38
+ const objectId = new ObjectId(input);
39
39
  return objectId.getTimestamp().toISOString();
40
40
  } catch (err) {
41
41
  throw new OperationError(err);
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import Operation from "../Operation.mjs";
8
- import UAParser from "ua-parser-js";
8
+ import { UAParser } from "ua-parser-js";
9
9
 
10
10
  /**
11
11
  * Parse User Agent operation
@@ -0,0 +1,83 @@
1
+ /**
2
+ * ROR13 Hash operation (Windows API hashing convention)
3
+ * @author fufu_btw
4
+ * @license Apache-2.0
5
+ */
6
+
7
+ import Operation from "../Operation.mjs";
8
+
9
+ /**
10
+ * Implements a ROR13 hash used for API name hashing techniques.
11
+ */
12
+ class ROR13 extends Operation {
13
+
14
+ /**
15
+ * Constructor
16
+ */
17
+ constructor() {
18
+ super();
19
+
20
+ this.name = "ROR13";
21
+ this.module = "Default";
22
+ this.description = "Computes a ROR13 hash used in API hashing techniques.";
23
+ this.infoURL = "";
24
+ this.inputType = "byteArray";
25
+ this.outputType = "string";
26
+
27
+ this.args = [];
28
+ }
29
+
30
+ /**
31
+ * Rotate right (32-bit)
32
+ *
33
+ * @param {number} value - input value
34
+ * @param {number} bits - rotation bits
35
+ * @returns {number} rotated value
36
+ */
37
+ ror(value, bits) {
38
+ return ((value >>> bits) | (value << (32 - bits))) >>> 0;
39
+ }
40
+
41
+ /**
42
+ * Execute ROR13 hash
43
+ *
44
+ * @param {byteArray} input - input bytes
45
+ * @param {Object[]} args - operation arguments
46
+ * @returns {string} hex hash
47
+ */
48
+ run(input, args) {
49
+ let hash = 0;
50
+
51
+ for (let i = 0; i < input.length; i++) {
52
+ const chr = input[i] & 0xFF;
53
+ hash = this.ror(hash, 13);
54
+ hash = (hash + chr) >>> 0;
55
+ }
56
+
57
+ return "0x" + hash.toString(16).padStart(8, "0").toUpperCase();
58
+ }
59
+
60
+ /**
61
+ * Highlight input
62
+ *
63
+ * @param {Object[]} pos
64
+ * @param {Object[]} args
65
+ * @returns {Object[]}
66
+ */
67
+ highlight(pos, args) {
68
+ return pos;
69
+ }
70
+
71
+ /**
72
+ * Reverse highlight
73
+ *
74
+ * @param {Object[]} pos
75
+ * @param {Object[]} args
76
+ * @returns {Object[]}
77
+ */
78
+ highlightReverse(pos, args) {
79
+ return pos;
80
+ }
81
+ }
82
+
83
+ export default ROR13;