cyberchef 11.0.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 (63) hide show
  1. package/CHANGELOG.md +144 -0
  2. package/Dockerfile +2 -2
  3. package/Gruntfile.js +10 -5
  4. package/README.md +3 -1
  5. package/SECURITY.md +8 -18
  6. package/package.json +35 -34
  7. package/src/core/config/Categories.json +6 -0
  8. package/src/core/config/OperationConfig.json +140 -16
  9. package/src/core/config/modules/Default.mjs +8 -0
  10. package/src/core/config/modules/PGP.mjs +2 -0
  11. package/src/core/config/scripts/generateOpsIndex.mjs +63 -0
  12. package/src/core/config/scripts/newOperation.mjs +31 -4
  13. package/src/core/operations/AESDecrypt.mjs +61 -16
  14. package/src/core/operations/AESEncrypt.mjs +26 -11
  15. package/src/core/operations/BLAKE3.mjs +13 -7
  16. package/src/core/operations/BSONDeserialise.mjs +2 -2
  17. package/src/core/operations/BSONSerialise.mjs +3 -2
  18. package/src/core/operations/Bcrypt.mjs +1 -1
  19. package/src/core/operations/BcryptCompare.mjs +1 -1
  20. package/src/core/operations/DecodeText.mjs +4 -0
  21. package/src/core/operations/EncodeText.mjs +4 -0
  22. package/src/core/operations/EscapeSmartCharacters.mjs +129 -0
  23. package/src/core/operations/GenerateLoremIpsum.mjs +34 -3
  24. package/src/core/operations/GeneratePGPKeyPair.mjs +8 -7
  25. package/src/core/operations/PGPSign.mjs +83 -0
  26. package/src/core/operations/ParseEthernetFrame.mjs +1 -1
  27. package/src/core/operations/ParseIPv4Header.mjs +1 -1
  28. package/src/core/operations/ParseObjectIDTimestamp.mjs +2 -2
  29. package/src/core/operations/ParseUserAgent.mjs +1 -1
  30. package/src/core/operations/ROR13.mjs +83 -0
  31. package/src/core/operations/RemoveANSIEscapeCodes.mjs +41 -0
  32. package/src/core/operations/SeriesChart.mjs +16 -0
  33. package/src/core/operations/Wrap.mjs +47 -0
  34. package/src/core/operations/index.mjs +10 -0
  35. package/src/node/index.mjs +25 -0
  36. package/src/web/App.mjs +19 -1
  37. package/src/web/HTMLIngredient.mjs +1 -0
  38. package/src/web/html/index.html +3 -3
  39. package/src/web/static/sitemap.mjs +3 -3
  40. package/src/web/waiters/RecipeWaiter.mjs +9 -1
  41. package/tests/browser/02_ops.js +7 -7
  42. package/tests/browser/03_recipe_load.js +48 -0
  43. package/tests/browser/browserUtils.js +6 -3
  44. package/tests/node/index.mjs +1 -0
  45. package/tests/node/tests/PGP.mjs +69 -0
  46. package/tests/node/tests/operations.mjs +41 -2
  47. package/tests/operations/index.mjs +71 -66
  48. package/tests/operations/tests/BLAKE3.mjs +18 -0
  49. package/tests/operations/tests/CharEnc.mjs +26 -0
  50. package/tests/operations/tests/Charts.mjs +11 -0
  51. package/tests/operations/tests/Crypt.mjs +288 -62
  52. package/tests/operations/tests/EscapeSmartCharacters.mjs +132 -0
  53. package/tests/operations/tests/FlaskSession.mjs +11 -8
  54. package/tests/operations/tests/GenerateLoremIpsum.mjs +80 -0
  55. package/tests/operations/tests/IPv6Transition.mjs +4 -4
  56. package/tests/operations/tests/PGP.mjs +178 -154
  57. package/tests/operations/tests/ParseEthernetFrame.mjs +11 -0
  58. package/tests/operations/tests/ParseIPv4Header.mjs +23 -0
  59. package/tests/operations/tests/ParseX509CRL.mjs +16 -16
  60. package/tests/operations/tests/ROR13.mjs +45 -0
  61. package/tests/operations/tests/Register.mjs +3 -1
  62. package/tests/operations/tests/RemoveANSIEscapeCodes.mjs +62 -0
  63. package/tests/operations/tests/Wrap.mjs +44 -0
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @author 0xff1ce [github.com/0xff1ce]
3
+ * @copyright Crown Copyright 2024
4
+ * @license Apache-2.0
5
+ */
6
+
7
+ import Operation from "../Operation.mjs";
8
+
9
+ /**
10
+ * Wrap operation
11
+ */
12
+ class Wrap extends Operation {
13
+
14
+ /**
15
+ * Wrap constructor
16
+ */
17
+ constructor() {
18
+ super();
19
+
20
+ this.name = "Wrap";
21
+ this.module = "Default";
22
+ this.description = "Wraps the input text at a specified number of characters per line.";
23
+ this.inputType = "string";
24
+ this.outputType = "string";
25
+ this.args = [
26
+ {
27
+ "name": "Line Width",
28
+ "type": "number",
29
+ "value": 64,
30
+ },
31
+ ];
32
+ }
33
+
34
+ /**
35
+ * @param {string} input
36
+ * @param {Object[]} args
37
+ * @returns {string}
38
+ */
39
+ run(input, args) {
40
+ if (!input) return ""; // Handle empty input
41
+ const lineWidth = args[0];
42
+ const regex = new RegExp(`.{1,${lineWidth}}`, "g");
43
+ return input.match(regex).join("\n");
44
+ }
45
+ }
46
+
47
+ export default Wrap;
@@ -114,6 +114,7 @@ import EncodeNetBIOSName from "./EncodeNetBIOSName.mjs";
114
114
  import EncodeText from "./EncodeText.mjs";
