mirror of
https://github.com/gchq/CyberChef
synced 2025-12-05 23:53:27 +00:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5893ac1a37 | ||
|
|
83c3ab97f9 | ||
|
|
9b6be140fa | ||
|
|
a6a60392c2 | ||
|
|
ccfa0b991e | ||
|
|
73b0e68993 | ||
|
|
31a4eef001 | ||
|
|
d6e2c9a6b9 | ||
|
|
e069f5db13 | ||
|
|
96b59cf0df | ||
|
|
c1e1d4b7e3 | ||
|
|
32d869231e | ||
|
|
6f95f01dda | ||
|
|
61a1c44f26 | ||
|
|
e6c7899569 | ||
|
|
a74a14145e | ||
|
|
04022b22be | ||
|
|
4f3010691c | ||
|
|
672b477751 | ||
|
|
19360391a6 | ||
|
|
447be8af34 | ||
|
|
0989550e5c | ||
|
|
4d9b48b4d8 | ||
|
|
979652387d | ||
|
|
de84fbdd1c | ||
|
|
170e564319 | ||
|
|
530836876f | ||
|
|
1abc46058c | ||
|
|
892a3716ed | ||
|
|
5d982a9c8d | ||
|
|
6206224f1e | ||
|
|
766310e2c7 | ||
|
|
02a397d2ae | ||
|
|
4563c86acd | ||
|
|
0a59f8068e | ||
|
|
24548e3a48 | ||
|
|
f4784d49e7 | ||
|
|
14d5069c6e | ||
|
|
9fdd55c5c6 | ||
|
|
5bc523aeff | ||
|
|
3ae2e2e2c8 | ||
|
|
83e49da7f6 | ||
|
|
fe6df8778f | ||
|
|
69073c9d99 | ||
|
|
d5a0adea0c | ||
|
|
1bcb8e433d | ||
|
|
fa05cf1d78 | ||
|
|
63dff0d34d | ||
|
|
e228b197f9 | ||
|
|
4bbeb6caa3 | ||
|
|
139d25dff9 | ||
|
|
6984258404 | ||
|
|
ba66fd6546 | ||
|
|
47bbefd81f | ||
|
|
50f796049c | ||
|
|
618da545b1 | ||
|
|
21236f1938 | ||
|
|
4169a15066 | ||
|
|
6b10f61e11 | ||
|
|
887ea0cf06 | ||
|
|
3e0525ee9e | ||
|
|
7526f4d7b1 | ||
|
|
cd8a85975c | ||
|
|
2f94ec20b0 | ||
|
|
09d9deae43 | ||
|
|
bda36e508a | ||
|
|
d2ea1273da | ||
|
|
2e0af64ac3 | ||
|
|
30bc8dfbe9 | ||
|
|
53a579028c | ||
|
|
3a2580fbc2 |
@@ -63,7 +63,8 @@
|
||||
}],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"quotes": ["error", "double", {
|
||||
"avoidEscape": true
|
||||
"avoidEscape": true,
|
||||
"allowTemplateLiterals": true
|
||||
}],
|
||||
"camelcase": ["error", {
|
||||
"properties": "always"
|
||||
|
||||
32
.github/workflows/codeql.yml
vendored
Normal file
32
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: "CodeQL Analysis"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '22 17 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
2
.github/workflows/master.yml
vendored
2
.github/workflows/master.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Master
|
||||
name: "Master Build, Test & Deploy"
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
2
.github/workflows/pull_requests.yml
vendored
2
.github/workflows/pull_requests.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: PRs
|
||||
name: "Pull Requests"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
3
.github/workflows/releases.yml
vendored
3
.github/workflows/releases.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Release
|
||||
name: "Releases"
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -47,6 +47,7 @@ jobs:
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
body: "See the [CHANGELOG](https://github.com/gchq/CyberChef/blob/master/CHANGELOG.md) and [commit messages](https://github.com/gchq/CyberChef/commits/master) for details."
|
||||
|
||||
- name: Publish to NPM
|
||||
if: success()
|
||||
|
||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -2,7 +2,7 @@
|
||||
|
||||
## Versioning
|
||||
|
||||
CyberChef uses the [semver](https://semver.org/) system to manage versioning: MAJOR.MINOR.PATCH.
|
||||
CyberChef uses the [semver](https://semver.org/) system to manage versioning: `<MAJOR>.<MINOR>.<PATCH>`.
|
||||
|
||||
- MAJOR version changes represent a significant change to the fundamental architecture of CyberChef and may (but don't always) make breaking changes that are not backwards compatible.
|
||||
- MINOR version changes usually mean the addition of new operations or reasonably significant new features.
|
||||
@@ -13,10 +13,16 @@ All major and minor version changes will be documented in this file. Details of
|
||||
|
||||
## Details
|
||||
|
||||
### [9.24.0] - 2020-02-02
|
||||
### [9.26.0] - 2021-02-11
|
||||
- 'Get Time' operation added [@n1073645] [@n1474335] | [#1045]
|
||||
|
||||
### [9.25.0] - 2021-02-11
|
||||
- 'Extract ID3' operation added [@n1073645] [@n1474335] | [#1006]
|
||||
|
||||
### [9.24.0] - 2021-02-02
|
||||
- 'SM3' hashing function added along with more configuration options for other hashing operations [@n1073645] [@n1474335] | [#1022]
|
||||
|
||||
### [9.23.0] - 2020-02-01
|
||||
### [9.23.0] - 2021-02-01
|
||||
- Various RSA operations added to encrypt, decrypt, sign, verify and generate keys [@mattnotmitt] [@GCHQ77703] | [#652]
|
||||
|
||||
### [9.22.0] - 2021-02-01
|
||||
@@ -247,6 +253,8 @@ All major and minor version changes will be documented in this file. Details of
|
||||
|
||||
|
||||
|
||||
[9.26.0]: https://github.com/gchq/CyberChef/releases/tag/v9.26.0
|
||||
[9.25.0]: https://github.com/gchq/CyberChef/releases/tag/v9.25.0
|
||||
[9.24.0]: https://github.com/gchq/CyberChef/releases/tag/v9.24.0
|
||||
[9.23.0]: https://github.com/gchq/CyberChef/releases/tag/v9.23.0
|
||||
[9.22.0]: https://github.com/gchq/CyberChef/releases/tag/v9.22.0
|
||||
@@ -424,6 +432,8 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[#965]: https://github.com/gchq/CyberChef/pull/965
|
||||
[#966]: https://github.com/gchq/CyberChef/pull/966
|
||||
[#987]: https://github.com/gchq/CyberChef/pull/987
|
||||
[#1006]: https://github.com/gchq/CyberChef/pull/1006
|
||||
[#1022]: https://github.com/gchq/CyberChef/pull/1022
|
||||
[#1045]: https://github.com/gchq/CyberChef/pull/1045
|
||||
[#1049]: https://github.com/gchq/CyberChef/pull/1049
|
||||
[#1083]: https://github.com/gchq/CyberChef/pull/1083
|
||||
12
Gruntfile.js
12
Gruntfile.js
@@ -174,7 +174,7 @@ module.exports = function (grunt) {
|
||||
// previous one fails. & would coninue on a fail
|
||||
.join("&&")
|
||||
// Windows does not support \n properly
|
||||
.replace("\n", "\\n");
|
||||
.replace(/\n/g, "\\n");
|
||||
}
|
||||
|
||||
grunt.initConfig({
|
||||
@@ -411,6 +411,16 @@ module.exports = function (grunt) {
|
||||
]),
|
||||
stdout: false,
|
||||
},
|
||||
fixCryptoApiImports: {
|
||||
command: [
|
||||
`[[ "$OSTYPE" == "darwin"* ]]`,
|
||||
"&&",
|
||||
`find ./node_modules/crypto-api/src/ \\( -type d -name .git -prune \\) -o -type f -print0 | xargs -0 sed -i '' -e '/\\.mjs/!s/\\(from "\\.[^"]*\\)";/\\1.mjs";/g'`,
|
||||
"||",
|
||||
`find ./node_modules/crypto-api/src/ \\( -type d -name .git -prune \\) -o -type f -print0 | xargs -0 sed -i -e '/\\.mjs/!s/\\(from "\\.[^"]*\\)";/\\1.mjs";/g'`
|
||||
].join(" "),
|
||||
stdout: false
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# CyberChef
|
||||
|
||||
[](https://travis-ci.org/gchq/CyberChef)
|
||||
[](https://github.com/gchq/CyberChef/actions?query=workflow%3A%22Master+Build%2C+Test+%26+Deploy%22)
|
||||
[](https://lgtm.com/projects/g/gchq/CyberChef/context:javascript)
|
||||
[](https://david-dm.org/gchq/CyberChef)
|
||||
[](https://www.npmjs.com/package/cyberchef)
|
||||
[](https://github.com/gchq/CyberChef/blob/master/LICENSE)
|
||||
|
||||
2633
package-lock.json
generated
2633
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
58
package.json
58
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "9.24.2",
|
||||
"version": "9.26.3",
|
||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||
"author": "n1474335 <n1474335@gmail.com>",
|
||||
"homepage": "https://gchq.github.io/CyberChef",
|
||||
@@ -36,9 +36,9 @@
|
||||
"node >= 10"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.10",
|
||||
"@babel/plugin-transform-runtime": "^7.12.10",
|
||||
"@babel/preset-env": "^7.12.11",
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/plugin-transform-runtime": "^7.12.15",
|
||||
"@babel/preset-env": "^7.12.16",
|
||||
"autoprefixer": "^10.2.4",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.2.2",
|
||||
@@ -47,7 +47,7 @@
|
||||
"cli-progress": "^3.9.0",
|
||||
"colors": "^1.4.0",
|
||||
"copy-webpack-plugin": "^7.0.0",
|
||||
"css-loader": "^5.0.1",
|
||||
"css-loader": "^5.0.2",
|
||||
"eslint": "^7.19.0",
|
||||
"exports-loader": "^2.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
@@ -62,30 +62,30 @@
|
||||
"grunt-exec": "~3.0.0",
|
||||
"grunt-webpack": "^4.0.2",
|
||||
"grunt-zip": "^0.18.2",
|
||||
"html-webpack-plugin": "^4.5.1",
|
||||
"html-webpack-plugin": "^5.1.0",
|
||||
"imports-loader": "^2.0.0",
|
||||
"mini-css-extract-plugin": "^1.3.5",
|
||||
"mini-css-extract-plugin": "^1.3.6",
|
||||
"nightwatch": "^1.5.1",
|
||||
"node-sass": "^5.0.0",
|
||||
"postcss": "^8.2.4",
|
||||
"postcss": "^8.2.6",
|
||||
"postcss-css-variables": "^0.17.0",
|
||||
"postcss-import": "^14.0.0",
|
||||
"postcss-loader": "^4.2.0",
|
||||
"postcss-loader": "^5.0.0",
|
||||
"prompt": "^1.1.0",
|
||||
"sass-loader": "^10.1.1",
|
||||
"sitemap": "^6.3.5",
|
||||
"sass-loader": "^11.0.1",
|
||||
"sitemap": "^6.3.6",
|
||||
"style-loader": "^2.0.0",
|
||||
"svg-url-loader": "^7.1.1",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.19.0",
|
||||
"webpack": "^5.21.2",
|
||||
"webpack-bundle-analyzer": "^4.4.0",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"webpack-node-externals": "^2.5.2",
|
||||
"worker-loader": "^3.0.7"
|
||||
"worker-loader": "^3.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"arrive": "^2.4.1",
|
||||
"avsc": "^5.5.3",
|
||||
"babel-plugin-transform-builtin-extend": "1.1.2",
|
||||
@@ -115,8 +115,8 @@
|
||||
"exif-parser": "^0.1.12",
|
||||
"file-saver": "^2.0.5",
|
||||
"flat": "^5.0.2",
|
||||
"geodesy": "^1.1.3",
|
||||
"highlight.js": "^10.5.0",
|
||||
"geodesy": "1.1.3",
|
||||
"highlight.js": "^10.6.0",
|
||||
"jimp": "^0.16.1",
|
||||
"jquery": "3.5.1",
|
||||
"js-crc": "^0.2.0",
|
||||
@@ -125,7 +125,7 @@
|
||||
"jsonpath": "^1.1.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jsqr": "^1.3.1",
|
||||
"jsrsasign": "10.1.5",
|
||||
"jsrsasign": "10.1.8",
|
||||
"kbpgp": "2.1.15",
|
||||
"libbzip2-wasm": "0.0.4",
|
||||
"libyara-wasm": "^1.1.0",
|
||||
@@ -134,14 +134,14 @@
|
||||
"loglevel-message-prefix": "^3.0.0",
|
||||
"markdown-it": "^12.0.4",
|
||||
"moment": "^2.29.1",
|
||||
"moment-timezone": "^0.5.32",
|
||||
"moment-timezone": "^0.5.33",
|
||||
"ngeohash": "^0.6.3",
|
||||
"node-forge": "^0.10.0",
|
||||
"node-md6": "^0.1.0",
|
||||
"nodom": "^2.4.0",
|
||||
"notepack.io": "^2.3.0",
|
||||
"nwmatcher": "^1.4.4",
|
||||
"otp": "^0.1.3",
|
||||
"otp": "0.1.3",
|
||||
"path": "^0.12.7",
|
||||
"popper.js": "^1.16.1",
|
||||
"process": "^0.11.10",
|
||||
@@ -153,26 +153,26 @@
|
||||
"ssdeep.js": "0.0.2",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"terser": "^5.5.1",
|
||||
"tesseract.js": "^2.1.1",
|
||||
"ua-parser-js": "^0.7.23",
|
||||
"tesseract.js": "2.1.1",
|
||||
"ua-parser-js": "^0.7.24",
|
||||
"unorm": "^1.6.0",
|
||||
"utf8": "^3.0.0",
|
||||
"vkbeautify": "^0.99.3",
|
||||
"xmldom": "^0.4.0",
|
||||
"xpath": "0.0.32",
|
||||
"xregexp": "^4.4.1",
|
||||
"xregexp": "^5.0.1",
|
||||
"zlibjs": "^0.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "grunt dev",
|
||||
"build": "grunt prod",
|
||||
"start": "npx grunt dev",
|
||||
"build": "npx grunt prod",
|
||||
"repl": "node src/node/repl.js",
|
||||
"test": "grunt configTests && node --experimental-modules --no-warnings --no-deprecation tests/node/index.mjs && node --experimental-modules --no-warnings --no-deprecation tests/operations/index.mjs",
|
||||
"test-node-consumer": "grunt testnodeconsumer",
|
||||
"testui": "grunt testui",
|
||||
"test": "npx grunt configTests && node --experimental-modules --no-warnings --no-deprecation tests/node/index.mjs && node --experimental-modules --no-warnings --no-deprecation tests/operations/index.mjs",
|
||||
"test-node-consumer": "npx grunt testnodeconsumer",
|
||||
"testui": "npx grunt testui",
|
||||
"testuidev": "npx nightwatch --env=dev",
|
||||
"lint": "grunt lint",
|
||||
"postinstall": "bash postinstall.sh",
|
||||
"lint": "npx grunt lint",
|
||||
"postinstall": "npx grunt exec:fixCryptoApiImports",
|
||||
"newop": "node --experimental-modules src/core/config/scripts/newOperation.mjs"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Add file extensions to Crypto-Api imports
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
find ./node_modules/crypto-api/src/ \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i '' -e '/\.mjs/!s/\(from "\.[^"]*\)";/\1.mjs";/g'
|
||||
else
|
||||
find ./node_modules/crypto-api/src/ \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i -e '/\.mjs/!s/\(from "\.[^"]*\)";/\1.mjs";/g'
|
||||
fi
|
||||
@@ -212,7 +212,7 @@ self.loadRequiredModules = function(recipeConfig) {
|
||||
if (!(module in OpModules)) {
|
||||
log.info(`Loading ${module} module`);
|
||||
self.sendStatusMessage(`Loading ${module} module`);
|
||||
self.importScripts(`${self.docURL}/modules/${module}.js`);
|
||||
self.importScripts(`${self.docURL}/modules/${module}.js`); // lgtm [js/client-side-unvalidated-url-redirection]
|
||||
self.sendStatusMessage("");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -207,7 +207,7 @@ class Dish {
|
||||
const data = new Uint8Array(this.value.slice(0, 2048)),
|
||||
types = detectFileType(data);
|
||||
|
||||
if (!types.length || !types[0].mime || !types[0].mime === "text/plain") {
|
||||
if (!types.length || !types[0].mime || !(types[0].mime === "text/plain")) {
|
||||
return null;
|
||||
} else {
|
||||
return types[0].mime;
|
||||
|
||||
@@ -170,13 +170,18 @@ class Utils {
|
||||
*
|
||||
* @param {string} str - The input string to display.
|
||||
* @param {boolean} [preserveWs=false] - Whether or not to print whitespace.
|
||||
* @param {boolean} [onlyAscii=false] - Whether or not to replace non ASCII characters.
|
||||
* @returns {string}
|
||||
*/
|
||||
static printable(str, preserveWs=false) {
|
||||
static printable(str, preserveWs=false, onlyAscii=false) {
|
||||
if (isWebEnvironment() && window.app && !window.app.options.treatAsUtf8) {
|
||||
str = Utils.byteArrayToChars(Utils.strToByteArray(str));
|
||||
}
|
||||
|
||||
if (onlyAscii) {
|
||||
return str.replace(/[^\x20-\x7f]/g, ".");
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-misleading-character-class
|
||||
const re = /[\0-\x08\x0B-\x0C\x0E-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uD7FF\uE000-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/g;
|
||||
const wsRe = /[\x09-\x10\x0D\u2028\u2029]/g;
|
||||
@@ -704,8 +709,21 @@ class Utils {
|
||||
* Utils.stripHtmlTags("<div>Test</div>");
|
||||
*/
|
||||
static stripHtmlTags(htmlStr, removeScriptAndStyle=false) {
|
||||
/**
|
||||
* Recursively remove a pattern from a string until there are no more matches.
|
||||
* Avoids incomplete sanitization e.g. "aabcbc".replace(/abc/g, "") === "abc"
|
||||
*
|
||||
* @param {RegExp} pattern
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function recursiveRemove(pattern, str) {
|
||||
const newStr = str.replace(pattern, "");
|
||||
return newStr.length === str.length ? newStr : recursiveRemove(pattern, newStr);
|
||||
}
|
||||
|
||||
if (removeScriptAndStyle) {
|
||||
htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, "");
|
||||
htmlStr = recursiveRemove(/<(script|style)[^>]*>.*?<\/(script|style)>/gi, htmlStr);
|
||||
}
|
||||
return htmlStr.replace(/<[^>]+>/g, "");
|
||||
}
|
||||
@@ -729,11 +747,10 @@ class Utils {
|
||||
">": ">",
|
||||
'"': """,
|
||||
"'": "'", // ' not recommended because it's not in the HTML spec
|
||||
"/": "/", // forward slash is included as it helps end an HTML entity
|
||||
"`": "`"
|
||||
};
|
||||
|
||||
return str.replace(/[&<>"'/`]/g, function (match) {
|
||||
return str.replace(/[&<>"'`]/g, function (match) {
|
||||
return HTML_CHARS[match];
|
||||
});
|
||||
}
|
||||
@@ -879,7 +896,7 @@ class Utils {
|
||||
while ((m = recipeRegex.exec(recipe))) {
|
||||
// Translate strings in args back to double-quotes
|
||||
args = m[2]
|
||||
.replace(/"/g, '\\"') // Escape double quotes
|
||||
.replace(/"/g, '\\"') // Escape double quotes // lgtm [js/incomplete-sanitization]
|
||||
.replace(/(^|,|{|:)'/g, '$1"') // Replace opening ' with "
|
||||
.replace(/([^\\]|(?:\\\\)+)'(,|:|}|$)/g, '$1"$2') // Replace closing ' with "
|
||||
.replace(/\\'/g, "'"); // Unescape single quotes
|
||||
|
||||
@@ -267,6 +267,7 @@
|
||||
"Windows Filetime to UNIX Timestamp",
|
||||
"UNIX Timestamp to Windows Filetime",
|
||||
"Extract dates",
|
||||
"Get Time",
|
||||
"Sleep"
|
||||
]
|
||||
},
|
||||
@@ -286,6 +287,7 @@
|
||||
"JPath expression",
|
||||
"CSS selector",
|
||||
"Extract EXIF",
|
||||
"Extract ID3",
|
||||
"Extract Files"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -121,7 +121,7 @@ prompt.get(schema, (err, result) => {
|
||||
|
||||
const moduleName = result.opName.replace(/\w\S*/g, txt => {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1);
|
||||
}).replace(/[\s-()/./]/g, "");
|
||||
}).replace(/[\s-()./]/g, "");
|
||||
|
||||
|
||||
const template = `/**
|
||||
|
||||
@@ -148,4 +148,8 @@ export const ALPHABET_OPTIONS = [
|
||||
{name: "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r", value: "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"},
|
||||
{name: "ROT13: N-ZA-Mn-za-m0-9+/=", value: "N-ZA-Mn-za-m0-9+/="},
|
||||
{name: "UNIX crypt: ./0-9A-Za-z", value: "./0-9A-Za-z"},
|
||||
{name: "Atom128: /128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", value: "/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC"},
|
||||
{name: "Megan35: 3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", value: "3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5"},
|
||||
{name: "Zong22: ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", value: "ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2"},
|
||||
{name: "Hazz15: HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", value: "HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5"}
|
||||
];
|
||||
|
||||
@@ -32,9 +32,9 @@ export const ALPHABET_OPTIONS = [
|
||||
* @returns {string}
|
||||
*/
|
||||
export function alphabetName(alphabet) {
|
||||
alphabet = alphabet.replace("'", "'");
|
||||
alphabet = alphabet.replace("\"", """);
|
||||
alphabet = alphabet.replace("\\", "\");
|
||||
alphabet = alphabet.replace(/'/g, "'");
|
||||
alphabet = alphabet.replace(/"/g, """);
|
||||
alphabet = alphabet.replace(/\\/g, "\");
|
||||
let name;
|
||||
|
||||
ALPHABET_OPTIONS.forEach(function(a) {
|
||||
|
||||
@@ -86,8 +86,8 @@ export function getScatterValues(input, recordDelimiter, fieldDelimiter, columnH
|
||||
}
|
||||
|
||||
values = values.map(row => {
|
||||
const x = parseFloat(row[0], 10),
|
||||
y = parseFloat(row[1], 10);
|
||||
const x = parseFloat(row[0]),
|
||||
y = parseFloat(row[1]);
|
||||
|
||||
if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
|
||||
if (Number.isNaN(y)) throw new OperationError("Values must be numbers in base 10.");
|
||||
@@ -121,8 +121,8 @@ export function getScatterValuesWithColour(input, recordDelimiter, fieldDelimite
|
||||
}
|
||||
|
||||
values = values.map(row => {
|
||||
const x = parseFloat(row[0], 10),
|
||||
y = parseFloat(row[1], 10),
|
||||
const x = parseFloat(row[0]),
|
||||
y = parseFloat(row[1]),
|
||||
colour = row[2];
|
||||
|
||||
if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
|
||||
@@ -157,7 +157,7 @@ export function getSeriesValues(input, recordDelimiter, fieldDelimiter, columnHe
|
||||
values.forEach(row => {
|
||||
const serie = row[0],
|
||||
xVal = row[1],
|
||||
val = parseFloat(row[2], 10);
|
||||
val = parseFloat(row[2]);
|
||||
|
||||
if (Number.isNaN(val)) throw new OperationError("Values must be numbers in base 10.");
|
||||
|
||||
|
||||
9
src/core/lib/Crypt.mjs
Normal file
9
src/core/lib/Crypt.mjs
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Crypt resources.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
export const cryptNotice = "WARNING: Cryptographic operations in CyberChef should not be relied upon to provide security in any situation. No guarantee is offered for their correctness. We advise you not to use keys generated from CyberChef in operational contexts.";
|
||||
@@ -2197,14 +2197,14 @@ export const FILE_SIGNATURES = {
|
||||
mime: "application/octet-stream",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x6b, // keych
|
||||
0: 0x6b, // kych
|
||||
1: 0x79,
|
||||
2: 0x63,
|
||||
3: 0x68,
|
||||
4: 0x00,
|
||||
5: 0x01
|
||||
},
|
||||
extractor: null
|
||||
extractor: extractMacOSXKeychain
|
||||
},
|
||||
{
|
||||
name: "TCP Packet",
|
||||
@@ -2355,6 +2355,12 @@ export const FILE_SIGNATURES = {
|
||||
1: 0x03,
|
||||
2: 0xc6,
|
||||
3: 0x04
|
||||
},
|
||||
{
|
||||
0: 0x95,
|
||||
1: 0x05,
|
||||
2: 0x86,
|
||||
3: 0x04
|
||||
}
|
||||
],
|
||||
extractor: null
|
||||
@@ -3406,6 +3412,26 @@ export function extractPListXML(bytes, offset) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MacOS X Keychain Extactor.
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {number} offset
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function extractMacOSXKeychain(bytes, offset) {
|
||||
const stream = new Stream(bytes.slice(offset));
|
||||
|
||||
// Move to size field.
|
||||
stream.moveTo(0x14);
|
||||
|
||||
// Move forwards by size.
|
||||
stream.moveForwardsBy(stream.readInt(4));
|
||||
|
||||
return stream.carve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* OLE2 extractor.
|
||||
*
|
||||
|
||||
@@ -213,7 +213,7 @@ function locatePotentialSig(buf, sig, offset) {
|
||||
export function isType(type, buf) {
|
||||
const types = detectFileType(buf);
|
||||
|
||||
if (!(types && types.length)) return false;
|
||||
if (!types.length) return false;
|
||||
|
||||
if (typeof type === "string") {
|
||||
return types.reduce((acc, t) => {
|
||||
|
||||
219
src/core/lib/FuzzySearch.mjs
Normal file
219
src/core/lib/FuzzySearch.mjs
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* LICENSE
|
||||
*
|
||||
* This software is dual-licensed to the public domain and under the following
|
||||
* license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
* publish, and distribute this file as you see fit.
|
||||
*
|
||||
* VERSION
|
||||
* 0.1.0 (2016-03-28) Initial release
|
||||
*
|
||||
* AUTHOR
|
||||
* Forrest Smith
|
||||
*
|
||||
* CONTRIBUTORS
|
||||
* J<>rgen Tjern<72> - async helper
|
||||
* Anurag Awasthi - updated to 0.2.0
|
||||
*/
|
||||
|
||||
const SEQUENTIAL_BONUS = 15; // bonus for adjacent matches
|
||||
const SEPARATOR_BONUS = 30; // bonus if match occurs after a separator
|
||||
const CAMEL_BONUS = 30; // bonus if match is uppercase and prev is lower
|
||||
const FIRST_LETTER_BONUS = 15; // bonus if the first letter is matched
|
||||
|
||||
const LEADING_LETTER_PENALTY = -5; // penalty applied for every letter in str before the first match
|
||||
const MAX_LEADING_LETTER_PENALTY = -15; // maximum penalty for leading letters
|
||||
const UNMATCHED_LETTER_PENALTY = -1;
|
||||
|
||||
/**
|
||||
* Does a fuzzy search to find pattern inside a string.
|
||||
* @param {*} pattern string pattern to search for
|
||||
* @param {*} str string string which is being searched
|
||||
* @returns [boolean, number] a boolean which tells if pattern was
|
||||
* found or not and a search score
|
||||
*/
|
||||
export function fuzzyMatch(pattern, str) {
|
||||
const recursionCount = 0;
|
||||
const recursionLimit = 10;
|
||||
const matches = [];
|
||||
const maxMatches = 256;
|
||||
|
||||
return fuzzyMatchRecursive(
|
||||
pattern,
|
||||
str,
|
||||
0 /* patternCurIndex */,
|
||||
0 /* strCurrIndex */,
|
||||
null /* srcMatces */,
|
||||
matches,
|
||||
maxMatches,
|
||||
0 /* nextMatch */,
|
||||
recursionCount,
|
||||
recursionLimit
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive helper function
|
||||
*/
|
||||
function fuzzyMatchRecursive(
|
||||
pattern,
|
||||
str,
|
||||
patternCurIndex,
|
||||
strCurrIndex,
|
||||
srcMatches,
|
||||
matches,
|
||||
maxMatches,
|
||||
nextMatch,
|
||||
recursionCount,
|
||||
recursionLimit
|
||||
) {
|
||||
let outScore = 0;
|
||||
|
||||
// Return if recursion limit is reached.
|
||||
if (++recursionCount >= recursionLimit) {
|
||||
return [false, outScore, []];
|
||||
}
|
||||
|
||||
// Return if we reached ends of strings.
|
||||
if (patternCurIndex === pattern.length || strCurrIndex === str.length) {
|
||||
return [false, outScore, []];
|
||||
}
|
||||
|
||||
// Recursion params
|
||||
let recursiveMatch = false;
|
||||
let bestRecursiveMatches = [];
|
||||
let bestRecursiveScore = 0;
|
||||
|
||||
// Loop through pattern and str looking for a match.
|
||||
let firstMatch = true;
|
||||
while (patternCurIndex < pattern.length && strCurrIndex < str.length) {
|
||||
// Match found.
|
||||
if (
|
||||
pattern[patternCurIndex].toLowerCase() === str[strCurrIndex].toLowerCase()
|
||||
) {
|
||||
if (nextMatch >= maxMatches) {
|
||||
return [false, outScore, []];
|
||||
}
|
||||
|
||||
if (firstMatch && srcMatches) {
|
||||
matches = [...srcMatches];
|
||||
firstMatch = false;
|
||||
}
|
||||
|
||||
const [matched, recursiveScore, recursiveMatches] = fuzzyMatchRecursive(
|
||||
pattern,
|
||||
str,
|
||||
patternCurIndex,
|
||||
strCurrIndex + 1,
|
||||
matches,
|
||||
recursiveMatches,
|
||||
maxMatches,
|
||||
nextMatch,
|
||||
recursionCount,
|
||||
recursionLimit
|
||||
);
|
||||
|
||||
if (matched) {
|
||||
// Pick best recursive score.
|
||||
if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
|
||||
bestRecursiveMatches = [...recursiveMatches];
|
||||
bestRecursiveScore = recursiveScore;
|
||||
}
|
||||
recursiveMatch = true;
|
||||
}
|
||||
|
||||
matches[nextMatch++] = strCurrIndex;
|
||||
++patternCurIndex;
|
||||
}
|
||||
++strCurrIndex;
|
||||
}
|
||||
|
||||
const matched = patternCurIndex === pattern.length;
|
||||
|
||||
if (matched) {
|
||||
outScore = 100;
|
||||
|
||||
// Apply leading letter penalty
|
||||
let penalty = LEADING_LETTER_PENALTY * matches[0];
|
||||
penalty =
|
||||
penalty < MAX_LEADING_LETTER_PENALTY ?
|
||||
MAX_LEADING_LETTER_PENALTY :
|
||||
penalty;
|
||||
outScore += penalty;
|
||||
|
||||
// Apply unmatched penalty
|
||||
const unmatched = str.length - nextMatch;
|
||||
outScore += UNMATCHED_LETTER_PENALTY * unmatched;
|
||||
|
||||
// Apply ordering bonuses
|
||||
for (let i = 0; i < nextMatch; i++) {
|
||||
const currIdx = matches[i];
|
||||
|
||||
if (i > 0) {
|
||||
const prevIdx = matches[i - 1];
|
||||
if (currIdx === prevIdx + 1) {
|
||||
outScore += SEQUENTIAL_BONUS;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for bonuses based on neighbor character value.
|
||||
if (currIdx > 0) {
|
||||
// Camel case
|
||||
const neighbor = str[currIdx - 1];
|
||||
const curr = str[currIdx];
|
||||
if (
|
||||
neighbor !== neighbor.toUpperCase() &&
|
||||
curr !== curr.toLowerCase()
|
||||
) {
|
||||
outScore += CAMEL_BONUS;
|
||||
}
|
||||
const isNeighbourSeparator = neighbor === "_" || neighbor === " ";
|
||||
if (isNeighbourSeparator) {
|
||||
outScore += SEPARATOR_BONUS;
|
||||
}
|
||||
} else {
|
||||
// First letter
|
||||
outScore += FIRST_LETTER_BONUS;
|
||||
}
|
||||
}
|
||||
|
||||
// Return best result
|
||||
if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
|
||||
// Recursive score is better than "this"
|
||||
matches = bestRecursiveMatches;
|
||||
outScore = bestRecursiveScore;
|
||||
return [true, outScore, matches];
|
||||
} else if (matched) {
|
||||
// "this" score is better than recursive
|
||||
return [true, outScore, matches];
|
||||
} else {
|
||||
return [false, outScore, matches];
|
||||
}
|
||||
}
|
||||
return [false, outScore, matches];
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a list of match indexes into a list of match ranges
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @param [number] matches
|
||||
* @returns [[number]]
|
||||
*/
|
||||
export function calcMatchRanges(matches) {
|
||||
const ranges = [];
|
||||
let start = matches[0],
|
||||
curr = start;
|
||||
|
||||
matches.forEach(m => {
|
||||
if (m === curr || m === curr + 1) curr = m;
|
||||
else {
|
||||
ranges.push([start, curr - start + 1]);
|
||||
start = m;
|
||||
curr = m;
|
||||
}
|
||||
});
|
||||
|
||||
ranges.push([start, curr - start + 1]);
|
||||
return ranges;
|
||||
}
|
||||
24
src/core/lib/JWT.mjs
Normal file
24
src/core/lib/JWT.mjs
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* JWT resources
|
||||
*
|
||||
* @author mt3571 [mt3571@protonmail.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* List of the JWT algorithms that can be used
|
||||
*/
|
||||
export const JWT_ALGORITHMS = [
|
||||
"HS256",
|
||||
"HS384",
|
||||
"HS512",
|
||||
"RS256",
|
||||
"RS384",
|
||||
"RS512",
|
||||
"ES256",
|
||||
"ES384",
|
||||
"ES512",
|
||||
"None"
|
||||
];
|
||||
@@ -177,7 +177,7 @@ export default class Stream {
|
||||
|
||||
// Get the skip table.
|
||||
const skiptable = preprocess(val, length);
|
||||
let found = true;
|
||||
let found;
|
||||
|
||||
while (this.position < this.length) {
|
||||
// Until we hit the final element of val in the stream.
|
||||
|
||||
@@ -39,8 +39,8 @@ class ExtractEmailAddresses extends Operation {
|
||||
*/
|
||||
run(input, args) {
|
||||
const displayTotal = args[0],
|
||||
// email regex from: https://www.regextester.com/98066
|
||||
regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/ig;
|
||||
// email regex from: https://www.regextester.com/98066
|
||||
regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}\])/ig;
|
||||
return search(input, regex, null, displayTotal);
|
||||
}
|
||||
|
||||
|
||||
324
src/core/operations/ExtractID3.mjs
Normal file
324
src/core/operations/ExtractID3.mjs
Normal file
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* @author n1073645 [n1073645@gmail.com]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Extract ID3 operation
|
||||
*/
|
||||
class ExtractID3 extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractID3 constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract ID3";
|
||||
this.module = "Default";
|
||||
this.description = "This operation extracts ID3 metadata from an MP3 file.<br><br>ID3 is a metadata container most often used in conjunction with the MP3 audio file format. It allows information such as the title, artist, album, track number, and other information about the file to be stored in the file itself.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/ID3";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "JSON";
|
||||
this.presentType = "html";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {JSON}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
|
||||
/**
|
||||
* Extracts the ID3 header fields.
|
||||
*/
|
||||
function extractHeader() {
|
||||
if (!Array.from(input.slice(0, 3)).equals([0x49, 0x44, 0x33]))
|
||||
throw new OperationError("No valid ID3 header.");
|
||||
|
||||
const header = {
|
||||
"Type": "ID3",
|
||||
// Tag version
|
||||
"Version": input[3].toString() + "." + input[4].toString(),
|
||||
// Header version
|
||||
"Flags": input[5].toString()
|
||||
};
|
||||
|
||||
input = input.slice(6);
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the size fields to a single integer.
|
||||
*
|
||||
* @param {number} num
|
||||
* @returns {string}
|
||||
*/
|
||||
function readSize(num) {
|
||||
let result = 0;
|
||||
|
||||
// The sizes are 7 bit numbers stored in 8 bit locations
|
||||
for (let i = (num) * 7; i; i -= 7) {
|
||||
result = (result << i) | input[0];
|
||||
input = input.slice(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads frame header based on ID.
|
||||
*
|
||||
* @param {string} id
|
||||
* @returns {number}
|
||||
*/
|
||||
function readFrame(id) {
|
||||
const frame = {};
|
||||
|
||||
// Size of frame
|
||||
const size = readSize(4);
|
||||
frame.Size = size.toString();
|
||||
frame.Description = FRAME_DESCRIPTIONS[id];
|
||||
input = input.slice(2);
|
||||
|
||||
// Read data from frame
|
||||
let data = "";
|
||||
for (let i = 1; i < size; i++)
|
||||
data += String.fromCharCode(input[i]);
|
||||
frame.Data = data;
|
||||
|
||||
// Move to next Frame
|
||||
input = input.slice(size);
|
||||
|
||||
return [frame, size];
|
||||
}
|
||||
|
||||
const result = extractHeader();
|
||||
|
||||
const headerTagSize = readSize(4);
|
||||
result.Size = headerTagSize.toString();
|
||||
|
||||
const tags = {};
|
||||
let pos = 10;
|
||||
|
||||
// While the current element is in the header
|
||||
while (pos < headerTagSize) {
|
||||
|
||||
// Frame Identifier of frame
|
||||
let id = String.fromCharCode(input[0]) + String.fromCharCode(input[1]) + String.fromCharCode(input[2]);
|
||||
input = input.slice(3);
|
||||
|
||||
// If the next character is non-zero it is an identifier
|
||||
if (input[0] !== 0) {
|
||||
id += String.fromCharCode(input[0]);
|
||||
}
|
||||
input = input.slice(1);
|
||||
|
||||
if (id in FRAME_DESCRIPTIONS) {
|
||||
const [frame, size] = readFrame(id);
|
||||
tags[id] = frame;
|
||||
pos += 10 + size;
|
||||
} else if (id === "\x00\x00\x00") { // end of header
|
||||
break;
|
||||
} else {
|
||||
throw new OperationError("Unknown Frame Identifier: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
result.Tags = tags;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the extracted data in a more accessible format for web apps.
|
||||
* @param {JSON} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data || !Object.prototype.hasOwnProperty.call(data, "Tags"))
|
||||
return JSON.stringify(data, null, 4);
|
||||
|
||||
let output = `<table class="table table-hover table-sm table-bordered table-nonfluid">
|
||||
<tr><th>Tag</th><th>Description</th><th>Data</th></tr>`;
|
||||
|
||||
for (const tagID in data.Tags) {
|
||||
const description = data.Tags[tagID].Description,
|
||||
contents = data.Tags[tagID].Data;
|
||||
output += `<tr><td>${tagID}</td><td>${Utils.escapeHtml(description)}</td><td>${Utils.escapeHtml(contents)}</td></tr>`;
|
||||
}
|
||||
output += "</table>";
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Borrowed from https://github.com/aadsm/jsmediatags
|
||||
const FRAME_DESCRIPTIONS = {
|
||||
// v2.2
|
||||
"BUF": "Recommended buffer size",
|
||||
"CNT": "Play counter",
|
||||
"COM": "Comments",
|
||||
"CRA": "Audio encryption",
|
||||
"CRM": "Encrypted meta frame",
|
||||
"ETC": "Event timing codes",
|
||||
"EQU": "Equalization",
|
||||
"GEO": "General encapsulated object",
|
||||
"IPL": "Involved people list",
|
||||
"LNK": "Linked information",
|
||||
"MCI": "Music CD Identifier",
|
||||
"MLL": "MPEG location lookup table",
|
||||
"PIC": "Attached picture",
|
||||
"POP": "Popularimeter",
|
||||
"REV": "Reverb",
|
||||
"RVA": "Relative volume adjustment",
|
||||
"SLT": "Synchronized lyric/text",
|
||||
"STC": "Synced tempo codes",
|
||||
"TAL": "Album/Movie/Show title",
|
||||
"TBP": "BPM (Beats Per Minute)",
|
||||
"TCM": "Composer",
|
||||
"TCO": "Content type",
|
||||
"TCR": "Copyright message",
|
||||
"TDA": "Date",
|
||||
"TDY": "Playlist delay",
|
||||
"TEN": "Encoded by",
|
||||
"TFT": "File type",
|
||||
"TIM": "Time",
|
||||
"TKE": "Initial key",
|
||||
"TLA": "Language(s)",
|
||||
"TLE": "Length",
|
||||
"TMT": "Media type",
|
||||
"TOA": "Original artist(s)/performer(s)",
|
||||
"TOF": "Original filename",
|
||||
"TOL": "Original Lyricist(s)/text writer(s)",
|
||||
"TOR": "Original release year",
|
||||
"TOT": "Original album/Movie/Show title",
|
||||
"TP1": "Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group",
|
||||
"TP2": "Band/Orchestra/Accompaniment",
|
||||
"TP3": "Conductor/Performer refinement",
|
||||
"TP4": "Interpreted, remixed, or otherwise modified by",
|
||||
"TPA": "Part of a set",
|
||||
"TPB": "Publisher",
|
||||
"TRC": "ISRC (International Standard Recording Code)",
|
||||
"TRD": "Recording dates",
|
||||
"TRK": "Track number/Position in set",
|
||||
"TSI": "Size",
|
||||
"TSS": "Software/hardware and settings used for encoding",
|
||||
"TT1": "Content group description",
|
||||
"TT2": "Title/Songname/Content description",
|
||||
"TT3": "Subtitle/Description refinement",
|
||||
"TXT": "Lyricist/text writer",
|
||||
"TXX": "User defined text information frame",
|
||||
"TYE": "Year",
|
||||
"UFI": "Unique file identifier",
|
||||
"ULT": "Unsychronized lyric/text transcription",
|
||||
"WAF": "Official audio file webpage",
|
||||
"WAR": "Official artist/performer webpage",
|
||||
"WAS": "Official audio source webpage",
|
||||
"WCM": "Commercial information",
|
||||
"WCP": "Copyright/Legal information",
|
||||
"WPB": "Publishers official webpage",
|
||||
"WXX": "User defined URL link frame",
|
||||
// v2.3
|
||||
"AENC": "Audio encryption",
|
||||
"APIC": "Attached picture",
|
||||
"ASPI": "Audio seek point index",
|
||||
"CHAP": "Chapter",
|
||||
"CTOC": "Table of contents",
|
||||
"COMM": "Comments",
|
||||
"COMR": "Commercial frame",
|
||||
"ENCR": "Encryption method registration",
|
||||
"EQU2": "Equalisation (2)",
|
||||
"EQUA": "Equalization",
|
||||
"ETCO": "Event timing codes",
|
||||
"GEOB": "General encapsulated object",
|
||||
"GRID": "Group identification registration",
|
||||
"IPLS": "Involved people list",
|
||||
"LINK": "Linked information",
|
||||
"MCDI": "Music CD identifier",
|
||||
"MLLT": "MPEG location lookup table",
|
||||
"OWNE": "Ownership frame",
|
||||
"PRIV": "Private frame",
|
||||
"PCNT": "Play counter",
|
||||
"POPM": "Popularimeter",
|
||||
"POSS": "Position synchronisation frame",
|
||||
"RBUF": "Recommended buffer size",
|
||||
"RVA2": "Relative volume adjustment (2)",
|
||||
"RVAD": "Relative volume adjustment",
|
||||
"RVRB": "Reverb",
|
||||
"SEEK": "Seek frame",
|
||||
"SYLT": "Synchronized lyric/text",
|
||||
"SYTC": "Synchronized tempo codes",
|
||||
"TALB": "Album/Movie/Show title",
|
||||
"TBPM": "BPM (beats per minute)",
|
||||
"TCOM": "Composer",
|
||||
"TCON": "Content type",
|
||||
"TCOP": "Copyright message",
|
||||
"TDAT": "Date",
|
||||
"TDLY": "Playlist delay",
|
||||
"TDRC": "Recording time",
|
||||
"TDRL": "Release time",
|
||||
"TDTG": "Tagging time",
|
||||
"TENC": "Encoded by",
|
||||
"TEXT": "Lyricist/Text writer",
|
||||
"TFLT": "File type",
|
||||
"TIME": "Time",
|
||||
"TIPL": "Involved people list",
|
||||
"TIT1": "Content group description",
|
||||
"TIT2": "Title/songname/content description",
|
||||
"TIT3": "Subtitle/Description refinement",
|
||||
"TKEY": "Initial key",
|
||||
"TLAN": "Language(s)",
|
||||
"TLEN": "Length",
|
||||
"TMCL": "Musician credits list",
|
||||
"TMED": "Media type",
|
||||
"TMOO": "Mood",
|
||||
"TOAL": "Original album/movie/show title",
|
||||
"TOFN": "Original filename",
|
||||
"TOLY": "Original lyricist(s)/text writer(s)",
|
||||
"TOPE": "Original artist(s)/performer(s)",
|
||||
"TORY": "Original release year",
|
||||
"TOWN": "File owner/licensee",
|
||||
"TPE1": "Lead performer(s)/Soloist(s)",
|
||||
"TPE2": "Band/orchestra/accompaniment",
|
||||
"TPE3": "Conductor/performer refinement",
|
||||
"TPE4": "Interpreted, remixed, or otherwise modified by",
|
||||
"TPOS": "Part of a set",
|
||||
"TPRO": "Produced notice",
|
||||
"TPUB": "Publisher",
|
||||
"TRCK": "Track number/Position in set",
|
||||
"TRDA": "Recording dates",
|
||||
"TRSN": "Internet radio station name",
|
||||
"TRSO": "Internet radio station owner",
|
||||
"TSOA": "Album sort order",
|
||||
"TSOP": "Performer sort order",
|
||||
"TSOT": "Title sort order",
|
||||
"TSIZ": "Size",
|
||||
"TSRC": "ISRC (international standard recording code)",
|
||||
"TSSE": "Software/Hardware and settings used for encoding",
|
||||
"TSST": "Set subtitle",
|
||||
"TYER": "Year",
|
||||
"TXXX": "User defined text information frame",
|
||||
"UFID": "Unique file identifier",
|
||||
"USER": "Terms of use",
|
||||
"USLT": "Unsychronized lyric/text transcription",
|
||||
"WCOM": "Commercial information",
|
||||
"WCOP": "Copyright/Legal information",
|
||||
"WOAF": "Official audio file webpage",
|
||||
"WOAR": "Official artist/performer webpage",
|
||||
"WOAS": "Official audio source webpage",
|
||||
"WORS": "Official internet radio station homepage",
|
||||
"WPAY": "Payment",
|
||||
"WPUB": "Publishers official webpage",
|
||||
"WXXX": "User defined URL link frame"
|
||||
};
|
||||
|
||||
export default ExtractID3;
|
||||
@@ -31,6 +31,11 @@ class FrequencyDistribution extends Operation {
|
||||
"name": "Show 0%s",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "Show ASCII",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -76,14 +81,14 @@ class FrequencyDistribution extends Operation {
|
||||
* @returns {html}
|
||||
*/
|
||||
present(freq, args) {
|
||||
const showZeroes = args[0];
|
||||
const [showZeroes, showAscii] = args;
|
||||
|
||||
// Print
|
||||
let output = `<canvas id='chart-area'></canvas><br>
|
||||
Total data length: ${freq.dataLength}
|
||||
Number of bytes represented: ${freq.bytesRepresented}
|
||||
Number of bytes not represented: ${256 - freq.bytesRepresented}
|
||||
|
||||
Byte Percentage
|
||||
<script>
|
||||
var canvas = document.getElementById("chart-area"),
|
||||
parentRect = canvas.parentNode.getBoundingClientRect(),
|
||||
@@ -93,16 +98,32 @@ Byte Percentage
|
||||
canvas.height = parentRect.height * 0.9;
|
||||
|
||||
CanvasComponents.drawBarChart(canvas, scores, "Byte", "Frequency %", 16, 6);
|
||||
</script>`;
|
||||
</script>
|
||||
<table class="table table-hover table-sm">
|
||||
<tr><th>Byte</th>${showAscii ? "<th>ASCII</th>" : ""}<th>Percentage</th><th></th></tr>`;
|
||||
|
||||
for (let i = 0; i < 256; i++) {
|
||||
if (freq.distribution[i] || showZeroes) {
|
||||
output += " " + Utils.hex(i, 2) + " (" +
|
||||
(freq.percentages[i].toFixed(2).replace(".00", "") + "%)").padEnd(8, " ") +
|
||||
Array(Math.ceil(freq.percentages[i])+1).join("|") + "\n";
|
||||
let c = "";
|
||||
if (showAscii) {
|
||||
if (i <= 32) {
|
||||
c = String.fromCharCode(0x2400 + i);
|
||||
} else if (i === 127) {
|
||||
c = String.fromCharCode(0x2421);
|
||||
} else {
|
||||
c = String.fromCharCode(i);
|
||||
}
|
||||
}
|
||||
const bite = `<td>${Utils.hex(i, 2)}</td>`,
|
||||
ascii = showAscii ? `<td>${c}</td>` : "",
|
||||
percentage = `<td>${(freq.percentages[i].toFixed(2).replace(".00", "") + "%").padEnd(8, " ")}</td>`,
|
||||
bars = `<td>${Array(Math.ceil(freq.percentages[i])+1).join("|")}</td>`;
|
||||
|
||||
output += `<tr>${bite}${ascii}${percentage}${bars}</tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
output += "</table>";
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ class FromBCD extends Operation {
|
||||
if (!packed) {
|
||||
// Discard each high nibble
|
||||
for (let i = 0; i < nibbles.length; i++) {
|
||||
nibbles.splice(i, 1);
|
||||
nibbles.splice(i, 1); // lgtm [js/loop-iteration-skipped-due-to-shifting]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class FromBase extends Operation {
|
||||
}
|
||||
|
||||
const number = input.replace(/\s/g, "").split(".");
|
||||
let result = new BigNumber(number[0], radix) || 0;
|
||||
let result = new BigNumber(number[0], radix);
|
||||
|
||||
if (number.length === 1) return result;
|
||||
|
||||
|
||||
@@ -84,10 +84,10 @@ class FromBase32 extends Operation {
|
||||
chr5 = ((enc7 & 7) << 5) | enc8;
|
||||
|
||||
output.push(chr1);
|
||||
if (enc2 & 3 !== 0 || enc3 !== 32) output.push(chr2);
|
||||
if (enc4 & 15 !== 0 || enc5 !== 32) output.push(chr3);
|
||||
if (enc5 & 1 !== 0 || enc6 !== 32) output.push(chr4);
|
||||
if (enc7 & 7 !== 0 || enc8 !== 32) output.push(chr5);
|
||||
if ((enc2 & 3) !== 0 || enc3 !== 32) output.push(chr2);
|
||||
if ((enc4 & 15) !== 0 || enc5 !== 32) output.push(chr3);
|
||||
if ((enc5 & 1) !== 0 || enc6 !== 32) output.push(chr4);
|
||||
if ((enc7 & 7) !== 0 || enc8 !== 32) output.push(chr5);
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
@@ -102,6 +102,26 @@ class FromBase64 extends Operation {
|
||||
flags: "i",
|
||||
args: ["./0-9A-Za-z", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}CC|[A-Z=\\d\\+/]{3}C)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}22|[A-Z=\\d\\+/]{3}2)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", true]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
import Operation from "../Operation.mjs";
|
||||
import kbpgp from "kbpgp";
|
||||
import { getSubkeySize, ASP } from "../lib/PGP.mjs";
|
||||
import { cryptNotice } from "../lib/Crypt.mjs";
|
||||
import * as es6promisify from "es6-promisify";
|
||||
const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
|
||||
|
||||
|
||||
/**
|
||||
* Generate PGP Key Pair operation
|
||||
*/
|
||||
@@ -25,7 +27,7 @@ class GeneratePGPKeyPair extends Operation {
|
||||
|
||||
this.name = "Generate PGP Key Pair";
|
||||
this.module = "PGP";
|
||||
this.description = "Generates a new public/private PGP key pair. Supports RSA and Eliptic Curve (EC) keys.<br><br>WARNING: Cryptographic operations in CyberChef should not be relied upon to provide security in any situation. No guarantee is offered for their correctness. We advise you not to use keys generated from CyberChef in operational contexts.";
|
||||
this.description = `Generates a new public/private PGP key pair. Supports RSA and Eliptic Curve (EC) keys.<br><br>${cryptNotice}`;
|
||||
this.infoURL = "https://wikipedia.org/wiki/Pretty_Good_Privacy";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import forge from "node-forge";
|
||||
import { cryptNotice } from "../lib/Crypt.mjs";
|
||||
|
||||
/**
|
||||
* Generate RSA Key Pair operation
|
||||
@@ -21,7 +22,7 @@ class GenerateRSAKeyPair extends Operation {
|
||||
|
||||
this.name = "Generate RSA Key Pair";
|
||||
this.module = "Ciphers";
|
||||
this.description = "Generate an RSA key pair with a given number of bits.<br><br>WARNING: Cryptographic operations in CyberChef should not be relied upon to provide security in any situation. No guarantee is offered for their correctness. We advise you not to use keys generated from CyberChef in operational contexts.";
|
||||
this.description = `Generate an RSA key pair with a given number of bits.<br><br>${cryptNotice}`;
|
||||
this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
|
||||
63
src/core/operations/GetTime.mjs
Normal file
63
src/core/operations/GetTime.mjs
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @author n1073645 [n1073645@gmail.com]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import {UNITS} from "../lib/DateTime.mjs";
|
||||
|
||||
/**
|
||||
* Get Time operation
|
||||
*/
|
||||
class GetTime extends Operation {
|
||||
|
||||
/**
|
||||
* GetTime constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Get Time";
|
||||
this.module = "Default";
|
||||
this.description = "Generates a timestamp showing the amount of time since the UNIX epoch (1970-01-01 00:00:00 UTC). Uses the W3C High Resolution Time API.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Unix_time";
|
||||
this.inputType = "string";
|
||||
this.outputType = "number";
|
||||
this.args = [
|
||||
{
|
||||
name: "Granularity",
|
||||
type: "option",
|
||||
value: UNITS
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
run(input, args) {
|
||||
const nowMs = (performance.timeOrigin + performance.now()),
|
||||
granularity = args[0];
|
||||
|
||||
switch (granularity) {
|
||||
case "Nanoseconds (ns)":
|
||||
return Math.round(nowMs * 1000 * 1000);
|
||||
case "Microseconds (μs)":
|
||||
return Math.round(nowMs * 1000);
|
||||
case "Milliseconds (ms)":
|
||||
return Math.round(nowMs);
|
||||
case "Seconds (s)":
|
||||
return Math.round(nowMs / 1000);
|
||||
default:
|
||||
throw new OperationError("Unknown granularity value: " + granularity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default GetTime;
|
||||
@@ -3,10 +3,11 @@
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import jwt from "jsonwebtoken";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import {JWT_ALGORITHMS} from "../lib/JWT.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* JWT Sign operation
|
||||
@@ -34,18 +35,7 @@ class JWTSign extends Operation {
|
||||
{
|
||||
name: "Signing algorithm",
|
||||
type: "option",
|
||||
value: [
|
||||
"HS256",
|
||||
"HS384",
|
||||
"HS512",
|
||||
"RS256",
|
||||
"RS384",
|
||||
"RS512",
|
||||
"ES256",
|
||||
"ES384",
|
||||
"ES512",
|
||||
"None"
|
||||
]
|
||||
value: JWT_ALGORITHMS
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import jwt from "jsonwebtoken";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import {JWT_ALGORITHMS} from "../lib/JWT.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* JWT Verify operation
|
||||
@@ -27,7 +28,7 @@ class JWTVerify extends Operation {
|
||||
this.outputType = "JSON";
|
||||
this.args = [
|
||||
{
|
||||
name: "Private/Secret Key",
|
||||
name: "Public/Secret Key",
|
||||
type: "text",
|
||||
value: "secret"
|
||||
},
|
||||
@@ -41,14 +42,11 @@ class JWTVerify extends Operation {
|
||||
*/
|
||||
run(input, args) {
|
||||
const [key] = args;
|
||||
const algos = JWT_ALGORITHMS;
|
||||
algos[algos.indexOf("None")] = "none";
|
||||
|
||||
try {
|
||||
const verified = jwt.verify(input, key, { algorithms: [
|
||||
"HS256",
|
||||
"HS384",
|
||||
"HS512",
|
||||
"none"
|
||||
]});
|
||||
const verified = jwt.verify(input, key, { algorithms: algos });
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(verified, "name") && verified.name === "JsonWebTokenError") {
|
||||
throw new OperationError(verified.message);
|
||||
|
||||
@@ -376,7 +376,7 @@ class Lorenz extends Operation {
|
||||
// Psi wheels only move sometimes, dependent on M37 current setting and limitations
|
||||
|
||||
const basicmotor = m37lug;
|
||||
let totalmotor = basicmotor;
|
||||
let totalmotor;
|
||||
let lim = 0;
|
||||
|
||||
p5[2] = p5[1];
|
||||
|
||||
@@ -51,7 +51,7 @@ class OpticalCharacterRecognition extends Operation {
|
||||
async run(input, args) {
|
||||
const [showConfidence] = args;
|
||||
|
||||
if (!isWorkerEnvironment()) throw OperationError("This operation only works in a browser");
|
||||
if (!isWorkerEnvironment()) throw new OperationError("This operation only works in a browser");
|
||||
|
||||
const type = isImage(input);
|
||||
if (!type) {
|
||||
|
||||
@@ -111,7 +111,7 @@ class PHPDeserialize extends Operation {
|
||||
} else {
|
||||
const numberCheck = lastItem.match(/[0-9]+/);
|
||||
if (args[0] && numberCheck && numberCheck[0].length === lastItem.length) {
|
||||
result.push("\"" + lastItem + "\": " + item);
|
||||
result.push('"' + lastItem + '": ' + item);
|
||||
} else {
|
||||
result.push(lastItem + ": " + item);
|
||||
}
|
||||
@@ -149,11 +149,11 @@ class PHPDeserialize extends Operation {
|
||||
const length = readUntil(":");
|
||||
expect("\"");
|
||||
const value = read(length);
|
||||
expect("\";");
|
||||
expect('";');
|
||||
if (args[0]) {
|
||||
return "\"" + value.replace(/"/g, "\\\"") + "\"";
|
||||
return '"' + value.replace(/"/g, '\\"') + '"'; // lgtm [js/incomplete-sanitization]
|
||||
} else {
|
||||
return "\"" + value + "\"";
|
||||
return '"' + value + '"';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ class ParseIPv6Address extends Operation {
|
||||
|
||||
|
||||
// Detect possible EUI-64 addresses
|
||||
if ((ipv6[5] & 0xff === 0xff) && (ipv6[6] >>> 8 === 0xfe)) {
|
||||
if (((ipv6[5] & 0xff) === 0xff) && (ipv6[6] >>> 8 === 0xfe)) {
|
||||
output += "\n\nThis IPv6 address contains a modified EUI-64 address, identified by the presence of FF:FE in the 12th and 13th octets.";
|
||||
|
||||
const intIdent = Utils.hex(ipv6[4] >>> 8) + ":" + Utils.hex(ipv6[4] & 0xff) + ":" +
|
||||
|
||||
@@ -87,7 +87,7 @@ class ParseSSHHostKey extends Operation {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
convertKeyToBinary(inputKey, inputFormat) {
|
||||
const keyPattern = new RegExp(/^(?:[ssh]|[ecdsa-sha2])\S+\s+(\S*)/),
|
||||
const keyPattern = new RegExp(/^(?:ssh|ecdsa-sha2)\S+\s+(\S*)/),
|
||||
keyMatch = inputKey.match(keyPattern);
|
||||
|
||||
if (keyMatch) {
|
||||
|
||||
@@ -45,7 +45,7 @@ class RegularExpression extends Operation {
|
||||
},
|
||||
{
|
||||
name: "Email address",
|
||||
value: "(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?\\.)+[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\\])"
|
||||
value: "(?:[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9](?:[\\u00A0-\\uD7FF\\uE000-\\uFFFF-a-z0-9-]*[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9])?\\.)+[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9](?:[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9-]*[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}\\])"
|
||||
},
|
||||
{
|
||||
name: "URL",
|
||||
|
||||
@@ -81,7 +81,7 @@ class SharpenImage extends Operation {
|
||||
|
||||
if (isWorkerEnvironment())
|
||||
self.sendStatusMessage("Sharpening image... (Blurring cloned image)");
|
||||
const blurImage = gaussianBlur(image.clone(), radius, 3);
|
||||
const blurImage = gaussianBlur(image.clone(), radius);
|
||||
|
||||
|
||||
if (isWorkerEnvironment())
|
||||
|
||||
@@ -125,7 +125,7 @@ class Sort extends Operation {
|
||||
const ret = a_[i].localeCompare(b_[i]); // Compare strings
|
||||
if (ret !== 0) return ret;
|
||||
}
|
||||
if (!isNaN(a_[i]) && !isNaN(a_[i])) { // Compare numbers
|
||||
if (!isNaN(a_[i]) && !isNaN(b_[i])) { // Compare numbers
|
||||
if (a_[i] - b_[i] !== 0) return a_[i] - b_[i];
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,7 @@ class Sort extends Operation {
|
||||
const ret = a_[i].localeCompare(b_[i]); // Compare strings
|
||||
if (ret !== 0) return ret;
|
||||
}
|
||||
if (!isNaN(a_[i]) && !isNaN(a_[i])) { // Compare numbers
|
||||
if (!isNaN(a_[i]) && !isNaN(b_[i])) { // Compare numbers
|
||||
if (a_[i] - b_[i] !== 0) return a_[i] - b_[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class ToCharcode extends Operation {
|
||||
const delim = Utils.charRep(args[0] || "Space"),
|
||||
base = args[1];
|
||||
let output = "",
|
||||
padding = 2,
|
||||
padding,
|
||||
ordinal;
|
||||
|
||||
if (base < 2 || base > 36) {
|
||||
|
||||
@@ -20,7 +20,7 @@ class ToHexdump extends Operation {
|
||||
|
||||
this.name = "To Hexdump";
|
||||
this.module = "Default";
|
||||
this.description = "Creates a hexdump of the input data, displaying both the hexadecimal values of each byte and an ASCII representation alongside.";
|
||||
this.description = "Creates a hexdump of the input data, displaying both the hexadecimal values of each byte and an ASCII representation alongside.<br><br>The 'UNIX format' argument defines which subset of printable characters are displayed in the preview column.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Hex_dump";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
@@ -39,6 +39,11 @@ class ToHexdump extends Operation {
|
||||
"name": "Include final length",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "UNIX format",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -50,7 +55,7 @@ class ToHexdump extends Operation {
|
||||
*/
|
||||
run(input, args) {
|
||||
const data = new Uint8Array(input);
|
||||
const [length, upperCase, includeFinalLength] = args;
|
||||
const [length, upperCase, includeFinalLength, unixFormat] = args;
|
||||
const padding = 2;
|
||||
|
||||
let output = "";
|
||||
@@ -70,7 +75,9 @@ class ToHexdump extends Operation {
|
||||
|
||||
output += lineNo + " " +
|
||||
hexa.padEnd(length*(padding+1), " ") +
|
||||
" |" + Utils.printable(Utils.byteArrayToChars(buff)).padEnd(buff.length, " ") + "|\n";
|
||||
" |" +
|
||||
Utils.printable(Utils.byteArrayToChars(buff), false, unixFormat).padEnd(buff.length, " ") +
|
||||
"|\n";
|
||||
|
||||
if (includeFinalLength && i+buff.length === data.length) {
|
||||
output += Utils.hex(i+buff.length, 8) + "\n";
|
||||
|
||||
@@ -47,7 +47,6 @@ class UnescapeUnicodeCharacters extends Operation {
|
||||
while ((m = regex.exec(input))) {
|
||||
// Add up to match
|
||||
output += input.slice(i, m.index);
|
||||
i = m.index;
|
||||
|
||||
// Add match
|
||||
output += Utils.chr(parseInt(m[1], 16));
|
||||
|
||||
@@ -153,7 +153,7 @@ class HTMLIngredient {
|
||||
for (i = 0; i < this.value.length; i++) {
|
||||
if ((m = this.value[i].match(/\[([a-z0-9 -()^]+)\]/i))) {
|
||||
html += `<optgroup label="${m[1]}">`;
|
||||
} else if ((m = this.value[i].match(/\[\/([a-z0-9 -()^]+)\]/i))) {
|
||||
} else if (this.value[i].match(/\[\/([a-z0-9 -()^]+)\]/i)) {
|
||||
html += "</optgroup>";
|
||||
} else {
|
||||
html += `<option ${this.defaultIndex === i ? "selected" : ""}>${this.value[i]}</option>`;
|
||||
@@ -177,7 +177,7 @@ class HTMLIngredient {
|
||||
for (i = 0; i < this.value.length; i++) {
|
||||
if ((m = this.value[i].name.match(/\[([a-z0-9 -()^]+)\]/i))) {
|
||||
html += `<optgroup label="${m[1]}">`;
|
||||
} else if ((m = this.value[i].name.match(/\[\/([a-z0-9 -()^]+)\]/i))) {
|
||||
} else if (this.value[i].name.match(/\[\/([a-z0-9 -()^]+)\]/i)) {
|
||||
html += "</optgroup>";
|
||||
} else {
|
||||
const val = this.type === "populateMultiOption" ?
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
|
||||
import HTMLIngredient from "./HTMLIngredient.mjs";
|
||||
import Utils from "../core/Utils.mjs";
|
||||
import url from "url";
|
||||
|
||||
|
||||
/**
|
||||
@@ -72,7 +74,7 @@ class HTMLOperation {
|
||||
* @returns {string}
|
||||
*/
|
||||
toFullHtml() {
|
||||
let html = `<div class="op-title">${this.name}</div>
|
||||
let html = `<div class="op-title">${Utils.escapeHtml(this.name)}</div>
|
||||
<div class="ingredients">`;
|
||||
|
||||
for (let i = 0; i < this.ingList.length; i++) {
|
||||
@@ -91,32 +93,52 @@ class HTMLOperation {
|
||||
|
||||
|
||||
/**
|
||||
* Highlights the searched string in the name and description of the operation.
|
||||
* Highlights searched strings in the name and description of the operation.
|
||||
*
|
||||
* @param {string} searchStr
|
||||
* @param {number} namePos - The position of the search string in the operation name
|
||||
* @param {number} descPos - The position of the search string in the operation description
|
||||
* @param {[[number]]} nameIdxs - Indexes of the search strings in the operation name [[start, length]]
|
||||
* @param {[[number]]} descIdxs - Indexes of the search strings in the operation description [[start, length]]
|
||||
*/
|
||||
highlightSearchString(searchStr, namePos, descPos) {
|
||||
if (namePos >= 0) {
|
||||
this.name = this.name.slice(0, namePos) + "<b><u>" +
|
||||
this.name.slice(namePos, namePos + searchStr.length) + "</u></b>" +
|
||||
this.name.slice(namePos + searchStr.length);
|
||||
highlightSearchStrings(nameIdxs, descIdxs) {
|
||||
if (nameIdxs.length && typeof nameIdxs[0][0] === "number") {
|
||||
let opName = "",
|
||||
pos = 0;
|
||||
|
||||
nameIdxs.forEach(idxs => {
|
||||
const [start, length] = idxs;
|
||||
if (typeof start !== "number") return;
|
||||
opName += this.name.slice(pos, start) + "<b>" +
|
||||
this.name.slice(start, start + length) + "</b>";
|
||||
pos = start + length;
|
||||
});
|
||||
opName += this.name.slice(pos, this.name.length);
|
||||
this.name = opName;
|
||||
}
|
||||
|
||||
if (this.description && descPos >= 0) {
|
||||
if (this.description && descIdxs.length && descIdxs[0][0] >= 0) {
|
||||
// Find HTML tag offsets
|
||||
const re = /<[^>]+>/g;
|
||||
let match;
|
||||
while ((match = re.exec(this.description))) {
|
||||
// If the search string occurs within an HTML tag, return without highlighting it.
|
||||
if (descPos >= match.index && descPos <= (match.index + match[0].length))
|
||||
return;
|
||||
const inHTMLTag = descIdxs.reduce((acc, idxs) => {
|
||||
const start = idxs[0];
|
||||
return start >= match.index && start <= (match.index + match[0].length);
|
||||
}, false);
|
||||
|
||||
if (inHTMLTag) return;
|
||||
}
|
||||
|
||||
this.description = this.description.slice(0, descPos) + "<b><u>" +
|
||||
this.description.slice(descPos, descPos + searchStr.length) + "</u></b>" +
|
||||
this.description.slice(descPos + searchStr.length);
|
||||
let desc = "",
|
||||
pos = 0;
|
||||
|
||||
descIdxs.forEach(idxs => {
|
||||
const [start, length] = idxs;
|
||||
desc += this.description.slice(pos, start) + "<b><u>" +
|
||||
this.description.slice(start, start + length) + "</u></b>";
|
||||
pos = start + length;
|
||||
});
|
||||
desc += this.description.slice(pos, this.description.length);
|
||||
this.description = desc;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,21 +148,29 @@ class HTMLOperation {
|
||||
/**
|
||||
* Given a URL for a Wikipedia (or other wiki) page, this function returns a link to that page.
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string} urlStr
|
||||
* @returns {string}
|
||||
*/
|
||||
function titleFromWikiLink(url) {
|
||||
const splitURL = url.split("/");
|
||||
if (splitURL.indexOf("wikipedia.org") < 0 && splitURL.indexOf("forensicswiki.org") < 0) {
|
||||
// Not a wiki link, return full URL
|
||||
return `<a href='${url}' target='_blank'>More Information<i class='material-icons inline-icon'>open_in_new</i></a>`;
|
||||
function titleFromWikiLink(urlStr) {
|
||||
const urlObj = url.parse(urlStr);
|
||||
let wikiName = "",
|
||||
pageTitle = "";
|
||||
|
||||
switch (urlObj.host) {
|
||||
case "forensicswiki.xyz":
|
||||
wikiName = "Forensics Wiki";
|
||||
pageTitle = urlObj.query.substr(6).replace(/_/g, " "); // Chop off 'title='
|
||||
break;
|
||||
case "wikipedia.org":
|
||||
wikiName = "Wikipedia";
|
||||
pageTitle = urlObj.pathname.substr(6).replace(/_/g, " "); // Chop off '/wiki/'
|
||||
break;
|
||||
default:
|
||||
// Not a wiki link, return full URL
|
||||
return `<a href='${urlStr}' target='_blank'>More Information<i class='material-icons inline-icon'>open_in_new</i></a>`;
|
||||
}
|
||||
|
||||
const wikiName = splitURL.indexOf("forensicswiki.org") < 0 ? "Wikipedia" : "Forensics Wiki";
|
||||
|
||||
const pageTitle = decodeURIComponent(splitURL[splitURL.length - 1])
|
||||
.replace(/_/g, " ");
|
||||
return `<a href='${url}' target='_blank'>${pageTitle}<i class='material-icons inline-icon'>open_in_new</i></a> on ${wikiName}`;
|
||||
return `<a href='${urlObj.href}' target='_blank'>${pageTitle}<i class='material-icons inline-icon'>open_in_new</i></a> on ${wikiName}`;
|
||||
}
|
||||
|
||||
export default HTMLOperation;
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
<script type="text/javascript">
|
||||
// Must be text/javascript rather than application/javascript otherwise IE won't recognise it...
|
||||
if (navigator.userAgent && navigator.userAgent.match(/Trident/)) {
|
||||
document.write("Internet Explorer is not supported, please use Firefox or Chrome instead");
|
||||
document.getElementById("notice").innerHTML += "Internet Explorer is not supported, please use Firefox or Chrome instead";
|
||||
alert("Internet Explorer is not supported, please use Firefox or Chrome instead");
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -102,7 +102,7 @@ class ControlsWaiter {
|
||||
const saveLinkEl = document.getElementById("save-link");
|
||||
const saveLink = this.generateStateUrl(includeRecipe, includeInput, recipeConfig);
|
||||
|
||||
saveLinkEl.innerHTML = Utils.truncate(saveLink, 120);
|
||||
saveLinkEl.innerHTML = Utils.escapeHtml(Utils.truncate(saveLink, 120));
|
||||
saveLinkEl.setAttribute("href", saveLink);
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ class ControlsWaiter {
|
||||
|
||||
const params = [
|
||||
includeRecipe ? ["recipe", recipeStr] : undefined,
|
||||
includeInput ? ["input", input] : undefined,
|
||||
includeInput ? ["input", Utils.escapeHtml(input)] : undefined,
|
||||
];
|
||||
|
||||
const hash = params
|
||||
|
||||
@@ -510,10 +510,6 @@ class InputWaiter {
|
||||
if (inputNum !== activeTab) return;
|
||||
|
||||
const fileLoaded = document.getElementById("input-file-loaded");
|
||||
let oldProgress = fileLoaded.textContent;
|
||||
if (oldProgress !== "Error") {
|
||||
oldProgress = parseInt(oldProgress.replace("%", ""), 10);
|
||||
}
|
||||
if (progress === "error") {
|
||||
fileLoaded.textContent = "Error";
|
||||
fileLoaded.style.color = "#FF0000";
|
||||
@@ -1276,7 +1272,7 @@ class InputWaiter {
|
||||
const func = function(time) {
|
||||
if (this.mousedown) {
|
||||
this.changeTabRight();
|
||||
const newTime = (time > 50) ? time = time - 10 : 50;
|
||||
const newTime = (time > 50) ? time - 10 : 50;
|
||||
setTimeout(func.bind(this, [newTime]), newTime);
|
||||
}
|
||||
};
|
||||
@@ -1293,7 +1289,7 @@ class InputWaiter {
|
||||
const func = function(time) {
|
||||
if (this.mousedown) {
|
||||
this.changeTabLeft();
|
||||
const newTime = (time > 50) ? time = time - 10 : 50;
|
||||
const newTime = (time > 50) ? time - 10 : 50;
|
||||
setTimeout(func.bind(this, [newTime]), newTime);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import HTMLOperation from "../HTMLOperation.mjs";
|
||||
import Sortable from "sortablejs";
|
||||
import {fuzzyMatch, calcMatchRanges} from "../../core/lib/FuzzySearch.mjs";
|
||||
|
||||
|
||||
/**
|
||||
@@ -108,28 +109,33 @@ class OperationsWaiter {
|
||||
const matchedOps = [];
|
||||
const matchedDescs = [];
|
||||
|
||||
const searchStr = inStr.toLowerCase();
|
||||
|
||||
for (const opName in this.app.operations) {
|
||||
const op = this.app.operations[opName];
|
||||
const namePos = opName.toLowerCase().indexOf(searchStr);
|
||||
const descPos = op.description.toLowerCase().indexOf(searchStr);
|
||||
|
||||
if (namePos >= 0 || descPos >= 0) {
|
||||
// Match op name using fuzzy match
|
||||
const [nameMatch, score, idxs] = fuzzyMatch(inStr, opName);
|
||||
|
||||
// Match description based on exact match
|
||||
const descPos = op.description.toLowerCase().indexOf(inStr.toLowerCase());
|
||||
|
||||
if (nameMatch || descPos >= 0) {
|
||||
const operation = new HTMLOperation(opName, this.app.operations[opName], this.app, this.manager);
|
||||
if (highlight) {
|
||||
operation.highlightSearchString(searchStr, namePos, descPos);
|
||||
operation.highlightSearchStrings(calcMatchRanges(idxs), [[descPos, inStr.length]]);
|
||||
}
|
||||
|
||||
if (namePos < 0) {
|
||||
matchedOps.push(operation);
|
||||
if (nameMatch) {
|
||||
matchedOps.push([operation, score]);
|
||||
} else {
|
||||
matchedDescs.push(operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matchedDescs.concat(matchedOps);
|
||||
// Sort matched operations based on fuzzy score
|
||||
matchedOps.sort((a, b) => b[1] - a[1]);
|
||||
|
||||
return matchedOps.map(a => a[0]).concat(matchedDescs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -306,8 +306,6 @@ class OutputWaiter {
|
||||
outputText.value = "";
|
||||
outputHtml.innerHTML = "";
|
||||
|
||||
lines = 0;
|
||||
length = 0;
|
||||
this.toggleLoader(false);
|
||||
return;
|
||||
}
|
||||
@@ -765,7 +763,7 @@ class OutputWaiter {
|
||||
const func = function(time) {
|
||||
if (this.mousedown) {
|
||||
this.changeTabRight();
|
||||
const newTime = (time > 50) ? time = time - 10 : 50;
|
||||
const newTime = (time > 50) ? time - 10 : 50;
|
||||
setTimeout(func.bind(this, [newTime]), newTime);
|
||||
}
|
||||
};
|
||||
@@ -782,7 +780,7 @@ class OutputWaiter {
|
||||
const func = function(time) {
|
||||
if (this.mousedown) {
|
||||
this.changeTabLeft();
|
||||
const newTime = (time > 50) ? time = time - 10 : 50;
|
||||
const newTime = (time > 50) ? time - 10 : 50;
|
||||
setTimeout(func.bind(this, [newTime]), newTime);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -316,7 +316,7 @@ class RecipeWaiter {
|
||||
};
|
||||
} else if (ingList[j].getAttribute("type") === "number") {
|
||||
// number
|
||||
ingredients[j] = parseFloat(ingList[j].value, 10);
|
||||
ingredients[j] = parseFloat(ingList[j].value);
|
||||
} else {
|
||||
// all others
|
||||
ingredients[j] = ingList[j].value;
|
||||
|
||||
@@ -218,7 +218,7 @@ module.exports = {
|
||||
.clearValue("#search")
|
||||
.setValue("#search", "md5")
|
||||
.useXpath()
|
||||
.waitForElementVisible("//ul[@id='search-results']//u[text()='MD5']", 1000);
|
||||
.waitForElementVisible("//ul[@id='search-results']//b[text()='MD5']", 1000);
|
||||
},
|
||||
|
||||
after: browser => {
|
||||
|
||||
@@ -14,10 +14,9 @@ const outputObject = JSON.stringify({
|
||||
iat: 1
|
||||
}, null, 4);
|
||||
|
||||
const invalidAlgorithm = "JsonWebTokenError: invalid algorithm";
|
||||
|
||||
const hsKey = "secret_cat";
|
||||
const rsKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||
/* Retaining private key as a comment
|
||||
const rsPriv = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICWwIBAAKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw
|
||||
33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW
|
||||
+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB
|
||||
@@ -32,11 +31,24 @@ fSSjAkLRi54PKJ8TFUeOP15h9sQzydI8zJU+upvDEKZsZc/UhT/SySDOxQ4G/523
|
||||
Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aP
|
||||
FaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw==
|
||||
-----END RSA PRIVATE KEY-----`;
|
||||
const esKey = `-----BEGIN PRIVATE KEY-----
|
||||
*/
|
||||
const rsPub = `-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd
|
||||
UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs
|
||||
HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D
|
||||
o2kQ+X5xK9cipRgEKwIDAQAB
|
||||
-----END PUBLIC KEY-----`;
|
||||
/* Retaining private key as a comment
|
||||
const esPriv = `-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2
|
||||
OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r
|
||||
1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G
|
||||
-----END PRIVATE KEY-----`;
|
||||
*/
|
||||
const esPub = `-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9
|
||||
q9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==
|
||||
-----END PUBLIC KEY-----`;
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
@@ -53,22 +65,22 @@ TestRegister.addTests([
|
||||
{
|
||||
name: "JWT Verify: RS",
|
||||
input: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJTdHJpbmciOiJTb21lU3RyaW5nIiwiTnVtYmVyIjo0MiwiaWF0IjoxfQ.MjEJhtZk2nXzigi24piMzANmrj3mILHJcDl0xOjl5a8EgdKVL1oaMEjTkMQp5RA8YrqeRBFaX-BGGCKOXn5zPY1DJwWsBUyN9C-wGR2Qye0eogH_3b4M9EW00TPCUPXm2rx8URFj7Wg9VlsmrGzLV2oKkPgkVxuFSxnpO3yjn1Y",
|
||||
expectedOutput: invalidAlgorithm,
|
||||
expectedOutput: outputObject,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JWT Verify",
|
||||
args: [rsKey],
|
||||
args: [rsPub],
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JWT Verify: ES",
|
||||
input: "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJTdHJpbmciOiJTb21lU3RyaW5nIiwiTnVtYmVyIjo0MiwiaWF0IjoxfQ.WkECT51jSfpRkcpQ4x0h5Dwe7CFBI6u6Et2gWp91HC7mpN_qCFadRpsvJLtKubm6cJTLa68xtei0YrDD8fxIUA",
|
||||
expectedOutput: invalidAlgorithm,
|
||||
expectedOutput: outputObject,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JWT Verify",
|
||||
args: [esKey],
|
||||
args: [esPub],
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user