cyberchef 9.38.6 → 9.38.9

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.38.6",
3
+ "version": "9.38.9",
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",
@@ -122,6 +122,7 @@
122
122
  "js-crc": "^0.2.0",
123
123
  "js-sha3": "^0.8.0",
124
124
  "jsesc": "^3.0.2",
125
+ "json5": "^2.2.1",
125
126
  "jsonpath": "^1.1.1",
126
127
  "jsonwebtoken": "^8.5.1",
127
128
  "jsqr": "^1.4.0",
@@ -7637,10 +7637,10 @@
7637
7637
  },
7638
7638
  "JSON Beautify": {
7639
7639
  "module": "Code",
7640
- "description": "Indents and prettifies JavaScript Object Notation (JSON) code.",
7640
+ "description": "Indents and pretty prints JavaScript Object Notation (JSON) code.<br><br>Tags: json viewer, prettify, syntax highlighting",
7641
7641
  "infoURL": null,
7642
7642
  "inputType": "string",
7643
- "outputType": "string",
7643
+ "outputType": "html",
7644
7644
  "flowControl": false,
7645
7645
  "manualBake": false,
7646
7646
  "args": [
@@ -7653,6 +7653,11 @@
7653
7653
  "name": "Sort Object Keys",
7654
7654
  "type": "boolean",
7655
7655
  "value": false
7656
+ },
7657
+ {
7658
+ "name": "Formatted",
7659
+ "type": "boolean",
7660
+ "value": true
7656
7661
  }
7657
7662
  ]
7658
7663
  },