115
115
  import Enigma from "./Enigma.mjs";
116
116
  import Entropy from "./Entropy.mjs";
117
+ import EscapeSmartCharacters from "./EscapeSmartCharacters.mjs";
117
118
  import EscapeString from "./EscapeString.mjs";
118
119
  import EscapeUnicodeCharacters from "./EscapeUnicodeCharacters.mjs";
119
120
  import ExpandAlphabetRange from "./ExpandAlphabetRange.mjs";
@@ -283,6 +284,7 @@ import PGPDecrypt from "./PGPDecrypt.mjs";
283
284
  import PGPDecryptAndVerify from "./PGPDecryptAndVerify.mjs";
284
285
  import PGPEncrypt from "./PGPEncrypt.mjs";
285
286
  import PGPEncryptAndSign from "./PGPEncryptAndSign.mjs";
287
+ import PGPSign from "./PGPSign.mjs";
286
288
  import PGPVerify from "./PGPVerify.mjs";
287
289
  import PHPDeserialize from "./PHPDeserialize.mjs";
288
290
  import PHPSerialize from "./PHPSerialize.mjs";
@@ -325,6 +327,7 @@ import RC4Drop from "./RC4Drop.mjs";
325
327
  import RC6Decrypt from "./RC6Decrypt.mjs";
326
328
  import RC6Encrypt from "./RC6Encrypt.mjs";
327
329
  import RIPEMD from "./RIPEMD.mjs";
330
+ import ROR13 from "./ROR13.mjs";
328
331
  import ROT13 from "./ROT13.mjs";
329
332
  import ROT13BruteForce from "./ROT13BruteForce.mjs";
330
333
  import ROT47 from "./ROT47.mjs";
@@ -342,6 +345,7 @@ import RawDeflate from "./RawDeflate.mjs";
342
345
  import RawInflate from "./RawInflate.mjs";
343
346
  import Register from "./Register.mjs";
344
347
  import RegularExpression from "./RegularExpression.mjs";
348
+ import RemoveANSIEscapeCodes from "./RemoveANSIEscapeCodes.mjs";
345
349
  import RemoveDiacritics from "./RemoveDiacritics.mjs";
346
350
  import RemoveEXIF from "./RemoveEXIF.mjs";
347
351
  import RemoveLineNumbers from "./RemoveLineNumbers.mjs";
@@ -466,6 +470,7 @@ import VigenèreDecode from "./VigenèreDecode.mjs";
466
470
  import VigenèreEncode from "./VigenèreEncode.mjs";
467
471
  import Whirlpool from "./Whirlpool.mjs";
468
472
  import WindowsFiletimeToUNIXTimestamp from "./WindowsFiletimeToUNIXTimestamp.mjs";
473
+ import Wrap from "./Wrap.mjs";
469
474
  import XKCDRandomNumber from "./XKCDRandomNumber.mjs";
470
475
  import XMLBeautify from "./XMLBeautify.mjs";
471
476
  import XMLMinify from "./XMLMinify.mjs";
