cyberchef 11.0.0 → 11.2.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.
- package/CHANGELOG.md +170 -1
- package/Dockerfile +2 -2
- package/Gruntfile.js +10 -5
- package/README.md +10 -7
- package/SECURITY.md +11 -19
- package/package.json +35 -34
- package/src/core/Chef.mjs +2 -0
- package/src/core/config/Categories.json +6 -0
- package/src/core/config/OperationConfig.json +144 -19
- package/src/core/config/modules/Default.mjs +8 -0
- package/src/core/config/modules/PGP.mjs +2 -0
- package/src/core/config/scripts/generateOpsIndex.mjs +63 -0
- package/src/core/config/scripts/newOperation.mjs +31 -4
- package/src/core/dishTypes/DishType.mjs +1 -1
- package/src/core/errors/ExcludedOperationError.mjs +1 -1
- package/src/core/lib/Charts.mjs +3 -3
- package/src/core/lib/Protocol.mjs +8 -6
- package/src/core/operations/AESDecrypt.mjs +61 -16
- package/src/core/operations/AESEncrypt.mjs +26 -11
- package/src/core/operations/BLAKE3.mjs +13 -7
- package/src/core/operations/BSONDeserialise.mjs +2 -2
- package/src/core/operations/BSONSerialise.mjs +3 -2
- package/src/core/operations/Bcrypt.mjs +1 -1
- package/src/core/operations/BcryptCompare.mjs +1 -1
- package/src/core/operations/DecodeText.mjs +4 -0
- package/src/core/operations/EncodeText.mjs +4 -0
- package/src/core/operations/EscapeSmartCharacters.mjs +129 -0
- package/src/core/operations/GenerateDeBruijnSequence.mjs +8 -0
- package/src/core/operations/GenerateLoremIpsum.mjs +34 -3
- package/src/core/operations/GeneratePGPKeyPair.mjs +8 -7
- package/src/core/operations/PGPSign.mjs +83 -0
- package/src/core/operations/ParityBit.mjs +1 -1
- package/src/core/operations/ParseEthernetFrame.mjs +1 -1
- package/src/core/operations/ParseIPv4Header.mjs +2 -2
- package/src/core/operations/ParseObjectIDTimestamp.mjs +2 -2
- package/src/core/operations/ParseUserAgent.mjs +1 -1
- package/src/core/operations/ROR13.mjs +83 -0
- package/src/core/operations/RemoveANSIEscapeCodes.mjs +41 -0
- package/src/core/operations/SHA2.mjs +1 -1
- package/src/core/operations/SeriesChart.mjs +16 -0
- package/src/core/operations/ToHexdump.mjs +7 -1
- package/src/core/operations/Wrap.mjs +47 -0
- package/src/core/operations/XORChecksum.mjs +10 -3
- package/src/core/operations/index.mjs +10 -0
- package/src/node/index.mjs +25 -0
- package/src/web/App.mjs +19 -1
- package/src/web/HTMLIngredient.mjs +1 -0
- package/src/web/html/index.html +3 -3
- package/src/web/static/sitemap.mjs +3 -3
- package/src/web/waiters/RecipeWaiter.mjs +9 -1
- package/tests/browser/02_ops.js +7 -7
- package/tests/browser/03_recipe_load.js +48 -0
- package/tests/browser/browserUtils.js +6 -3
- package/tests/node/index.mjs +2 -0
- package/tests/node/tests/PGP.mjs +69 -0
- package/tests/node/tests/lib/ChartsProtocolPrototypePollution.mjs +90 -0
- package/tests/node/tests/operations.mjs +41 -2
- package/tests/operations/index.mjs +71 -66
- package/tests/operations/tests/BLAKE3.mjs +18 -0
- package/tests/operations/tests/CharEnc.mjs +26 -0
- package/tests/operations/tests/Charts.mjs +11 -0
- package/tests/operations/tests/Crypt.mjs +288 -62
- package/tests/operations/tests/EscapeSmartCharacters.mjs +132 -0
- package/tests/operations/tests/FlaskSession.mjs +11 -8
- package/tests/operations/tests/GenerateLoremIpsum.mjs +80 -0
- package/tests/operations/tests/Hexdump.mjs +11 -0
- package/tests/operations/tests/IPv6Transition.mjs +4 -4
- package/tests/operations/tests/PGP.mjs +178 -154
- package/tests/operations/tests/ParseEthernetFrame.mjs +11 -0
- package/tests/operations/tests/ParseIPv4Header.mjs +34 -0
- package/tests/operations/tests/ParseX509CRL.mjs +16 -16
- package/tests/operations/tests/ROR13.mjs +45 -0
- package/tests/operations/tests/Register.mjs +3 -1
- package/tests/operations/tests/RemoveANSIEscapeCodes.mjs +62 -0
- package/tests/operations/tests/Wrap.mjs +44 -0
package/src/web/html/index.html
CHANGED
|
@@ -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
|
|
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.
|
|
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>© 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="
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
}
|
package/tests/browser/02_ops.js
CHANGED
|
@@ -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", /^\$
|
|
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
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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",
|
|
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",
|
|
61
|
-
.waitForElementNotVisible("#output-loader",
|
|
63
|
+
.waitForElementNotVisible("#stale-indicator", 10000)
|
|
64
|
+
.waitForElementNotVisible("#output-loader", 10000);
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
/** @function
|
package/tests/node/index.mjs
CHANGED
|
@@ -18,12 +18,14 @@ 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";
|
|
24
25
|
import "./tests/Utils.mjs";
|
|
25
26
|
import "./tests/Categories.mjs";
|
|
26
27
|
import "./tests/lib/BigIntUtils.mjs";
|
|
28
|
+
import "./tests/lib/ChartsProtocolPrototypePollution.mjs";
|
|
27
29
|
|
|
28
30
|
const testStatus = {
|
|
29
31
|
allTestsPassing: true,
|
|
@@ -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
|
+
]);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import TestRegister from "../../../lib/TestRegister.mjs";
|
|
2
|
+
import {getSeriesValues} from "../../../../src/core/lib/Charts.mjs";
|
|
3
|
+
import {objToTable} from "../../../../src/core/lib/Protocol.mjs";
|
|
4
|
+
import SeriesChart from "../../../../src/core/operations/SeriesChart.mjs";
|
|
5
|
+
import ParseUDP from "../../../../src/core/operations/ParseUDP.mjs";
|
|
6
|
+
import it from "../../assertionHandler.mjs";
|
|
7
|
+
import assert from "assert";
|
|
8
|
+
|
|
9
|
+
const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
|
|
10
|
+
|
|
11
|
+
TestRegister.addApiTests([
|
|
12
|
+
it("Charts: should not pollute Object.prototype from a __proto__ series name", () => {
|
|
13
|
+
const xVal = "<img src=x onerror=alert(1)>";
|
|
14
|
+
delete Object.prototype[xVal];
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const result = getSeriesValues(`__proto__,${xVal},1`, "\n", ",", false);
|
|
18
|
+
|
|
19
|
+
assert.equal(Object.prototype[xVal], undefined);
|
|
20
|
+
assert.deepEqual(result.xValues, [xVal]);
|
|
21
|
+
assert.equal(result.series.length, 1);
|
|
22
|
+
assert.equal(result.series[0].name, "__proto__");
|
|
23
|
+
assert.equal(Object.getPrototypeOf(result.series[0].data), null);
|
|
24
|
+
assert(hasOwn(result.series[0].data, xVal));
|
|
25
|
+
assert.equal(result.series[0].data[xVal], 1);
|
|
26
|
+
} finally {
|
|
27
|
+
delete Object.prototype[xVal];
|
|
28
|
+
}
|
|
29
|
+
}),
|
|
30
|
+
|
|
31
|
+
it("Charts: should keep __proto__ x-axis names as own data keys", () => {
|
|
32
|
+
const result = getSeriesValues("safe,__proto__,1", "\n", ",", false);
|
|
33
|
+
|
|
34
|
+
assert.equal(result.series.length, 1);
|
|
35
|
+
assert.equal(Object.getPrototypeOf(result.series[0].data), null);
|
|
36
|
+
assert(hasOwn(result.series[0].data, "__proto__"));
|
|
37
|
+
assert.equal(result.series[0].data.__proto__, 1);
|
|
38
|
+
}),
|
|
39
|
+
|
|
40
|
+
it("Protocol: should ignore inherited properties when rendering tables", () => {
|
|
41
|
+
const inheritedKey = "<img src=x onerror=alert(1)>";
|
|
42
|
+
delete Object.prototype[inheritedKey];
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
Object.prototype[inheritedKey] = "polluted";
|
|
46
|
+
|
|
47
|
+
const html = objToTable({safe: "value"});
|
|
48
|
+
|
|
49
|
+
assert(!html.includes(inheritedKey));
|
|
50
|
+
assert(!html.includes("polluted"));
|
|
51
|
+
assert(html.includes("safe"));
|
|
52
|
+
assert(html.includes("value"));
|
|
53
|
+
} finally {
|
|
54
|
+
delete Object.prototype[inheritedKey];
|
|
55
|
+
}
|
|
56
|
+
}),
|
|
57
|
+
|
|
58
|
+
it("Protocol: should escape table keys and scalar values", () => {
|
|
59
|
+
const obj = {
|
|
60
|
+
"<b>field</b>": "<img src=x onerror=alert(1)>",
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const html = objToTable(obj);
|
|
64
|
+
|
|
65
|
+
assert(!html.includes("<b>field</b>"));
|
|
66
|
+
assert(!html.includes("<img src=x onerror=alert(1)>"));
|
|
67
|
+
assert(html.includes("<b>field</b>"));
|
|
68
|
+
assert(html.includes("<img src=x onerror=alert(1)>"));
|
|
69
|
+
}),
|
|
70
|
+
|
|
71
|
+
it("Series chart and Parse UDP: should not expose polluted prototype data as HTML", () => {
|
|
72
|
+
const xVal = "<img src=x onerror=alert(document.domain)>";
|
|
73
|
+
delete Object.prototype[xVal];
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const chartHtml = new SeriesChart().run(
|
|
77
|
+
`__proto__,${xVal},1`,
|
|
78
|
+
["Line feed", "Comma", "", 1, "red"]
|
|
79
|
+
);
|
|
80
|
+
assert.equal(Object.prototype[xVal], undefined);
|
|
81
|
+
|
|
82
|
+
const parseUDP = new ParseUDP();
|
|
83
|
+
const tableHtml = parseUDP.present(parseUDP.run(chartHtml, ["Raw"]));
|
|
84
|
+
|
|
85
|
+
assert(!/<img|onerror|alert\(/.test(tableHtml));
|
|
86
|
+
} finally {
|
|
87
|
+
delete Object.prototype[xVal];
|
|
88
|
+
}
|
|
89
|
+
}),
|
|
90
|
+
]);
|
|
@@ -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.
|
|
140
|
-
assert.equal(strResult.
|
|
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() => {
|