@@ -8580,7 +8585,7 @@
8580
8585
  "PEM to Hex": {
8581
8586
  "module": "Default",
8582
8587
  "description": "Converts PEM (Privacy Enhanced Mail) format to a hexadecimal DER (Distinguished Encoding Rules) string.",
8583
- "infoURL": "https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail#Format",
8588
+ "infoURL": "https://wikipedia.org/wiki/Privacy-Enhanced_Mail#Format",
8584
8589
  "inputType": "string",
8585
8590
  "outputType": "string",
8586
8591
  "flowControl": false,
@@ -130,10 +130,11 @@ export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", r
130
130
  i = 0;
131
131
 
132
132
  while (i < data.length) {
133
- enc1 = alphabet.indexOf(data.charAt(i++));
134
- enc2 = alphabet.indexOf(data.charAt(i++));
135
- enc3 = alphabet.indexOf(data.charAt(i++));
136
- enc4 = alphabet.indexOf(data.charAt(i++));
133
+ // Including `|| null` forces empty strings to null so that indexOf returns -1 instead of 0
134
+ enc1 = alphabet.indexOf(data.charAt(i++) || null);
135
+ enc2 = alphabet.indexOf(data.charAt(i++) || null);
136
+ enc3 = alphabet.indexOf(data.charAt(i++) || null);
137
+ enc4 = alphabet.indexOf(data.charAt(i++) || null);
137
138
 
138
139
  if (strictMode && (enc1 < 0 || enc2 < 0 || enc3 < 0 || enc4 < 0)) {
139
140
  throw new OperationError("Error: Base64 input contains non-alphabet char(s)");
@@ -143,13 +144,13 @@ export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", r
143
144
  chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
144
145
  chr3 = ((enc3 & 3) << 6) | enc4;
145
146
 
146
- if (chr1 < 256) {
147
+ if (chr1 >= 0 && chr1 < 256) {
147
148
  output.push(chr1);
148
149
  }
149
- if (chr2 < 256 && enc3 !== 64) {
150
+ if (chr2 >= 0 && chr2 < 256 && enc3 !== 64) {
150
151
  output.push(chr2);
151
152
  }
152
- if (chr3 < 256 && enc4 !== 64) {
153
+ if (chr3 >= 0 && chr3 < 256 && enc4 !== 64) {
153
154
  output.push(chr3);
154
155
  }
155
156
  }
@@ -9,35 +9,25 @@
9
9
  import { toHex, fromHex } from "./Hex.mjs";
10
10
 
11
11
  /**
12
- * Formats Distinguished Name (DN) strings.
12
+ * Formats Distinguished Name (DN) objects to strings.
13
13
  *
14
- * @param {string} dnStr
14
+ * @param {Object} dnObj
15
15
  * @param {number} indent
16
16
  * @returns {string}
17
17
  */
18
- export function formatDnStr(dnStr, indent) {
19
- const fields = dnStr.substr(1).replace(/([^\\])\//g, "$1$1/").split(/[^\\]\//);
20
- let output = "",
21
- maxKeyLen = 0,
22
- key,
23
- value,
24
- i,
25
- str;
26
-
27
- for (i = 0; i < fields.length; i++) {
28
- if (!fields[i].length) continue;
29
-
30
- key = fields[i].split("=")[0];
18
+ export function formatDnObj(dnObj, indent) {
19
+ let output = "";
31
20
 
32
- maxKeyLen = key.length > maxKeyLen ? key.length : maxKeyLen;
33
- }
21
+ const maxKeyLen = dnObj.array.reduce((max, item) => {
22
+ return item[0].type.length > max ? item[0].type.length : max;
23
+ }, 0);
34
24
 
35
- for (i = 0; i < fields.length; i++) {
36
- if (!fields[i].length) continue;
25
+ for (let i = 0; i < dnObj.array.length; i++) {
26
+ if (!dnObj.array[i].length) continue;
37
27
 
38
- key = fields[i].split("=")[0];
39
- value = fields[i].split("=")[1];
40
- str = key.padEnd(maxKeyLen, " ") + " = " + value + "\n";
28
+ const key = dnObj.array[i][0].type;
29
+ const value = dnObj.array[i][0].value;
30
+ const str = `${key.padEnd(maxKeyLen, " ")} = ${value}\n`;
41
31
 
42
32
  output += str.padStart(indent + str.length, " ");
43
33
  }
@@ -5,8 +5,10 @@
5
5
  * @license Apache-2.0
6
6
  */
7
7
 
8
- import vkbeautify from "vkbeautify";
8
+ import JSON5 from "json5";
9
+ import OperationError from "../errors/OperationError.mjs";
9
10
  import Operation from "../Operation.mjs";
11
+ import Utils from "../Utils.mjs";
10
12
 
11
13
  /**
12
14
  * JSON Beautify operation
@@ -21,19 +23,25 @@ class JSONBeautify extends Operation {
21
23
 
22
24
  this.name = "JSON Beautify";
23
25
  this.module = "Code";
24
- this.description = "Indents and prettifies JavaScript Object Notation (JSON) code.";
26
+ this.description = "Indents and pretty prints JavaScript Object Notation (JSON) code.<br><br>Tags: json viewer, prettify, syntax highlighting";
25
27
  this.inputType = "string";
26
28
  this.outputType = "string";
29
+ this.presentType = "html";
27
30
  this.args = [
28
31
  {
29
- "name": "Indent string",
30
- "type": "binaryShortString",
31
- "value": " "
32
+ name: "Indent string",
33
+ type: "binaryShortString",
34
+ value: " "
32
35
  },
33
36
  {
34
- "name": "Sort Object Keys",
35
- "type": "boolean",
36
- "value": false
37
+ name: "Sort Object Keys",
38
+ type: "boolean",
39
+ value: false
40
+ },
41
+ {
42
+ name: "Formatted",
43
+ type: "boolean",
44
+ value: true
37
45
  }
38
46
  ];
39
47
  }
@@ -44,35 +52,193 @@ class JSONBeautify extends Operation {
44
52
  * @returns {string}
45
53
  */
46
54
  run(input, args) {
55
+ if (!input) return "";
56
+
47
57
  const [indentStr, sortBool] = args;
58
+ let json = null;
48
59
 
49
- if (!input) return "";
50
- if (sortBool) {
51
- input = JSON.stringify(JSONBeautify._sort(JSON.parse(input)));
60
+ try {
61
+ json = JSON5.parse(input);
62
+ } catch (err) {
63
+ throw new OperationError("Unable to parse input as JSON.\n" + err);
52
64
  }
53
- return vkbeautify.json(input, indentStr);
54
- }
55
65
 
66
+ if (sortBool) json = sortKeys(json);
67
+
68
+ return JSON.stringify(json, null, indentStr);
69
+ }
56
70
 
57
71
  /**
58
- * Sort JSON representation of an object
72
+ * Adds various dynamic features to the JSON blob
59
73
  *
60
- * @author Phillip Nordwall [phillip.nordwall@gmail.com]
61
- * @private
62
- * @param {object} o
63
- * @returns {object}
74
+ * @param {string} data
75
+ * @param {Object[]} args
76
+ * @returns {html}
64
77
  */
65
- static _sort(o) {
66
- if (Array.isArray(o)) {
67
- return o.map(JSONBeautify._sort);
68
- } else if ("[object Object]" === Object.prototype.toString.call(o)) {
69
- return Object.keys(o).sort().reduce(function(a, k) {
70
- a[k] = JSONBeautify._sort(o[k]);
71
- return a;
72
- }, {});
78
+ present(data, args) {
79
+ const formatted = args[2];
80
+ if (!formatted) return Utils.escapeHtml(data);
81
+
82
+ const json = JSON5.parse(data);
83
+ const options = {
84
+ withLinks: true,
85
+ bigNumbers: true
86
+ };
87
+ let html = '<div class="json-document">';
88
+
89
+ if (isCollapsable(json)) {
90
+ const isArr = json instanceof Array;
91
+ html += '<details open class="json-details">' +
92
+ `<summary class="json-summary ${isArr ? "json-arr" : "json-obj"}"></summary>` +
93
+ json2html(json, options) +
94
+ "</details>";
95
+ } else {
96
+ html += json2html(json, options);
97
+ }
98
+
99
+ html += "</div>";
100
+ return html;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Sort keys in a JSON object
106
+ *
107
+ * @author Phillip Nordwall [phillip.nordwall@gmail.com]
108
+ * @param {object} o
109
+ * @returns {object}
110
+ */
111
+ function sortKeys(o) {
112
+ if (Array.isArray(o)) {
113
+ return o.map(sortKeys);
114
+ } else if ("[object Object]" === Object.prototype.toString.call(o)) {
115
+ return Object.keys(o).sort().reduce(function(a, k) {
116
+ a[k] = sortKeys(o[k]);
117
+ return a;
118
+ }, {});
119
+ }
120
+ return o;
121
+ }
122
+
123
+
124
+ /**
125
+ * Check if arg is either an array with at least 1 element, or a dict with at least 1 key
126
+ * @returns {boolean}
127
+ */
128
+ function isCollapsable(arg) {
129
+ return arg instanceof Object && Object.keys(arg).length > 0;
130
+ }
131
+
132
+ /**
133
+ * Check if a string looks like a URL, based on protocol
134
+ * @returns {boolean}
135
+ */
136
+ function isUrl(string) {
137
+ const protocols = ["http", "https", "ftp", "ftps"];
138
+ for (let i = 0; i < protocols.length; i++) {
139
+ if (string.startsWith(protocols[i] + "://")) {
140
+ return true;
141
+ }
142
+ }
143
+ return false;
144
+ }
145
+
146
+ /**
147
+ * Transform a json object into html representation
148
+ *
149
+ * Adapted for CyberChef by @n1474335 from jQuery json-viewer
150
+ * @author Alexandre Bodelot <alexandre.bodelot@gmail.com>
151
+ * @link https://github.com/abodelot/jquery.json-viewer
152
+ * @license MIT
153
+ *
154
+ * @returns {string}
155
+ */
156
+ function json2html(json, options) {
157
+ let html = "";
158
+ if (typeof json === "string") {
159
+ // Escape tags and quotes
160
+ json = Utils.escapeHtml(json);
161
+
162
+ if (options.withLinks && isUrl(json)) {
163
+ html += `<a href="${json}" class="json-string" target="_blank">${json}</a>`;
164
+ } else {
165
+ // Escape double quotes in the rendered non-URL string.
166
+ json = json.replace(/&quot;/g, "\\&quot;");
167
+ html += `<span class="json-string">"${json}"</span>`;
168
+ }
169
+ } else if (typeof json === "number" || typeof json === "bigint") {
170
+ html += `<span class="json-literal">${json}</span>`;
171
+ } else if (typeof json === "boolean") {
172
+ html += `<span class="json-literal">${json}</span>`;
173
+ } else if (json === null) {
174
+ html += '<span class="json-literal">null</span>';
175
+ } else if (json instanceof Array) {
176
+ if (json.length > 0) {
177
+ html += '<span class="json-bracket">[</span><ol class="json-array">';
178
+ for (let i = 0; i < json.length; i++) {
179
+ html += "<li>";
180
+
181
+ // Add toggle button if item is collapsable
182
+ if (isCollapsable(json[i])) {
183
+ const isArr = json[i] instanceof Array;
184
+ html += '<details open class="json-details">' +
185
+ `<summary class="json-summary ${isArr ? "json-arr" : "json-obj"}"></summary>` +
186
+ json2html(json[i], options) +
187
+ "</details>";
188
+ } else {
189
+ html += json2html(json[i], options);
190
+ }
191
+
192
+ // Add comma if item is not last
193
+ if (i < json.length - 1) {
194
+ html += '<span class="json-comma">,</span>';
195
+ }
196
+ html += "</li>";
197
+ }
198
+ html += '</ol><span class="json-bracket">]</span>';
199
+ } else {
200
+ html += '<span class="json-bracket">[]</span>';
201
+ }
202
+ } else if (typeof json === "object") {
203
+ // Optional support different libraries for big numbers
204
+ // json.isLosslessNumber: package lossless-json
205
+ // json.toExponential(): packages bignumber.js, big.js, decimal.js, decimal.js-light, others?
206
+ if (options.bigNumbers && (typeof json.toExponential === "function" || json.isLosslessNumber)) {
207
+ html += `<span class="json-literal">${json.toString()}</span>`;
208
+ } else {
209
+ let keyCount = Object.keys(json).length;
210
+ if (keyCount > 0) {
211
+ html += '<span class="json-brace">{</span><ul class="json-dict">';
212
+ for (const key in json) {
213
+ if (Object.prototype.hasOwnProperty.call(json, key)) {
214
+ const safeKey = Utils.escapeHtml(key);
215
+ html += "<li>";
216
+
217
+ // Add toggle button if item is collapsable
218
+ if (isCollapsable(json[key])) {
219
+ const isArr = json[key] instanceof Array;
220
+ html += '<details open class="json-details">' +
221
+ `<summary class="json-summary ${isArr ? "json-arr" : "json-obj"}">${safeKey}<span class="json-colon">:</span> </summary>` +
222
+ json2html(json[key], options) +
223
+ "</details>";
224
+ } else {
225
+ html += safeKey + '<span class="json-colon">:</span> ' + json2html(json[key], options);
226
+ }
227
+
228
+ // Add comma if item is not last
229
+ if (--keyCount > 0) {
230
+ html += '<span class="json-comma">,</span>';
231
+ }
232
+ html += "</li>";
233
+ }
234
+ }
235
+ html += '</ul><span class="json-brace">}</span>';
236
+ } else {
237
+ html += '<span class="json-brace">{}</span>';
238
+ }
73
239
  }
74
- return o;
75
240
  }
241
+ return html;
76
242
  }
77
243
 
78
244
  export default JSONBeautify;
@@ -24,7 +24,7 @@ class PEMToHex extends Operation {
24
24
  this.name = "PEM to Hex";
25
25
  this.module = "Default";
26
26
  this.description = "Converts PEM (Privacy Enhanced Mail) format to a hexadecimal DER (Distinguished Encoding Rules) string.";
27
- this.infoURL = "https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail#Format";
27
+ this.infoURL = "https://wikipedia.org/wiki/Privacy-Enhanced_Mail#Format";
28
28
  this.inputType = "string";
29
29
  this.outputType = "string";
30
30
  this.args = [];
@@ -7,7 +7,7 @@
7
7
  import r from "jsrsasign";
8
8
  import { fromBase64 } from "../lib/Base64.mjs";
9
9
  import { toHex } from "../lib/Hex.mjs";
10
- import { formatByteStr, formatDnStr } from "../lib/PublicKey.mjs";
10
+ import { formatByteStr, formatDnObj } from "../lib/PublicKey.mjs";
11
11
  import Operation from "../Operation.mjs";
12
12
  import Utils from "../Utils.mjs";
13
13
 
@@ -76,8 +76,8 @@ class ParseX509Certificate extends Operation {
76
76
  }
77
77
 
78
78
  const sn = cert.getSerialNumberHex(),
79
- issuer = cert.getIssuerString(),
80
- subject = cert.getSubjectString(),
79
+ issuer = cert.getIssuer(),
80
+ subject = cert.getSubject(),
81
81
  pk = cert.getPublicKey(),
82
82
  pkFields = [],
83
83
  sig = cert.getSignatureValueHex();
@@ -170,10 +170,10 @@ class ParseX509Certificate extends Operation {
170
170
  extensions = cert.getInfo().split("X509v3 Extensions:\n")[1].split("signature")[0];
171
171
  } catch (err) {}
172
172
 
173
- const issuerStr = formatDnStr(issuer, 2),
173
+ const issuerStr = formatDnObj(issuer, 2),
174
174
  nbDate = formatDate(cert.getNotBefore()),
175
175
  naDate = formatDate(cert.getNotAfter()),
176
- subjectStr = formatDnStr(subject, 2);
176
+ subjectStr = formatDnObj(subject, 2);
177
177
 
178
178
  return `Version: ${cert.version} (0x${Utils.hex(cert.version - 1)})
179
179
  Serial number: ${new r.BigInteger(sn, 16).toString()} (0x${sn})
@@ -34,3 +34,6 @@
34
34
  @import "./layout/_operations.css";
35
35
  @import "./layout/_recipe.css";
36
36
  @import "./layout/_structure.css";
37
+
38
+ /* Operations */
39
+ @import "./operations/json.css";
@@ -0,0 +1,78 @@
1
+ /**
2
+ * JSON styles
3
+ *
4
+ * @author n1474335 [n1474335@gmail.com]
5
+ * @copyright Crown Copyright 2022
6
+ * @license Apache-2.0
7
+ *
8
+ * Adapted for CyberChef by @n1474335 from jQuery json-viewer
9
+ * @author Alexandre Bodelot <alexandre.bodelot@gmail.com>
10
+ * @link https://github.com/abodelot/jquery.json-viewer
11
+ * @license MIT
12
+ */
13
+
14
+ /* Root element */
15
+ .json-document {
16
+ padding: .5em 1.5em;
17
+ }
18
+
19
+ /* Syntax highlighting for JSON objects */
20
+ ul.json-dict, ol.json-array {
21
+ list-style-type: none;
22
+ margin: 0 0 0 1px;
23
+ border-left: 1px dotted #ccc;
24
+ padding-left: 2em;
25
+ }
26
+ .json-string {
27
+ color: green;
28
+ }
29
+ .json-literal {
30
+ color: red;
31
+ }
32
+ .json-brace,
33
+ .json-bracket,
34
+ .json-colon,
35
+ .json-comma {
36
+ color: gray;
37
+ }
38
+
39
+ /* Collapse */
40
+ .json-details {
41
+ display: inline;
42
+ }
43
+ .json-details[open] {
44
+ display: contents;
45
+ }
46
+ .json-summary {
47
+ display: contents;
48
+ }
49
+
50
+ /* Display object and array brackets when closed */
51
+ .json-summary.json-obj::after {
52
+ color: gray;
53
+ content: "{ ... }"
54
+ }
55
+ .json-summary.json-arr::after {
56
+ color: gray;
57
+ content: "[ ... ]"
58
+ }
59
+ .json-details[open] > .json-summary.json-obj::after,
60
+ .json-details[open] > .json-summary.json-arr::after {
61
+ content: "";
62
+ }
63
+
64
+ /* Show arrows, even in inline mode */
65
+ .json-summary::before {
66
+ content: "\25BC";
67
+ color: #c0c0c0;
68
+ margin-left: -12px;
69
+ margin-right: 5px;
70
+ display: inline-block;
71
+ transform: rotate(-90deg);
72
+ }
73
+ .json-summary:hover::before {
74
+ color: #aaa;
75
+ }
76
+ .json-details[open] > .json-summary::before {
77
+ transform: rotate(0deg);
78
+ }
@@ -16,7 +16,7 @@ TestRegister.addTests([
16
16
  recipeConfig: [
17
17
  {
18
18
  op: "JSON Beautify",
19
- args: [" ", false],
19
+ args: [" ", false, false],
20
20
  },
21
21
  ],
22
22
  },
@@ -27,7 +27,7 @@ TestRegister.addTests([
27
27
  recipeConfig: [
28
28
  {
29
29
  op: "JSON Beautify",
30
- args: [" ", false],
30
+ args: [" ", false, false],
31
31
  },
32
32
  ],
33
33
  },
@@ -38,8 +38,12 @@ TestRegister.addTests([
38
38
  recipeConfig: [
39
39
  {
40
40
  op: "JSON Beautify",
41
- args: [" ", false],
41
+ args: [" ", false, false],
42
42
  },
43
+ {
44
+ op: "HTML To Text",
45
+ args: []
46
+ }
43
47
  ],
44
48
  },
45
49
  {
@@ -49,7 +53,7 @@ TestRegister.addTests([
49
53
  recipeConfig: [
50
54
  {
51
55
  op: "JSON Beautify",
52
- args: [" ", false],
56
+ args: [" ", false, false],
53
57
  },
54
58
  ],
55
59
  },
@@ -60,7 +64,7 @@ TestRegister.addTests([
60
64
  recipeConfig: [
61
65
  {
62
66
  op: "JSON Beautify",
63
- args: [" ", false],
67
+ args: [" ", false, false],
64
68
  },
65
69
  ],
66
70
  },
@@ -71,7 +75,7 @@ TestRegister.addTests([
71
75
  recipeConfig: [
72
76
  {
73
77
  op: "JSON Beautify",
74
- args: [" ", false],
78
+ args: [" ", false, false],
75
79
  },
76
80
  ],
77
81
  },
@@ -82,7 +86,7 @@ TestRegister.addTests([
82
86
  recipeConfig: [
83
87
  {
84
88
  op: "JSON Beautify",
85
- args: ["\t", false],
89
+ args: ["\t", false, false],
86
90
  },
87
91
  ],
88
92
  },
@@ -93,8 +97,12 @@ TestRegister.addTests([
93
97
  recipeConfig: [
94
98
  {
95
99
  op: "JSON Beautify",
96
- args: [" ", false],
100
+ args: [" ", false, false],
97
101
  },
102
+ {
103
+ op: "HTML To Text",
104
+ args: []
105
+ }
98
106
  ],
99
107
  },
100
108
  {
@@ -104,8 +112,12 @@ TestRegister.addTests([
104
112
  recipeConfig: [
105
113
  {
106
114
  op: "JSON Beautify",
107
- args: ["\t", false],
115
+ args: ["\t", false, false],
108
116
  },
117
+ {
118
+ op: "HTML To Text",
119
+ args: []
120
+ }
109
121
  ],
110
122
  },
111
123
  {
@@ -115,8 +127,12 @@ TestRegister.addTests([
115
127
  recipeConfig: [
116
128
  {
117
129
  op: "JSON Beautify",
118
- args: ["\t", true],
130
+ args: ["\t", true, false],
119
131
  },
132
+ {
133
+ op: "HTML To Text",
134
+ args: []
135
+ }
120
136
  ],
121
137
  },
122
138
  ]);