cyberchef 9.39.3 → 9.39.6

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyberchef",
3
- "version": "9.39.3",
3
+ "version": "9.39.6",
4
4
  "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
5
5
  "author": "n1474335 <n1474335@gmail.com>",
6
6
  "homepage": "https://gchq.github.io/CyberChef",
@@ -5652,6 +5652,11 @@
5652
5652
  "name": "Alphabet",
5653
5653
  "type": "string",
5654
5654
  "value": "0-9A-Z $%*+\\-./:"
5655
+ },
5656
+ {
5657
+ "name": "Remove non-alphabet chars",
5658
+ "type": "boolean",
5659
+ "value": true
5655
5660
  }
5656
5661
  ]
5657
5662
  },
@@ -5995,6 +6000,31 @@
5995
6000
  "value": "0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~"
5996
6001
  }
5997
6002
  ]
6003
+ },
6004
+ {
6005
+ "name": "Remove non-alphabet chars",
6006
+ "type": "boolean",
6007
+ "value": true
6008
+ }
6009
+ ],
6010
+ "checks": [
6011
+ {
6012
+ "pattern": "^\\s*(?:<~)?[\\s!-uz]*[!-uz]{15}[\\s!-uz]*(?:~>)?\\s*$",
6013
+ "args": [
6014
+ "!-u"
6015
+ ]
6016
+ },
6017
+ {
6018
+ "pattern": "^[\\s0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]*[0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]{15}[\\s0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]*$",
6019
+ "args": [
6020
+ "0-9a-zA-Z.\\-:+=^!/*?&<>()[]{}@%$#"
6021
+ ]
6022
+ },
6023
+ {
6024
+ "pattern": "^[\\s0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]*[0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]{15}[\\s0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]*$",
6025
+ "args": [
6026
+ "0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~"
6027
+ ]
5998
6028
  }
5999
6029
  ]
6000
6030
  },
@@ -8340,13 +8370,19 @@
8340
8370
  },
8341
8371
  "Merge": {
8342
8372
  "module": "Default",
8343
- "description": "Consolidate all branches back into a single trunk. The opposite of Fork.",
8373
+ "description": "Consolidate all branches back into a single trunk. The opposite of Fork. Unticking the Merge All checkbox will only consolidate all branches up to the nearest Fork/Subsection.",
8344
8374
  "infoURL": null,
8345
8375
  "inputType": "string",
8346
8376
  "outputType": "string",
8347
8377
  "flowControl": true,
8348
8378
  "manualBake": false,
8349
- "args": []
8379
+ "args": [
8380
+ {
8381
+ "name": "Merge All",
8382
+ "type": "boolean",
8383
+ "value": true
8384
+ }
8385
+ ]
8350
8386
  },