@@ -592,6 +597,7 @@ export {
592
597
  EncodeText,
593
598
  Enigma,
594
599
  Entropy,
600
+ EscapeSmartCharacters,
595
601
  EscapeString,
596
602
  EscapeUnicodeCharacters,
597
603
  ExpandAlphabetRange,
@@ -761,6 +767,7 @@ export {
761
767
  PGPDecryptAndVerify,
762
768
  PGPEncrypt,
763
769
  PGPEncryptAndSign,
770
+ PGPSign,
764
771
  PGPVerify,
765
772
  PHPDeserialize,
766
773
  PHPSerialize,
@@ -803,6 +810,7 @@ export {
803
810
  RC6Decrypt,
804
811
  RC6Encrypt,
805
812
  RIPEMD,
813
+ ROR13,
806
814
  ROT13,
807
815
  ROT13BruteForce,
808
816
  ROT47,
@@ -820,6 +828,7 @@ export {
820
828
  RawInflate,
821
829
  Register,
822
830
  RegularExpression,
831
+ RemoveANSIEscapeCodes,
823
832
  RemoveDiacritics,
824
833
  RemoveEXIF,
825
834
  RemoveLineNumbers,
@@ -944,6 +953,7 @@ export {
944
953
  VigenèreEncode,
945
954
  Whirlpool,
946
955
  WindowsFiletimeToUNIXTimestamp,
956
+ Wrap,
947
957
  XKCDRandomNumber,
948
958
  XMLBeautify,
949
959
  XMLMinify,
@@ -122,6 +122,7 @@ import {
122
122
  EncodeText as core_EncodeText,
123
123
  Enigma as core_Enigma,
124
124
  Entropy as core_Entropy,
125
+ EscapeSmartCharacters as core_EscapeSmartCharacters,
125
126
  EscapeString as core_EscapeString,
126
127
  EscapeUnicodeCharacters as core_EscapeUnicodeCharacters,
127
128
  ExpandAlphabetRange as core_ExpandAlphabetRange,
@@ -284,6 +285,7 @@ import {
284
285
  PGPDecryptAndVerify as core_PGPDecryptAndVerify,
285
286
  PGPEncrypt as core_PGPEncrypt,
286
287
  PGPEncryptAndSign as core_PGPEncryptAndSign,
288
+ PGPSign as core_PGPSign,
287
289
  PGPVerify as core_PGPVerify,
288
290
  PHPDeserialize as core_PHPDeserialize,
289
291
  PHPSerialize as core_PHPSerialize,
@@ -326,6 +328,7 @@ import {
326
328
  RC6Decrypt as core_RC6Decrypt,
327
329
  RC6Encrypt as core_RC6Encrypt,
328
330
  RIPEMD as core_RIPEMD,
331
+ ROR13 as core_ROR13,
329
332
  ROT13 as core_ROT13,
330
333
  ROT13BruteForce as core_ROT13BruteForce,
331
334
  ROT47 as core_ROT47,
@@ -343,6 +346,7 @@ import {
343
346
  RawInflate as core_RawInflate,
344
347
  Register as core_Register,
345
348
  RegularExpression as core_RegularExpression,
349
+ RemoveANSIEscapeCodes as core_RemoveANSIEscapeCodes,
346
350
  RemoveDiacritics as core_RemoveDiacritics,
347
351
  RemoveEXIF as core_RemoveEXIF,
348
352
  RemoveLineNumbers as core_RemoveLineNumbers,
@@ -466,6 +470,7 @@ import {
466
470
  VigenèreEncode as core_VigenèreEncode,
467
471
  Whirlpool as core_Whirlpool,
468
472
  WindowsFiletimeToUNIXTimestamp as core_WindowsFiletimeToUNIXTimestamp,
473
+ Wrap as core_Wrap,
469
474
  XKCDRandomNumber as core_XKCDRandomNumber,
470
475
  XMLBeautify as core_XMLBeautify,
471
476
  XMLMinify as core_XMLMinify,
@@ -600,6 +605,7 @@ function generateChef() {
600
605
  "encodeText": _wrap(core_EncodeText),
601
606
  "enigma": _wrap(core_Enigma),
602
607
  "entropy": _wrap(core_Entropy),
608
+ "escapeSmartCharacters": _wrap(core_EscapeSmartCharacters),
603
609
  "escapeString": _wrap(core_EscapeString),
604
610
  "escapeUnicodeCharacters": _wrap(core_EscapeUnicodeCharacters),
605
611
  "expandAlphabetRange": _wrap(core_ExpandAlphabetRange),
@@ -762,6 +768,7 @@ function generateChef() {
762
768
  "PGPDecryptAndVerify": _wrap(core_PGPDecryptAndVerify),
763
769
  "PGPEncrypt": _wrap(core_PGPEncrypt),
764
770
  "PGPEncryptAndSign": _wrap(core_PGPEncryptAndSign),
771
+ "PGPSign": _wrap(core_PGPSign),
765
772
  "PGPVerify": _wrap(core_PGPVerify),
766
773
  "PHPDeserialize": _wrap(core_PHPDeserialize),
767
774
  "PHPSerialize": _wrap(core_PHPSerialize),
@@ -804,6 +811,7 @@ function generateChef() {
804
811
  "RC6Decrypt": _wrap(core_RC6Decrypt),
805
812
  "RC6Encrypt": _wrap(core_RC6Encrypt),
806
813
  "RIPEMD": _wrap(core_RIPEMD),
814
+ "ROR13": _wrap(core_ROR13),
807
815
  "ROT13": _wrap(core_ROT13),
808
816
  "ROT13BruteForce": _wrap(core_ROT13BruteForce),
809
817
  "ROT47": _wrap(core_ROT47),
@@ -821,6 +829,7 @@ function generateChef() {
821
829
  "rawInflate": _wrap(core_RawInflate),
822
830
  "register": _wrap(core_Register),
823
831
  "regularExpression": _wrap(core_RegularExpression),
832
+ "removeANSIEscapeCodes": _wrap(core_RemoveANSIEscapeCodes),
824
833
  "removeDiacritics": _wrap(core_RemoveDiacritics),
825
834
  "removeEXIF": _wrap(core_RemoveEXIF),
826
835
  "removeLineNumbers": _wrap(core_RemoveLineNumbers),
@@ -944,6 +953,7 @@ function generateChef() {
944
953
  "vigenèreEncode": _wrap(core_VigenèreEncode),
945
954
  "whirlpool": _wrap(core_Whirlpool),
946
955
  "windowsFiletimeToUNIXTimestamp": _wrap(core_WindowsFiletimeToUNIXTimestamp),
956
+ "wrap": _wrap(core_Wrap),
947
957
  "XKCDRandomNumber": _wrap(core_XKCDRandomNumber),
948
958
  "XMLBeautify": _wrap(core_XMLBeautify),
949
959
  "XMLMinify": _wrap(core_XMLMinify),
@@ -1088,6 +1098,7 @@ const encodeNetBIOSName = chef.encodeNetBIOSName;
1088
1098
  const encodeText = chef.encodeText;
1089
1099
  const enigma = chef.enigma;
1090
1100
  const entropy = chef.entropy;
1101
+ const escapeSmartCharacters = chef.escapeSmartCharacters;
1091
1102
  const escapeString = chef.escapeString;
1092
1103
  const escapeUnicodeCharacters = chef.escapeUnicodeCharacters;
1093
1104
  const expandAlphabetRange = chef.expandAlphabetRange;
@@ -1257,6 +1268,7 @@ const PGPDecrypt = chef.PGPDecrypt;
1257
1268
  const PGPDecryptAndVerify = chef.PGPDecryptAndVerify;
1258
1269
  const PGPEncrypt = chef.PGPEncrypt;
1259
1270
  const PGPEncryptAndSign = chef.PGPEncryptAndSign;
1271
+ const PGPSign = chef.PGPSign;
1260
1272
  const PGPVerify = chef.PGPVerify;
1261
1273
  const PHPDeserialize = chef.PHPDeserialize;
1262
1274
  const PHPSerialize = chef.PHPSerialize;
@@ -1299,6 +1311,7 @@ const RC4Drop = chef.RC4Drop;
1299
1311
  const RC6Decrypt = chef.RC6Decrypt;
1300
1312
  const RC6Encrypt = chef.RC6Encrypt;
1301
1313
  const RIPEMD = chef.RIPEMD;
1314
+ const ROR13 = chef.ROR13;
1302
1315
  const ROT13 = chef.ROT13;
1303
1316
  const ROT13BruteForce = chef.ROT13BruteForce;
1304
1317
  const ROT47 = chef.ROT47;
@@ -1316,6 +1329,7 @@ const rawDeflate = chef.rawDeflate;
1316
1329
  const rawInflate = chef.rawInflate;
1317
1330
  const register = chef.register;
1318
1331
  const regularExpression = chef.regularExpression;
1332
+ const removeANSIEscapeCodes = chef.removeANSIEscapeCodes;
1319
1333
  const removeDiacritics = chef.removeDiacritics;
1320
1334
  const removeEXIF = chef.removeEXIF;
1321
1335
  const removeLineNumbers = chef.removeLineNumbers;
@@ -1440,6 +1454,7 @@ const vigenèreDecode = chef.vigenèreDecode;
1440
1454
  const vigenèreEncode = chef.vigenèreEncode;
1441
1455
  const whirlpool = chef.whirlpool;
1442
1456
  const windowsFiletimeToUNIXTimestamp = chef.windowsFiletimeToUNIXTimestamp;
1457
+ const wrap = chef.wrap;
1443
1458
  const XKCDRandomNumber = chef.XKCDRandomNumber;
1444
1459
  const XMLBeautify = chef.XMLBeautify;
1445
1460
  const XMLMinify = chef.XMLMinify;
@@ -1568,6 +1583,7 @@ const operations = [
1568
1583
  encodeText,
1569
1584
  enigma,
1570
1585
  entropy,
1586
+ escapeSmartCharacters,
1571
1587
  escapeString,
1572
1588
  escapeUnicodeCharacters,
1573
1589
  expandAlphabetRange,
@@ -1737,6 +1753,7 @@ const operations = [
1737
1753
  PGPDecryptAndVerify,
1738
1754
  PGPEncrypt,
1739
1755
  PGPEncryptAndSign,
1756
+ PGPSign,
1740
1757
  PGPVerify,
1741
1758
  PHPDeserialize,
1742
1759
  PHPSerialize,
@@ -1779,6 +1796,7 @@ const operations = [
1779
1796
  RC6Decrypt,
1780
1797
  RC6Encrypt,
1781
1798
  RIPEMD,
1799
+ ROR13,
1782
1800
  ROT13,
1783
1801
  ROT13BruteForce,
1784
1802
  ROT47,
@@ -1796,6 +1814,7 @@ const operations = [
1796
1814
  rawInflate,
1797
1815
  register,
1798
1816
  regularExpression,
1817
+ removeANSIEscapeCodes,
1799
1818
  removeDiacritics,
1800
1819
  removeEXIF,
1801
1820
  removeLineNumbers,
@@ -1920,6 +1939,7 @@ const operations = [
1920
1939
  vigenèreEncode,
1921
1940
  whirlpool,
1922
1941
  windowsFiletimeToUNIXTimestamp,
1942
+ wrap,
1923
1943
  XKCDRandomNumber,
1924
1944
  XMLBeautify,
1925
1945
  XMLMinify,
@@ -2052,6 +2072,7 @@ export {
2052
2072
  encodeText,
2053
2073
  enigma,
2054
2074
  entropy,
2075
+ escapeSmartCharacters,
2055
2076
  escapeString,
2056
2077
  escapeUnicodeCharacters,
2057
2078
  expandAlphabetRange,
@@ -2221,6 +2242,7 @@ export {
2221
2242
  PGPDecryptAndVerify,
2222
2243
  PGPEncrypt,
2223
2244
  PGPEncryptAndSign,
2245
+ PGPSign,
2224
2246
  PGPVerify,
2225
2247
  PHPDeserialize,
2226
2248
  PHPSerialize,
@@ -2263,6 +2285,7 @@ export {
2263
2285
  RC6Decrypt,
2264
2286
  RC6Encrypt,
2265
2287
  RIPEMD,
2288
+ ROR13,
2266
2289
  ROT13,
2267
2290
  ROT13BruteForce,
2268
2291
  ROT47,
@@ -2280,6 +2303,7 @@ export {
2280
2303
  rawInflate,
2281
2304
  register,
2282
2305
  regularExpression,
2306
+ removeANSIEscapeCodes,
2283
2307
  removeDiacritics,
2284
2308
  removeEXIF,
2285
2309
  removeLineNumbers,
@@ -2404,6 +2428,7 @@ export {
2404
2428
  vigenèreEncode,
2405
2429
  whirlpool,
2406
2430
  windowsFiletimeToUNIXTimestamp,
2431
+ wrap,
2407
2432
  XKCDRandomNumber,
2408
2433
  XMLBeautify,
2409
2434
  XMLMinify,
package/src/web/App.mjs CHANGED
@@ -42,6 +42,14 @@ class App {
42
42
  this.progress = 0;
43
43
  this.ingId = 0;
44
44
 
45
+ // stateChangeId increments on every statechange dispatch; bakeStateId records
46
+ // the stateChangeId captured when the most recent bake started. autoBake uses
47
+ // these to decide whether the output is genuinely stale, so a debounced
48
+ // autoBake firing shortly after a manual bake doesn't clobber the bake's
49
+ // hideStaleIndicator with a redundant showStaleIndicator.
50
+ this.stateChangeId = 0;
51
+ this.bakeStateId = -1;
52
+
45
53
  this.appLoaded = false;
46
54
  this.workerLoaded = false;
47
55
  this.waitersLoaded = false;
@@ -136,6 +144,9 @@ class App {
136
144
  bake(step=false) {
137
145
  if (this.baking) return;
138
146
 
147
+ // Record which state version this bake is covering.
148
+ this.bakeStateId = this.stateChangeId;
149
+
139
150
  // Reset attemptHighlight flag
140
151
  this.options.attemptHighlight = true;
141
152
 
@@ -166,7 +177,10 @@ class App {
166
177
  nums: [this.manager.tabs.getActiveTab("input")],
167
178
  step: false
168
179
  });
169
- } else {
180
+ } else if (this.bakeStateId < this.stateChangeId) {
181
+ // Only show stale-indicator if the most recent bake didn't cover the
182
+ // current state. Without this guard, a debounced autoBake firing after
183
+ // a manual bake completed would re-show the indicator on fresh output.
170
184
  this.manager.controls.showStaleIndicator();
171
185
  }
172
186
  }
@@ -768,6 +782,10 @@ class App {
768
782
  * @param {event} e
769
783
  */
770
784
  stateChange(e) {
785
+ // Bump the state-change counter synchronously so a manual bake invoked between
786
+ // here and the debounced autoBake firing can record it via bakeStateId.
787
+ this.stateChangeId++;
788
+
771
789
  debounce(function() {
772
790
  this.progress = 0;
773
791
  this.autoBake();
@@ -166,6 +166,7 @@ class HTMLIngredient {
166
166
  id="${this.id}"
167
167
  tabindex="${this.tabIndex}"
168
168
  arg-name="${this.name}"
169
+ data-target="${this.target}"
169
170
  ${this.disabled ? "disabled" : ""}>`;
170
171
  for (i = 0; i < this.value.length; i++) {
171
172
  if ((m = this.value[i].name.match(/\[([a-z0-9 -()^]+)\]/i))) {
@@ -868,15 +868,15 @@
868
868
  Be aware that the standalone version will never update itself, meaning it will not receive bug fixes or new features until you re-download newer versions manually.
869
869
  </p>
870
870
 
871
- <h6>CyberChef v<%= htmlWebpackPlugin.options.version %></h6>
871
+ <h6>CyberChef <%= htmlWebpackPlugin.options.version %></h6>
872
872
  <ul>
873
873
  <li>Build time: <%= htmlWebpackPlugin.options.compileTime %></li>
874
- <li>The changelog for this version can be viewed <a href="https://github.com/gchq/CyberChef/blob/v<%= htmlWebpackPlugin.options.version %>/CHANGELOG.md">here</a></li>
874
+ <li>The changelog for this version can be viewed <a href="https://github.com/gchq/CyberChef/blob/v<%= htmlWebpackPlugin.options.latestReleaseVersion %>/CHANGELOG.md">here</a></li>
875
875
  <li>&copy; Crown Copyright 2016-<%= htmlWebpackPlugin.options.compileYear %></li>
876
876
  <li>Released under the Apache Licence, Version 2.0</li>
877
877
  <li>SHA256 hash: DOWNLOAD_HASH_PLACEHOLDER</li>
878
878
  </ul>
879
- <a href="CyberChef_v<%= htmlWebpackPlugin.options.version %>.zip" download class="btn btn-outline-primary">Download ZIP file</a>
879
+ <a href="<%= htmlWebpackPlugin.options.downloadZipFilename %>" download class="btn btn-outline-primary">Download ZIP file</a>
880
880
  </div>
881
881
  <div class="modal-footer">
882
882
  <button type="button" class="btn btn-primary" data-dismiss="modal">Ok</button>
@@ -1,4 +1,4 @@
1
- import sm from "sitemap";
1
+ import { SitemapStream, streamToPromise } from "sitemap";
2
2
  import OperationConfig from "../../core/config/OperationConfig.json" with { type: "json" };
3
3
 
4
4
  /**
@@ -11,7 +11,7 @@ import OperationConfig from "../../core/config/OperationConfig.json" with { type
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
@@ -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";
@@ -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
+ ]);