8351
8387
  "Microsoft Script Decoder": {
8352
8388
  "module": "Default",
@@ -1,3 +1,5 @@
1
+ import Utils from "../Utils.mjs";
2
+
1
3
  /**
2
4
  * Base85 resources.
3
5
  *
@@ -32,13 +34,12 @@ export const ALPHABET_OPTIONS = [
32
34
  * @returns {string}
33
35
  */
34
36
  export function alphabetName(alphabet) {
35
- alphabet = alphabet.replace(/'/g, "&apos;");
36
- alphabet = alphabet.replace(/"/g, "&quot;");
37
- alphabet = alphabet.replace(/\\/g, "&bsol;");
37
+ alphabet = escape(alphabet);
38
38
  let name;
39
39
 
40
40
  ALPHABET_OPTIONS.forEach(function(a) {
41
- if (escape(alphabet) === escape(a.value)) name = a.name;
41
+ const expanded = Utils.expandAlphRange(a.value).join("");
42
+ if (alphabet === escape(expanded)) name = a.name;
42
43
  });
43
44
 
44
45
  return name;
@@ -65,12 +65,21 @@ class Fork extends Operation {
65
65
  if (input)
66
66
  inputs = input.split(splitDelim);
67
67
 
68
+ // Set to 1 as if we are here, then there is one, the current one.
69
+ let numOp = 1;
68
70
  // Create subOpList for each tranche to operate on
69
- // (all remaining operations unless we encounter a Merge)
71
+ // all remaining operations unless we encounter a Merge
70
72
  for (i = state.progress + 1; i < opList.length; i++) {
71
73
  if (opList[i].name === "Merge" && !opList[i].disabled) {
72
- break;
74
+ numOp--;
75
+ if (numOp === 0 || opList[i].ingValues[0])
76
+ break;
77
+ else
78
+ // Not this Fork's Merge.
79
+ subOpList.push(opList[i]);
73
80
  } else {
81
+ if (opList[i].name === "Fork" || opList[i].name === "Subsection")
82
+ numOp++;
74
83
  subOpList.push(opList[i]);
75
84
  }
76
85
  }
@@ -32,7 +32,12 @@ class FromBase45 extends Operation {
32
32
  name: "Alphabet",
33
33
  type: "string",
34
34
  value: ALPHABET
35
- }
35
+ },
36
+ {
37
+ name: "Remove non-alphabet chars",
38
+ type: "boolean",
39
+ value: true
40
+ },
36
41
  ];
37
42
 
38
43
  this.highlight = highlightFromBase45;
@@ -46,10 +51,17 @@ class FromBase45 extends Operation {
46
51
  */
47
52
  run(input, args) {
48
53
  if (!input) return [];
49
- const alphabet = Utils.expandAlphRange(args[0]);
54
+ const alphabet = Utils.expandAlphRange(args[0]).join("");
55
+ const removeNonAlphChars = args[1];
50
56
 
51
57
  const res = [];
52
58
 
59
+ // Remove non-alphabet characters
60
+ if (removeNonAlphChars) {
61
+ const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
62
+ input = input.replace(re, "");
63
+ }
64
+
53
65
  for (const triple of Utils.chunked(input, 3)) {
54
66
  triple.reverse();
55
67
  let b = 0;
@@ -32,6 +32,40 @@ class FromBase85 extends Operation {
32
32
  type: "editableOption",
33
33
  value: ALPHABET_OPTIONS
34
34
  },
35
+ {
36
+ name: "Remove non-alphabet chars",
37
+ type: "boolean",
38
+ value: true
39
+ },
40
+ ];
41
+ this.checks = [
42
+ {
43
+ pattern:
44
+ "^\\s*(?:<~)?" + // Optional whitespace and starting marker
45
+ "[\\s!-uz]*" + // Any amount of base85 characters and whitespace
46
+ "[!-uz]{15}" + // At least 15 continoues base85 characters without whitespace
47
+ "[\\s!-uz]*" + // Any amount of base85 characters and whitespace
48
+ "(?:~>)?\\s*$", // Optional ending marker and whitespace
49
+ args: ["!-u"],
50
+ },
51
+ {
52
+ pattern:
53
+ "^" +
54
+ "[\\s0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]*" +
55
+ "[0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]{15}" + // At least 15 continoues base85 characters without whitespace
56
+ "[\\s0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]*" +
57
+ "$",
58
+ args: ["0-9a-zA-Z.\\-:+=^!/*?&<>()[]{}@%$#"],
59
+ },
60
+ {
61
+ pattern:
62
+ "^" +
63
+ "[\\s0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]*" +
64
+ "[0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]{15}" + // At least 15 continoues base85 characters without whitespace
65
+ "[\\s0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]*" +
66
+ "$",
67
+ args: ["0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~"],
68
+ },
35
69
  ];
36
70
  }
37
71
 
@@ -43,6 +77,7 @@ class FromBase85 extends Operation {
43
77
  run(input, args) {
44
78
  const alphabet = Utils.expandAlphRange(args[0]).join(""),
45
79
  encoding = alphabetName(alphabet),
80
+ removeNonAlphChars = args[1],
46
81
  result = [];
47
82
 
48
83
  if (alphabet.length !== 85 ||
@@ -50,11 +85,18 @@ class FromBase85 extends Operation {
50
85
  throw new OperationError("Alphabet must be of length 85");
51
86
  }
52
87
 
53
- if (input.length === 0) return [];
54
-
55
- const matches = input.match(/<~(.+?)~>/);
88
+ // Remove delimiters if present
89
+ const matches = input.match(/^<~(.+?)~>$/);
56
90
  if (matches !== null) input = matches[1];
57
91
 
92
+ // Remove non-alphabet characters
93
+ if (removeNonAlphChars) {
94
+ const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
95
+ input = input.replace(re, "");
96
+ }
97
+
98
+ if (input.length === 0) return [];
99
+
58
100
  let i = 0;
59
101
  let block, blockBytes;
60
102
  while (i < input.length) {
@@ -69,7 +111,7 @@ class FromBase85 extends Operation {
69
111
  .map((chr, idx) => {
70
112
  const digit = alphabet.indexOf(chr);
71
113
  if (digit < 0 || digit > 84) {
72
- throw `Invalid character '${chr}' at index ${idx}`;
114
+ throw `Invalid character '${chr}' at index ${i + idx}`;
73
115
  }
74
116
  return digit;
75
117
  });
@@ -20,10 +20,16 @@ class Merge extends Operation {
20
20
  this.name = "Merge";
21
21
  this.flowControl = true;
22
22
  this.module = "Default";
23
- this.description = "Consolidate all branches back into a single trunk. The opposite of Fork.";
23
+ this.description = "Consolidate all branches back into a single trunk. The opposite of Fork. Unticking the Merge All checkbox will only consolidate all branches up to the nearest Fork/Subsection.";
24
24
  this.inputType = "string";
25
25
  this.outputType = "string";
26
- this.args = [];
26
+ this.args = [
27
+ {
28
+ name: "Merge All",
29
+ type: "boolean",
30
+ value: true,
31
+ }
32
+ ];
27
33
  }
28
34
 
29
35
  /**
@@ -67,12 +67,21 @@ class Subsection extends Operation {
67
67
  subOpList = [];
68
68
 
69
69
  if (input && section !== "") {
70
+ // Set to 1 as if we are here, then there is one, the current one.
71
+ let numOp = 1;
70
72
  // Create subOpList for each tranche to operate on
71
73
  // all remaining operations unless we encounter a Merge
72
74
  for (let i = state.progress + 1; i < opList.length; i++) {
73
75
  if (opList[i].name === "Merge" && !opList[i].disabled) {
74
- break;
76
+ numOp--;
77
+ if (numOp === 0 || opList[i].ingValues[0])
78
+ break;
79
+ else
80
+ // Not this subsection's Merge.
81
+ subOpList.push(opList[i]);
75
82
  } else {
83
+ if (opList[i].name === "Fork" || opList[i].name === "Subsection")
84
+ numOp++;
76
85
  subOpList.push(opList[i]);
77
86
  }
78
87
  }
@@ -12,6 +12,7 @@
12
12
  import Chef from "../../src/core/Chef.mjs";
13
13
  import Utils from "../../src/core/Utils.mjs";
14
14
  import cliProgress from "cli-progress";
15
+ import log from "loglevel";
15
16
 
16
17
  /**
17
18
  * Object to store and run the list of tests.
@@ -50,6 +51,9 @@ class TestRegister {
50
51
  * Runs all the tests in the register.
51
52
  */
52
53
  async runTests () {
54
+ // Turn off logging to avoid messy errors
55
+ log.setLevel("silent", false);
56
+
53
57
  const progBar = new cliProgress.SingleBar({
54
58
  format: formatter,
55
59
  stopOnComplete: true
@@ -84,7 +88,17 @@ class TestRegister {
84
88
 
85
89
  if (result.error) {
86
90
  if (test.expectedError) {
87
- ret.status = "passing";
91
+ if (result.error.displayStr === test.expectedOutput) {
92
+ ret.status = "passing";
93
+ } else {
94
+ ret.status = "failing";
95
+ ret.output = [
96
+ "Expected",
97
+ "\t" + test.expectedOutput.replace(/\n/g, "\n\t"),
98
+ "Received",
99
+ "\t" + result.error.displayStr.replace(/\n/g, "\n\t"),
100
+ ].join("\n");
101
+ }
88
102
  } else {
89
103
  ret.status = "erroring";
90
104
  ret.output = result.error.displayStr;
@@ -118,6 +132,9 @@ class TestRegister {
118
132
  progBar.increment();
119
133
  }
120
134
 
135
+ // Turn logging back on
136
+ log.setLevel("info", false);
137
+
121
138
  return testResults;
122
139
  }
123
140
 
@@ -24,6 +24,7 @@ import "./tests/Base45.mjs";
24
24
  import "./tests/Base58.mjs";
25
25
  import "./tests/Base64.mjs";
26
26
  import "./tests/Base62.mjs";
27
+ import "./tests/Base85.mjs";
27
28
  import "./tests/BitwiseOp.mjs";
28
29
  import "./tests/ByteRepr.mjs";
29
30
  import "./tests/CartesianProduct.mjs";
@@ -114,6 +115,7 @@ import "./tests/HASSH.mjs";
114
115
  import "./tests/GetAllCasings.mjs";
115
116
  import "./tests/SIGABA.mjs";
116
117
  import "./tests/ELFInfo.mjs";
118
+ import "./tests/Subsection.mjs";
117
119
 
118
120
 
119
121
  // Cannot test operations that use the File type yet
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Base85 tests
3
+ *
4
+ * @author john19696
5
+ * @copyright Crown Copyright 2019
6
+ * @license Apache-2.0
7
+ */
8
+ import TestRegister from "../../lib/TestRegister.mjs";
9
+
10
+ // Example from Wikipedia
11
+ const wpExample = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";
12
+ // Escape newline, quote & backslash
13
+ const wpOutput = "9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKF<GL>Cj@.4Gp$d7F!,L7@<6@)/0JDEF<G%<+EV:2F!,O<\
14
+ DJ+*.@<*K0@<6L(Df-\\0Ec5e;DffZ(EZee.Bl.9pF\"AGXBPCsi+DGm>@3BB/F*&OCAfu2/AKYi(\
15
+ DIb:@FD,*)+C]U=@3BN#EcYf8ATD3s@q?d$AftVqCh[NqF<G:8+EV:.+Cf>-FD5W8ARlolDIal(\
16
+ DId<j@<?3r@:F%a+D58'ATD4$Bl@l3De:,-DJs`8ARoFb/0JMK@qB4^F!,R<AKZ&-DfTqBG%G>u\
17
+ D.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c";
18
+
19
+ TestRegister.addTests([
20
+ {
21
+ name: "To Base85",
22
+ input: wpExample,
23
+ expectedOutput: wpOutput,
24
+ recipeConfig: [
25
+ { "op": "To Base85",
26
+ "args": ["!-u"] }
27
+ ]
28
+ },
29
+ {
30
+ name: "From Base85",
31
+ input: wpOutput + "\n",
32
+ expectedOutput: wpExample,
33
+ recipeConfig: [
34
+ { "op": "From Base85",
35
+ "args": ["!-u", true] }
36
+ ]
37
+ },
38
+ {
39
+ name: "From Base85",
40
+ input: wpOutput + "v",
41
+ expectedError: true,
42
+ expectedOutput: "From Base85 - Invalid character 'v' at index 337",
43
+ recipeConfig: [
44
+ { "op": "From Base85",
45
+ "args": ["!-u", false] }
46
+ ]
47
+ },
48
+ ]);
@@ -31,7 +31,7 @@ TestRegister.addTests([
31
31
  },
32
32
  {
33
33
  op: "Merge",
34
- args: [],
34
+ args: [true],
35
35
  },
36
36
  ],
37
37
  },
@@ -50,7 +50,7 @@ TestRegister.addTests([
50
50
  },
51
51
  {
52
52
  op: "Merge",
53
- args: [],
53
+ args: [true],
54
54
  },
55
55
  ],
56
56
  },
@@ -66,5 +66,16 @@ TestRegister.addTests([
66
66
  {"op": "Label", "args": ["skipReturn"]},
67
67
  {"op": "To Base64", "args": ["A-Za-z0-9+/="]}
68
68
  ]
69
- }
69
+ },
70
+ {
71
+ name: "Fork, Partial Merge",
72
+ input: "Hello World",
73
+ expectedOutput: "48656c6c6f 576f726c64",
74
+ recipeConfig: [
75
+ { "op": "Fork", "args": [" ", " ", false] },
76
+ { "op": "Fork", "args": ["l", "l", false] },
77
+ { "op": "Merge", "args": [false] },
78
+ { "op": "To Hex", "args": ["None", 0] },
79
+ ]
80
+ },
70
81
  ]);
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Subsection Tests.
3
+ *
4
+ * @author n1073645 [n1073645@gmail.com]
5
+ * @copyright Crown Copyright 2022
6
+ * @license Apache-2.0
7
+ */
8
+ import TestRegister from "../../lib/TestRegister.mjs";
9
+
10
+ TestRegister.addTests([
11
+ {
12
+ name: "Subsection: nothing",
13
+ input: "",
14
+ expectedOutput: "",
15
+ recipeConfig: [
16
+ {
17
+ "op": "Subsection",
18
+ "args": ["", true, true, false],
19
+ },
20
+ ],
21
+ },
22
+ {
23
+ name: "Subsection, Full Merge: nothing",
24
+ input: "",
25
+ expectedOutput: "",
26
+ recipeConfig: [
27
+ {
28
+ "op": "Subsection",
29
+ "args": ["", true, true, false],
30
+ },
31
+ {
32
+ "op": "Merge",
33
+ "args": [true],
34
+ },
35
+ ],
36
+ },
37
+ {
38
+ name: "Subsection, Partial Merge: nothing",
39
+ input: "",
40
+ expectedOutput: "",
41
+ recipeConfig: [
42
+ {
43
+ "op": "Subsection",
44
+ "args": ["", true, true, false],
45
+ },
46
+ {
47
+ "op": "Merge",
48
+ "args": [false],
49
+ },
50
+ ],
51
+ },
52
+ {
53
+ name: "Subsection, Full Merge: Base64 with Hex",
54
+ input: "SGVsbG38675629ybGQ=",
55
+ expectedOutput: "Hello World",
56
+ recipeConfig: [
57
+ {
58
+ "op": "Subsection",
59
+ "args": ["386756", true, true, false],
60
+ },
61
+ {
62
+ "op": "From Hex",
63
+ "args": ["Auto"],
64
+ },
65
+ {
66
+ "op": "Merge",
67
+ "args": [true],
68
+ },
69
+ {
70
+ "op": "From Base64",
71
+ "args": ["A-Za-z0-9+/=", true, false],
72
+ },
73
+ ],
74
+ },
75
+ {
76
+ name: "Subsection, Partial Merge: Base64 with Hex surrounded by binary data.",
77
+ input: "000000000SGVsbG38675629ybGQ=0000000000",
78
+ expectedOutput: "000000000Hello World0000000000",
79
+ recipeConfig: [
80
+ {
81
+ "op": "Subsection",
82
+ "args": ["SGVsbG38675629ybGQ=", true, true, false],
83
+ },
84
+ {
85
+ "op": "Subsection",
86
+ "args": ["386756", true, true, false],
87
+ },
88
+ {
89
+ "op": "From Hex",
90
+ "args": ["Auto"],
91
+ },
92
+ {
93
+ "op": "Merge",
94
+ "args": [false],
95
+ },
96
+ {
97
+ "op": "From Base64",
98
+ "args": ["A-Za-z0-9+/=", true, false],
99
+ },
100
+ ],
101
+ },
102
+ ]);