mirror of
https://github.com/gchq/CyberChef
synced 2024-12-28 21:43:21 +00:00
Merge branch 'gchq:master' into RAKE
This commit is contained in:
commit
8b5b17b8e0
34 changed files with 4481 additions and 11709 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,6 +3,7 @@ npm-debug.log
|
|||
travis.log
|
||||
build
|
||||
.vscode
|
||||
.idea
|
||||
.*.swp
|
||||
src/core/config/modules/*
|
||||
src/core/config/OperationConfig.json
|
||||
|
|
31
CHANGELOG.md
31
CHANGELOG.md
|
@ -13,6 +13,17 @@ All major and minor version changes will be documented in this file. Details of
|
|||
|
||||
## Details
|
||||
|
||||
### [10.7.0] - 2024-02-09
|
||||
- Added 'File Tree' operation [@sw5678] | [#1667]
|
||||
- Added 'RISON' operation [@sg5506844] | [#1555]
|
||||
- Added 'MurmurHash3' operation [@AliceGrey] | [#1694]
|
||||
|
||||
### [10.6.0] - 2024-02-03
|
||||
- Updated 'Forensics Wiki' URLs to new domain [@a3957273] | [#1703]
|
||||
- Added 'LZNT1 Decompress' operation [@0xThiebaut] | [#1675]
|
||||
- Updated 'Regex Expression' UUID matcher [@cnotin] | [#1678]
|
||||
- Removed duplicate 'hover' message within baking info [@KevinSJ] | [#1541]
|
||||
|
||||
### [10.5.0] - 2023-07-14
|
||||
- Added GOST Encrypt, Decrypt, Sign, Verify, Key Wrap, and Key Unwrap operations [@n1474335] | [#592]
|
||||
|
||||
|
@ -372,8 +383,8 @@ All major and minor version changes will be documented in this file. Details of
|
|||
## [4.0.0] - 2016-11-28
|
||||
- Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306)
|
||||
|
||||
|
||||
|
||||
[10.7.0]: https://github.com/gchq/CyberChef/releases/tag/v10.7.0
|
||||
[10.6.0]: https://github.com/gchq/CyberChef/releases/tag/v10.6.0
|
||||
[10.5.0]: https://github.com/gchq/CyberChef/releases/tag/v10.5.0
|
||||
[10.4.0]: https://github.com/gchq/CyberChef/releases/tag/v10.4.0
|
||||
[10.3.0]: https://github.com/gchq/CyberChef/releases/tag/v10.3.0
|
||||
|
@ -528,6 +539,14 @@ All major and minor version changes will be documented in this file. Details of
|
|||
[@joostrijneveld]: https://github.com/joostrijneveld
|
||||
[@Xenonym]: https://github.com/Xenonym
|
||||
[@gchq77703]: https://github.com/gchq77703
|
||||
[@a3957273]: https://github.com/a3957273
|
||||
[@0xThiebaut]: https://github.com/0xThiebaut
|
||||
[@cnotin]: https://github.com/cnotin
|
||||
[@KevinSJ]: https://github.com/KevinSJ
|
||||
[@sw5678]: https://github.com/sw5678
|
||||
[@sg5506844]: https://github.com/sg5506844
|
||||
[@AliceGrey]: https://github.com/AliceGrey
|
||||
|
||||
|
||||
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7
|
||||
[9a33498]: https://github.com/gchq/CyberChef/commit/9a33498fed26a8df9c9f35f39a78a174bf50a513
|
||||
|
@ -646,4 +665,10 @@ All major and minor version changes will be documented in this file. Details of
|
|||
[#661]: https://github.com/gchq/CyberChef/pull/661
|
||||
[#493]: https://github.com/gchq/CyberChef/pull/493
|
||||
[#592]: https://github.com/gchq/CyberChef/issues/592
|
||||
|
||||
[#1703]: https://github.com/gchq/CyberChef/issues/1703
|
||||
[#1675]: https://github.com/gchq/CyberChef/issues/1675
|
||||
[#1678]: https://github.com/gchq/CyberChef/issues/1678
|
||||
[#1541]: https://github.com/gchq/CyberChef/issues/1541
|
||||
[#1667]: https://github.com/gchq/CyberChef/issues/1667
|
||||
[#1555]: https://github.com/gchq/CyberChef/issues/1555
|
||||
[#1694]: https://github.com/gchq/CyberChef/issues/1694
|
||||
|
|
|
@ -89,14 +89,14 @@ CyberChef is built to support
|
|||
|
||||
## Node.js support
|
||||
|
||||
CyberChef is built to fully support Node.js `v16`. For more information, see the Node API page in the project [wiki pages](https://github.com/gchq/CyberChef/wiki)
|
||||
CyberChef is built to fully support Node.js `v16`. For more information, see the ["Node API" wiki page](https://github.com/gchq/CyberChef/wiki/Node-API)
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributing a new operation to CyberChef is super easy! The quickstart script will walk you through the process. If you can write basic JavaScript, you can write a CyberChef operation.
|
||||
|
||||
An installation walkthrough, how-to guides for adding new operations and themes, descriptions of the repository structure, available data types and coding conventions can all be found in the project [wiki pages](https://github.com/gchq/CyberChef/wiki).
|
||||
An installation walkthrough, how-to guides for adding new operations and themes, descriptions of the repository structure, available data types and coding conventions can all be found in the ["Contributing" wiki page](https://github.com/gchq/CyberChef/wiki/Contributing).
|
||||
|
||||
- Push your changes to your fork.
|
||||
- Submit a pull request. If you are doing this for the first time, you will be prompted to sign the [GCHQ Contributor Licence Agreement](https://cla-assistant.io/gchq/CyberChef) via the CLA assistant on the pull request. This will also ask whether you are happy for GCHQ to contact you about a token of thanks for your contribution, or about job opportunities at GCHQ.
|
||||
|
|
15048
package-lock.json
generated
15048
package-lock.json
generated
File diff suppressed because it is too large
Load diff
113
package.json
113
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cyberchef",
|
||||
"version": "10.5.2",
|
||||
"version": "10.7.0",
|
||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||
"author": "n1474335 <n1474335@gmail.com>",
|
||||
"homepage": "https://gchq.github.io/CyberChef",
|
||||
|
@ -39,55 +39,55 @@
|
|||
"node >= 16"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.21.0",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"@babel/plugin-syntax-import-assertions": "^7.20.0",
|
||||
"@babel/plugin-transform-runtime": "^7.21.0",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/runtime": "^7.21.0",
|
||||
"@codemirror/commands": "^6.2.1",
|
||||
"@codemirror/language": "^6.6.0",
|
||||
"@codemirror/search": "^6.2.3",
|
||||
"@codemirror/state": "^6.2.0",
|
||||
"@codemirror/view": "^6.9.2",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"babel-loader": "^9.1.2",
|
||||
"@babel/core": "^7.23.9",
|
||||
"@babel/eslint-parser": "^7.23.10",
|
||||
"@babel/plugin-syntax-import-assertions": "^7.23.3",
|
||||
"@babel/plugin-transform-runtime": "^7.23.9",
|
||||
"@babel/preset-env": "^7.23.9",
|
||||
"@babel/runtime": "^7.23.9",
|
||||
"@codemirror/commands": "^6.3.3",
|
||||
"@codemirror/language": "^6.10.1",
|
||||
"@codemirror/search": "^6.5.5",
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"@codemirror/view": "^6.23.1",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-transform-builtin-extend": "1.1.2",
|
||||
"base64-loader": "^1.0.0",
|
||||
"chromedriver": "^114.0.2",
|
||||
"chromedriver": "^121.0.0",
|
||||
"cli-progress": "^3.12.0",
|
||||
"colors": "^1.4.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"core-js": "^3.29.0",
|
||||
"css-loader": "6.7.3",
|
||||
"eslint": "^8.35.0",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"core-js": "^3.35.1",
|
||||
"css-loader": "6.10.0",
|
||||
"eslint": "^8.56.0",
|
||||
"grunt": "^1.6.1",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
"grunt-concurrent": "^3.0.0",
|
||||
"grunt-contrib-clean": "~2.0.1",
|
||||
"grunt-contrib-connect": "^3.0.0",
|
||||
"grunt-contrib-connect": "^4.0.0",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-eslint": "^24.0.1",
|
||||
"grunt-eslint": "^24.3.0",
|
||||
"grunt-exec": "~3.0.0",
|
||||
"grunt-webpack": "^5.0.0",
|
||||
"grunt-zip": "^0.20.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"imports-loader": "^4.0.1",
|
||||
"mini-css-extract-plugin": "2.7.3",
|
||||
"grunt-webpack": "^6.0.0",
|
||||
"grunt-zip": "^1.0.0",
|
||||
"html-webpack-plugin": "^5.6.0",
|
||||
"imports-loader": "^5.0.0",
|
||||
"mini-css-extract-plugin": "2.8.0",
|
||||
"modify-source-webpack-plugin": "^3.0.0",
|
||||
"nightwatch": "^2.6.16",
|
||||
"postcss": "^8.4.21",
|
||||
"postcss-css-variables": "^0.18.0",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-loader": "^7.0.2",
|
||||
"nightwatch": "^3.4.0",
|
||||
"postcss": "^8.4.33",
|
||||
"postcss-css-variables": "^0.19.0",
|
||||
"postcss-import": "^16.0.0",
|
||||
"postcss-loader": "^8.1.0",
|
||||
"prompt": "^1.3.0",
|
||||
"sitemap": "^7.1.1",
|
||||
"terser": "^5.16.6",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-bundle-analyzer": "^4.8.0",
|
||||
"webpack-dev-server": "4.11.1",
|
||||
"terser": "^5.27.0",
|
||||
"webpack": "^5.90.1",
|
||||
"webpack-bundle-analyzer": "^4.10.1",
|
||||
"webpack-dev-server": "4.15.1",
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
"worker-loader": "^3.0.8"
|
||||
},
|
||||
|
@ -100,7 +100,7 @@
|
|||
"arrive": "^2.4.1",
|
||||
"avsc": "^5.7.7",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bignumber.js": "^9.1.1",
|
||||
"bignumber.js": "^9.1.2",
|
||||
"blakejs": "^1.2.1",
|
||||
"bootstrap": "4.6.2",
|
||||
"bootstrap-colorpicker": "^3.4.0",
|
||||
|
@ -108,45 +108,45 @@
|
|||
"browserify-zlib": "^0.2.0",
|
||||
"bson": "^4.7.2",
|
||||
"buffer": "^6.0.3",
|
||||
"cbor": "8.1.0",
|
||||
"cbor": "9.0.2",
|
||||
"chi-squared": "^1.1.0",
|
||||
"codepage": "^1.15.0",
|
||||
"crypto-api": "^0.8.5",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"crypto-js": "^4.1.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"ctph.js": "0.0.5",
|
||||
"d3": "7.8.2",
|
||||
"d3": "7.8.5",
|
||||
"d3-hexbin": "^0.2.2",
|
||||
"diff": "^5.1.0",
|
||||
"es6-promisify": "^7.0.0",
|
||||
"escodegen": "^2.0.0",
|
||||
"escodegen": "^2.1.0",
|
||||
"esprima": "^4.0.1",
|
||||
"exif-parser": "^0.1.12",
|
||||
"file-saver": "^2.0.5",
|
||||
"flat": "^5.0.2",
|
||||
"flat": "^6.0.1",
|
||||
"geodesy": "1.1.3",
|
||||
"highlight.js": "^11.7.0",
|
||||
"highlight.js": "^11.9.0",
|
||||
"jimp": "^0.16.13",
|
||||
"jquery": "3.6.4",
|
||||
"jquery": "3.7.1",
|
||||
"js-crc": "^0.2.0",
|
||||
"js-sha3": "^0.8.0",
|
||||
"js-sha3": "^0.9.3",
|
||||
"jsesc": "^3.0.2",
|
||||
"json5": "^2.2.3",
|
||||
"jsonpath-plus": "^7.2.0",
|
||||
"jsonpath-plus": "^8.0.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"jsqr": "^1.4.0",
|
||||
"jsrsasign": "^10.6.1",
|
||||
"jsrsasign": "^11.1.0",
|
||||
"kbpgp": "2.1.15",
|
||||
"libbzip2-wasm": "0.0.4",
|
||||
"libyara-wasm": "^1.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
"loglevel": "^1.8.1",
|
||||
"loglevel": "^1.9.1",
|
||||
"loglevel-message-prefix": "^3.0.0",
|
||||
"lz-string": "^1.5.0",
|
||||
"lz4js": "^0.2.0",
|
||||
"markdown-it": "^13.0.1",
|
||||
"moment": "^2.29.4",
|
||||
"moment-timezone": "^0.5.41",
|
||||
"markdown-it": "^14.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.44",
|
||||
"ngeohash": "^0.6.3",
|
||||
"node-forge": "^1.3.1",
|
||||
"node-md6": "^0.1.0",
|
||||
|
@ -158,22 +158,23 @@
|
|||
"path": "^0.12.7",
|
||||
"popper.js": "^1.16.1",
|
||||
"process": "^0.11.10",
|
||||
"protobufjs": "^7.2.2",
|
||||
"protobufjs": "^7.2.6",
|
||||
"qr-image": "^3.2.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"reflect-metadata": "^0.2.1",
|
||||
"rison": "^0.1.1",
|
||||
"scryptsy": "^2.1.0",
|
||||
"snackbarjs": "^1.1.0",
|
||||
"sortablejs": "^1.15.0",
|
||||
"sortablejs": "^1.15.2",
|
||||
"split.js": "^1.6.5",
|
||||
"ssdeep.js": "0.0.3",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"tesseract.js": "3.0.3",
|
||||
"ua-parser-js": "^1.0.34",
|
||||
"tesseract.js": "5.0.4",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"unorm": "^1.6.0",
|
||||
"utf8": "^3.0.0",
|
||||
"vkbeautify": "^0.99.3",
|
||||
"xmldom": "^0.6.0",
|
||||
"xpath": "0.0.32",
|
||||
"xpath": "0.0.34",
|
||||
"xregexp": "^5.1.1",
|
||||
"zlibjs": "^0.3.1"
|
||||
},
|
||||
|
|
|
@ -892,6 +892,23 @@ class Utils {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a string to it's title case equivalent.
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns string
|
||||
*
|
||||
* @example
|
||||
* // return "A Tiny String"
|
||||
* Utils.toTitleCase("a tIny String");
|
||||
*/
|
||||
static toTitleCase(str) {
|
||||
return str.replace(/\w\S*/g, function(txt) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a URI fragment (#) or query (?) using a minimal amount of percent-encoding.
|
||||
*
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
"To Base64",
|
||||
"From Base64",
|
||||
"Show Base64 offsets",
|
||||
"To Base92",
|
||||
"From Base92",
|
||||
"To Base85",
|
||||
"From Base85",
|
||||
"To Base",
|
||||
|
@ -67,7 +69,9 @@
|
|||
"JSON to CSV",
|
||||
"Avro to JSON",
|
||||
"CBOR Encode",
|
||||
"CBOR Decode"
|
||||
"CBOR Decode",
|
||||
"Rison Encode",
|
||||
"Rison Decode"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -294,7 +298,8 @@
|
|||
"Escape string",
|
||||
"Unescape string",
|
||||
"Pseudo-Random Number Generator",
|
||||
"Sleep"
|
||||
"Sleep",
|
||||
"File Tree"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -352,7 +357,8 @@
|
|||
"LZMA Decompress",
|
||||
"LZMA Compress",
|
||||
"LZ4 Decompress",
|
||||
"LZ4 Compress"
|
||||
"LZ4 Compress",
|
||||
"LZNT1 Decompress"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -369,6 +375,7 @@
|
|||
"SHA2",
|
||||
"SHA3",
|
||||
"SM3",
|
||||
"MurmurHash3",
|
||||
"Keccak",
|
||||
"Shake",
|
||||
"RIPEMD",
|
||||
|
|
44
src/core/lib/Base92.mjs
Normal file
44
src/core/lib/Base92.mjs
Normal file
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Base92 resources.
|
||||
*
|
||||
* @author sg5506844 [sg5506844@gmail.com]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Base92 alphabet char
|
||||
*
|
||||
* @param {number} val
|
||||
* @returns {number}
|
||||
*/
|
||||
export function base92Chr(val) {
|
||||
if (val < 0 || val >= 91) {
|
||||
throw new OperationError("Invalid value");
|
||||
}
|
||||
if (val === 0)
|
||||
return "!".charCodeAt(0);
|
||||
else if (val <= 61)
|
||||
return "#".charCodeAt(0) + val - 1;
|
||||
else
|
||||
return "a".charCodeAt(0) + val - 62;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base92 alphabet ord
|
||||
*
|
||||
* @param {string} val
|
||||
* @returns {number}
|
||||
*/
|
||||
export function base92Ord(val) {
|
||||
if (val === "!")
|
||||
return 0;
|
||||
else if ("#" <= val && val <= "_")
|
||||
return val.charCodeAt(0) - "#".charCodeAt(0) + 1;
|
||||
else if ("a" <= val && val <= "}")
|
||||
return val.charCodeAt(0) - "a".charCodeAt(0) + 62;
|
||||
throw new OperationError(`${val} is not a base92 character`);
|
||||
}
|
||||
|
88
src/core/lib/LZNT1.mjs
Normal file
88
src/core/lib/LZNT1.mjs
Normal file
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
*
|
||||
* LZNT1 Decompress.
|
||||
*
|
||||
* @author 0xThiebaut [thiebaut.dev]
|
||||
* @copyright Crown Copyright 2023
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* https://github.com/Velocidex/go-ntfs/blob/master/parser%2Flznt1.go
|
||||
*/
|
||||
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
const COMPRESSED_MASK = 1 << 15,
|
||||
SIZE_MASK = (1 << 12) - 1;
|
||||
|
||||
/**
|
||||
* @param {number} offset
|
||||
* @returns {number}
|
||||
*/
|
||||
function getDisplacement(offset) {
|
||||
let result = 0;
|
||||
while (offset >= 0x10) {
|
||||
offset >>= 1;
|
||||
result += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} compressed
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
export function decompress(compressed) {
|
||||
const decompressed = Array();
|
||||
let coffset = 0;
|
||||
|
||||
while (coffset + 2 <= compressed.length) {
|
||||
const doffset = decompressed.length;
|
||||
|
||||
const blockHeader = Utils.byteArrayToInt(compressed.slice(coffset, coffset + 2), "little");
|
||||
coffset += 2;
|
||||
|
||||
const size = blockHeader & SIZE_MASK;
|
||||
const blockEnd = coffset + size + 1;
|
||||
|
||||
if (size === 0) {
|
||||
break;
|
||||
} else if (compressed.length < coffset + size) {
|
||||
throw new OperationError("Malformed LZNT1 stream: Block too small! Has the stream been truncated?");
|
||||
}
|
||||
|
||||
if ((blockHeader & COMPRESSED_MASK) !== 0) {
|
||||
while (coffset < blockEnd) {
|
||||
let header = compressed[coffset++];
|
||||
|
||||
for (let i = 0; i < 8 && coffset < blockEnd; i++) {
|
||||
if ((header & 1) === 0) {
|
||||
decompressed.push(compressed[coffset++]);
|
||||
} else {
|
||||
const pointer = Utils.byteArrayToInt(compressed.slice(coffset, coffset + 2), "little");
|
||||
coffset += 2;
|
||||
|
||||
const displacement = getDisplacement(decompressed.length - doffset - 1);
|
||||
const symbolOffset = (pointer >> (12 - displacement)) + 1;
|
||||
const symbolLength = (pointer & (0xFFF >> displacement)) + 2;
|
||||
const shiftOffset = decompressed.length - symbolOffset;
|
||||
|
||||
for (let shiftDelta = 0; shiftDelta < symbolLength + 1; shiftDelta++) {
|
||||
const shift = shiftOffset + shiftDelta;
|
||||
if (shift < 0 || decompressed.length <= shift) {
|
||||
throw new OperationError("Malformed LZNT1 stream: Invalid shift!");
|
||||
}
|
||||
decompressed.push(decompressed[shift]);
|
||||
}
|
||||
}
|
||||
header >>= 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
decompressed.push(...compressed.slice(coffset, coffset + size + 1));
|
||||
coffset += size + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return decompressed;
|
||||
}
|
|
@ -21,7 +21,7 @@ class CTPH extends Operation {
|
|||
this.name = "CTPH";
|
||||
this.module = "Crypto";
|
||||
this.description = "Context Triggered Piecewise Hashing, also called Fuzzy Hashing, can match inputs that have homologies. Such inputs have sequences of identical bytes in the same order, although bytes in between these sequences may be different in both content and length.<br><br>CTPH was originally based on the work of Dr. Andrew Tridgell and a spam email detector called SpamSum. This method was adapted by Jesse Kornblum and published at the DFRWS conference in 2006 in a paper 'Identifying Almost Identical Files Using Context Triggered Piecewise Hashing'.";
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Context_Triggered_Piecewise_Hashing";
|
||||
this.infoURL = "https://forensics.wiki/context_triggered_piecewise_hashing/";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
|
|
|
@ -24,7 +24,7 @@ class CompareCTPHHashes extends Operation {
|
|||
this.name = "Compare CTPH hashes";
|
||||
this.module = "Crypto";
|
||||
this.description = "Compares two Context Triggered Piecewise Hashing (CTPH) fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Context_Triggered_Piecewise_Hashing";
|
||||
this.infoURL = "https://forensics.wiki/context_triggered_piecewise_hashing/";
|
||||
this.inputType = "string";
|
||||
this.outputType = "Number";
|
||||
this.args = [
|
||||
|
|
|
@ -24,7 +24,7 @@ class CompareSSDEEPHashes extends Operation {
|
|||
this.name = "Compare SSDEEP hashes";
|
||||
this.module = "Crypto";
|
||||
this.description = "Compares two SSDEEP fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Ssdeep";
|
||||
this.infoURL = "https://forensics.wiki/ssdeep/";
|
||||
this.inputType = "string";
|
||||
this.outputType = "Number";
|
||||
this.args = [
|
||||
|
|
|
@ -39,7 +39,7 @@ class ExtractFiles extends Operation {
|
|||
${supportedExts.join("</li><li>")}
|
||||
</li>
|
||||
</ul>Minimum File Size can be used to prune small false positives.`;
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=File_Carving";
|
||||
this.infoURL = "https://forensics.wiki/file_carving";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "List<File>";
|
||||
this.presentType = "html";
|
||||
|
|
|
@ -66,7 +66,7 @@ class ExtractIPAddresses extends Operation {
|
|||
run(input, args) {
|
||||
const [includeIpv4, includeIpv6, removeLocal, displayTotal, sort, unique] = args,
|
||||
ipv4 = "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?",
|
||||
ipv6 = "((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})((([\\dA-F]{1,4}((?!\\3)::|:\\b|(?![\\dA-F])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})";
|
||||
ipv6 = "((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})(([\\dA-F]{1,4}((?!\\3)::|:\\b|(?![\\dA-F])))|(?!\\2\\3)){2}";
|
||||
let ips = "";
|
||||
|
||||
if (includeIpv4 && includeIpv6) {
|
||||
|
|
93
src/core/operations/FileTree.mjs
Normal file
93
src/core/operations/FileTree.mjs
Normal file
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* @author sw5678
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import {INPUT_DELIM_OPTIONS} from "../lib/Delim.mjs";
|
||||
|
||||
/**
|
||||
* Unique operation
|
||||
*/
|
||||
class FileTree extends Operation {
|
||||
|
||||
/**
|
||||
* Unique constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "File Tree";
|
||||
this.module = "Default";
|
||||
this.description = "Creates file tree from list of file paths (similar to the tree command in Linux)";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "File Path Delimiter",
|
||||
type: "binaryString",
|
||||
value: "/"
|
||||
},
|
||||
{
|
||||
name: "Delimiter",
|
||||
type: "option",
|
||||
value: INPUT_DELIM_OPTIONS
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
|
||||
// Set up arrow and pipe for nice output display
|
||||
const ARROW = "|---";
|
||||
const PIPE = "| ";
|
||||
|
||||
// Get args from input
|
||||
const fileDelim = args[0];
|
||||
const entryDelim = Utils.charRep(args[1]);
|
||||
|
||||
// Store path to print
|
||||
const completedList = [];
|
||||
const printList = [];
|
||||
|
||||
// Loop through all entries
|
||||
const filePaths = input.split(entryDelim).unique().sort();
|
||||
for (let i = 0; i < filePaths.length; i++) {
|
||||
// Split by file delimiter
|
||||
let path = filePaths[i].split(fileDelim);
|
||||
|
||||
if (path[0] === "") {
|
||||
path = path.slice(1, path.length);
|
||||
}
|
||||
|
||||
for (let j = 0; j < path.length; j++) {
|
||||
let printLine;
|
||||
let key;
|
||||
if (j === 0) {
|
||||
printLine = path[j];
|
||||
key = path[j];
|
||||
} else {
|
||||
printLine = PIPE.repeat(j-1) + ARROW + path[j];
|
||||
key = path.slice(0, j+1).join("/");
|
||||
}
|
||||
|
||||
// Check to see we have already added that path
|
||||
if (!completedList.includes(key)) {
|
||||
completedList.push(key);
|
||||
printList.push(printLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
return printList.join("\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default FileTree;
|
55
src/core/operations/FromBase92.mjs
Normal file
55
src/core/operations/FromBase92.mjs
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* @author sg5506844 [sg5506844@gmail.com]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import { base92Ord } from "../lib/Base92.mjs";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* From Base92 operation
|
||||
*/
|
||||
class FromBase92 extends Operation {
|
||||
/**
|
||||
* FromBase92 constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "From Base92";
|
||||
this.module = "Default";
|
||||
this.description = "Base92 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
const res = [];
|
||||
let bitString = "";
|
||||
|
||||
for (let i = 0; i < input.length; i += 2) {
|
||||
if (i + 1 !== input.length) {
|
||||
const x = base92Ord(input[i]) * 91 + base92Ord(input[i + 1]);
|
||||
bitString += x.toString(2).padStart(13, "0");
|
||||
} else {
|
||||
const x = base92Ord(input[i]);
|
||||
bitString += x.toString(2).padStart(6, "0");
|
||||
}
|
||||
while (bitString.length >= 8) {
|
||||
res.push(parseInt(bitString.slice(0, 8), 2));
|
||||
bitString = bitString.slice(8);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
export default FromBase92;
|
41
src/core/operations/LZNT1Decompress.mjs
Normal file
41
src/core/operations/LZNT1Decompress.mjs
Normal file
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* @author 0xThiebaut [thiebaut.dev]
|
||||
* @copyright Crown Copyright 2023
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import {decompress} from "../lib/LZNT1.mjs";
|
||||
|
||||
/**
|
||||
* LZNT1 Decompress operation
|
||||
*/
|
||||
class LZNT1Decompress extends Operation {
|
||||
|
||||
/**
|
||||
* LZNT1 Decompress constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "LZNT1 Decompress";
|
||||
this.module = "Compression";
|
||||
this.description = "Decompresses data using the LZNT1 algorithm.<br><br>Similar to the Windows API <code>RtlDecompressBuffer</code>.";
|
||||
this.infoURL = "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-xca/5655f4a3-6ba4-489b-959f-e1f407c52f15";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
return decompress(input);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default LZNT1Decompress;
|
143
src/core/operations/MurmurHash3.mjs
Normal file
143
src/core/operations/MurmurHash3.mjs
Normal file
|
@ -0,0 +1,143 @@
|
|||
/**
|
||||
* Based on murmurhash-js (https://github.com/garycourt/murmurhash-js)
|
||||
* @author Gary Court
|
||||
* @license MIT
|
||||
*
|
||||
* @author AliceGrey [alice@grey.systems]
|
||||
* @copyright Crown Copyright 2024
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* MurmurHash3 operation
|
||||
*/
|
||||
class MurmurHash3 extends Operation {
|
||||
|
||||
/**
|
||||
* MurmurHash3 constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "MurmurHash3";
|
||||
this.module = "Default";
|
||||
this.description = "Generates a MurmurHash v3 for a string input and an optional seed input";
|
||||
this.infoURL = "https://wikipedia.org/wiki/MurmurHash";
|
||||
this.inputType = "string";
|
||||
this.outputType = "number";
|
||||
this.args = [
|
||||
{
|
||||
name: "Seed",
|
||||
type: "number",
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: "Convert to Signed",
|
||||
type: "boolean",
|
||||
value: false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the MurmurHash3 hash of the input.
|
||||
* Based on Gary Court's JS MurmurHash implementation
|
||||
* @see http://github.com/garycourt/murmurhash-js
|
||||
* @author AliceGrey [alice@grey.systems]
|
||||
* @param {string} input ASCII only
|
||||
* @param {number} seed Positive integer only
|
||||
* @return {number} 32-bit positive integer hash
|
||||
*/
|
||||
mmh3(input, seed) {
|
||||
let h1b;
|
||||
let k1;
|
||||
const remainder = input.length & 3; // input.length % 4
|
||||
const bytes = input.length - remainder;
|
||||
let h1 = seed;
|
||||
const c1 = 0xcc9e2d51;
|
||||
const c2 = 0x1b873593;
|
||||
let i = 0;
|
||||
|
||||
while (i < bytes) {
|
||||
k1 =
|
||||
((input.charCodeAt(i) & 0xff)) |
|
||||
((input.charCodeAt(++i) & 0xff) << 8) |
|
||||
((input.charCodeAt(++i) & 0xff) << 16) |
|
||||
((input.charCodeAt(++i) & 0xff) << 24);
|
||||
++i;
|
||||
|
||||
k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff;
|
||||
k1 = (k1 << 15) | (k1 >>> 17);
|
||||
k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = (h1 << 13) | (h1 >>> 19);
|
||||
h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff;
|
||||
h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16));
|
||||
}
|
||||
|
||||
k1 = 0;
|
||||
|
||||
if (remainder === 3) {
|
||||
k1 ^= (input.charCodeAt(i + 2) & 0xff) << 16;
|
||||
}
|
||||
|
||||
if (remainder === 3 || remainder === 2) {
|
||||
k1 ^= (input.charCodeAt(i + 1) & 0xff) << 8;
|
||||
}
|
||||
|
||||
if (remainder === 3 || remainder === 2 || remainder === 1) {
|
||||
k1 ^= (input.charCodeAt(i) & 0xff);
|
||||
|
||||
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
|
||||
k1 = (k1 << 15) | (k1 >>> 17);
|
||||
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
|
||||
h1 ^= k1;
|
||||
}
|
||||
|
||||
h1 ^= input.length;
|
||||
|
||||
h1 ^= h1 >>> 16;
|
||||
h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
|
||||
h1 ^= h1 >>> 13;
|
||||
h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff;
|
||||
h1 ^= h1 >>> 16;
|
||||
|
||||
return h1 >>> 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an unsigned 32-bit integer to a signed 32-bit integer
|
||||
* @author AliceGrey [alice@grey.systems]
|
||||
* @param {value} 32-bit unsigned integer
|
||||
* @return {number} 32-bit signed integer
|
||||
*/
|
||||
unsignedToSigned(value) {
|
||||
if (value & 0x80000000) {
|
||||
return -0x100000000 + value;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
run(input, args) {
|
||||
if (args && args.length >= 1) {
|
||||
const seed = args[0];
|
||||
const hash = this.mmh3(input, seed);
|
||||
if (args.length > 1 && args[1]) {
|
||||
return this.unsignedToSigned(hash);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
return this.mmh3(input);
|
||||
}
|
||||
}
|
||||
|
||||
export default MurmurHash3;
|
|
@ -20,7 +20,7 @@ class ParseASN1HexString extends Operation {
|
|||
|
||||
this.name = "Parse ASN.1 hex string";
|
||||
this.module = "PublicKey";
|
||||
this.description = "Abstract Syntax Notation One (ASN.1) is a standard and notation that describes rules and structures for representing, encoding, transmitting, and decoding data in telecommunications and computer networking.<br><br>This operation parses arbitrary ASN.1 data and presents the resulting tree.";
|
||||
this.description = "Abstract Syntax Notation One (ASN.1) is a standard and notation that describes rules and structures for representing, encoding, transmitting, and decoding data in telecommunications and computer networking.<br><br>This operation parses arbitrary ASN.1 data (encoded as an hex string: use the 'To Hex' operation if necessary) and presents the resulting tree.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Abstract_Syntax_Notation_One";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
|
|
|
@ -83,6 +83,10 @@ class RegularExpression extends Operation {
|
|||
name: "Strings",
|
||||
value: "[A-Za-z\\d/\\-:.,_$%\\x27\"()<>= !\\[\\]{}@]{4,}"
|
||||
},
|
||||
{
|
||||
name: "UUID (any version)",
|
||||
value: "[0-9a-fA-F]{8}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{12}"
|
||||
},
|
||||
],
|
||||
"target": 1
|
||||
},
|
||||
|
|
60
src/core/operations/RisonDecode.mjs
Normal file
60
src/core/operations/RisonDecode.mjs
Normal file
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* @author sg5506844 [sg5506844@gmail.com]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import rison from "rison";
|
||||
|
||||
/**
|
||||
* Rison Decode operation
|
||||
*/
|
||||
class RisonDecode extends Operation {
|
||||
|
||||
/**
|
||||
* RisonDecode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Rison Decode";
|
||||
this.module = "Default";
|
||||
this.description = "Rison, a data serialization format optimized for compactness in URIs. Rison is a slight variation of JSON that looks vastly superior after URI encoding. Rison still expresses exactly the same set of data structures as JSON, so data can be translated back and forth without loss or guesswork.";
|
||||
this.infoURL = "https://github.com/Nanonid/rison";
|
||||
this.inputType = "string";
|
||||
this.outputType = "Object";
|
||||
this.args = [
|
||||
{
|
||||
name: "Decode Option",
|
||||
type: "editableOption",
|
||||
value: [
|
||||
{ name: "Decode", value: "Decode", },
|
||||
{ name: "Decode Object", value: "Decode Object", },
|
||||
{ name: "Decode Array", value: "Decode Array", },
|
||||
]
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {Object}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [decodeOption] = args;
|
||||
switch (decodeOption) {
|
||||
case "Decode":
|
||||
return rison.decode(input);
|
||||
case "Decode Object":
|
||||
return rison.decode_object(input);
|
||||
case "Decode Array":
|
||||
return rison.decode_array(input);
|
||||
}
|
||||
throw new OperationError("Invalid Decode option");
|
||||
}
|
||||
}
|
||||
|
||||
export default RisonDecode;
|
63
src/core/operations/RisonEncode.mjs
Normal file
63
src/core/operations/RisonEncode.mjs
Normal file
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* @author sg5506844 [sg5506844@gmail.com]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import rison from "rison";
|
||||
|
||||
/**
|
||||
* Rison Encode operation
|
||||
*/
|
||||
class RisonEncode extends Operation {
|
||||
|
||||
/**
|
||||
* RisonEncode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Rison Encode";
|
||||
this.module = "Default";
|
||||
this.description = "Rison, a data serialization format optimized for compactness in URIs. Rison is a slight variation of JSON that looks vastly superior after URI encoding. Rison still expresses exactly the same set of data structures as JSON, so data can be translated back and forth without loss or guesswork.";
|
||||
this.infoURL = "https://github.com/Nanonid/rison";
|
||||
this.inputType = "Object";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Encode Option",
|
||||
type: "editableOption",
|
||||
value: [
|
||||
{ name: "Encode", value: "Encode", },
|
||||
{ name: "Encode Object", value: "Encode Object", },
|
||||
{ name: "Encode Array", value: "Encode Array", },
|
||||
{ name: "Encode URI", value: "Encode URI", }
|
||||
]
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [encodeOption] = args;
|
||||
switch (encodeOption) {
|
||||
case "Encode":
|
||||
return rison.encode(input);
|
||||
case "Encode Object":
|
||||
return rison.encode_object(input);
|
||||
case "Encode Array":
|
||||
return rison.encode_array(input);
|
||||
case "Encode URI":
|
||||
return rison.encode_uri(input);
|
||||
}
|
||||
throw new OperationError("Invalid encode option");
|
||||
}
|
||||
}
|
||||
|
||||
export default RisonEncode;
|
|
@ -21,7 +21,7 @@ class SSDEEP extends Operation {
|
|||
this.name = "SSDEEP";
|
||||
this.module = "Crypto";
|
||||
this.description = "SSDEEP is a program for computing context triggered piecewise hashes (CTPH). Also called fuzzy hashes, CTPH can match inputs that have homologies. Such inputs have sequences of identical bytes in the same order, although bytes in between these sequences may be different in both content and length.<br><br>SSDEEP hashes are now widely used for simple identification purposes (e.g. the 'Basic Properties' section in VirusTotal). Although 'better' fuzzy hashes are available, SSDEEP is still one of the primary choices because of its speed and being a de facto standard.<br><br>This operation is fundamentally the same as the CTPH operation, however their outputs differ in format.";
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Ssdeep";
|
||||
this.infoURL = "https://forensics.wiki/ssdeep";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
|
|
67
src/core/operations/ToBase92.mjs
Normal file
67
src/core/operations/ToBase92.mjs
Normal file
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* @author sg5506844 [sg5506844@gmail.com]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import { base92Chr } from "../lib/Base92.mjs";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* To Base92 operation
|
||||
*/
|
||||
class ToBase92 extends Operation {
|
||||
/**
|
||||
* ToBase92 constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "To Base92";
|
||||
this.module = "Default";
|
||||
this.description = "Base92 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
const res = [];
|
||||
let bitString = "";
|
||||
|
||||
while (input.length > 0) {
|
||||
while (bitString.length < 13 && input.length > 0) {
|
||||
bitString += input[0].charCodeAt(0).toString(2).padStart(8, "0");
|
||||
input = input.slice(1);
|
||||
}
|
||||
if (bitString.length < 13)
|
||||
break;
|
||||
const i = parseInt(bitString.slice(0, 13), 2);
|
||||
res.push(base92Chr(Math.floor(i / 91)));
|
||||
res.push(base92Chr(i % 91));
|
||||
bitString = bitString.slice(13);
|
||||
}
|
||||
|
||||
if (bitString.length > 0) {
|
||||
if (bitString.length < 7) {
|
||||
bitString = bitString.padEnd(6, "0");
|
||||
res.push(base92Chr(parseInt(bitString, 2)));
|
||||
} else {
|
||||
bitString = bitString.padEnd(13, "0");
|
||||
const i = parseInt(bitString.slice(0, 13), 2);
|
||||
res.push(base92Chr(Math.floor(i / 91)));
|
||||
res.push(base92Chr(i % 91));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default ToBase92;
|
|
@ -157,9 +157,9 @@ function titleFromWikiLink(urlStr) {
|
|||
pageTitle = "";
|
||||
|
||||
switch (urlObj.host) {
|
||||
case "forensicswiki.xyz":
|
||||
case "forensics.wiki":
|
||||
wikiName = "Forensics Wiki";
|
||||
pageTitle = urlObj.query.substr(6).replace(/_/g, " "); // Chop off 'title='
|
||||
pageTitle = Utils.toTitleCase(urlObj.path.replace(/\//g, "").replace(/_/g, " "));
|
||||
break;
|
||||
case "wikipedia.org":
|
||||
wikiName = "Wikipedia";
|
||||
|
|
|
@ -62,7 +62,8 @@
|
|||
"Training branch predictor...",
|
||||
"Timing cache hits...",
|
||||
"Speculatively executing recipes...",
|
||||
"Adding LLM hallucinations..."
|
||||
"Adding LLM hallucinations...",
|
||||
"Decompressing malware..."
|
||||
];
|
||||
|
||||
// Shuffle array using Durstenfeld algorithm
|
||||
|
|
|
@ -275,7 +275,6 @@ class StatusBarPanel {
|
|||
bakingTime.textContent = this.timing.duration(this.tabNumGetter());
|
||||
|
||||
const info = this.timing.printStages(this.tabNumGetter()).replace(/\n/g, "<br>");
|
||||
bakingTimeInfo.setAttribute("title", info);
|
||||
bakingTimeInfo.setAttribute("data-original-title", info);
|
||||
} else {
|
||||
bakingTimeInfo.style.display = "none";
|
||||
|
|
|
@ -635,6 +635,10 @@ WWFkYSBZYWRh\r
|
|||
assert.strictEqual(chef.keccak("Flea Market").toString(), "c2a06880b19e453ee5440e8bd4c2024bedc15a6630096aa3f609acfd2b8f15f27cd293e1cc73933e81432269129ce954a6138889ce87831179d55dcff1cc7587");
|
||||
}),
|
||||
|
||||
it("LZNT1 Decompress", () => {
|
||||
assert.strictEqual(chef.LZNT1Decompress("\x1a\xb0\x00compress\x00edtestda\x04ta\x07\x88alot").toString(), "compressedtestdatacompressedalot");
|
||||
}),
|
||||
|
||||
it("MD6", () => {
|
||||
assert.strictEqual(chef.MD6("Head Over Heels", {key: "arty"}).toString(), "d8f7fe4931fbaa37316f76283d5f615f50ddd54afdc794b61da522556aee99ad");
|
||||
}),
|
||||
|
|
|
@ -25,6 +25,7 @@ import "./tests/Base58.mjs";
|
|||
import "./tests/Base64.mjs";
|
||||
import "./tests/Base62.mjs";
|
||||
import "./tests/Base85.mjs";
|
||||
import "./tests/Base92.mjs";
|
||||
import "./tests/BitwiseOp.mjs";
|
||||
import "./tests/ByteRepr.mjs";
|
||||
import "./tests/CartesianProduct.mjs";
|
||||
|
@ -62,9 +63,11 @@ import "./tests/JSONtoCSV.mjs";
|
|||
import "./tests/JWTDecode.mjs";
|
||||
import "./tests/JWTSign.mjs";
|
||||
import "./tests/JWTVerify.mjs";
|
||||
import "./tests/LZNT1Decompress.mjs";
|
||||
import "./tests/MS.mjs";
|
||||
import "./tests/Magic.mjs";
|
||||
import "./tests/MorseCode.mjs";
|
||||
import "./tests/MurmurHash3.mjs";
|
||||
import "./tests/NetBIOS.mjs";
|
||||
import "./tests/NormaliseUnicode.mjs";
|
||||
import "./tests/OTP.mjs";
|
||||
|
@ -114,6 +117,7 @@ import "./tests/Unicode.mjs";
|
|||
import "./tests/RSA.mjs";
|
||||
import "./tests/CBOREncode.mjs";
|
||||
import "./tests/CBORDecode.mjs";
|
||||
import "./tests/RisonEncodeDecode.mjs";
|
||||
import "./tests/JA3Fingerprint.mjs";
|
||||
import "./tests/JA3SFingerprint.mjs";
|
||||
import "./tests/HASSH.mjs";
|
||||
|
|
89
tests/operations/tests/Base92.mjs
Normal file
89
tests/operations/tests/Base92.mjs
Normal file
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* Base92 tests.
|
||||
*
|
||||
* @author sg5506844 [sg5506844@gmail.com]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "To Base92: nothing",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Base92",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Base92: Spec encoding example 1",
|
||||
input: "AB",
|
||||
expectedOutput: "8y2",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Base92",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Base92: Spec encoding example 2",
|
||||
input: "Hello!!",
|
||||
expectedOutput: ";K_$aOTo&",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Base92",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Base92: Spec encoding example 3",
|
||||
input: "base-92",
|
||||
expectedOutput: "DX2?V<Y(*",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Base92",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "From Base92: nothing",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Base92",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "From Base92: Spec decoding example 1",
|
||||
input: "G'_DW[B",
|
||||
expectedOutput: "ietf!",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Base92",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "From Base92: Invalid character",
|
||||
input: "~",
|
||||
expectedOutput: "~ is not a base92 character",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Base92",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
21
tests/operations/tests/FileTree.mjs
Normal file
21
tests/operations/tests/FileTree.mjs
Normal file
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* @author sw5678
|
||||
* @copyright Crown Copyright 2023
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
"name": "Swap Case: basic example",
|
||||
"input": "/test_dir1/test_file1.txt\n/test_dir1/test_file2.txt\n/test_dir2/test_file1.txt",
|
||||
"expectedOutput": "test_dir1\n|---test_file1.txt\n|---test_file2.txt\ntest_dir2\n|---test_file1.txt",
|
||||
"recipeConfig": [
|
||||
{
|
||||
"op": "File Tree",
|
||||
"args": [
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
]);
|
22
tests/operations/tests/LZNT1Decompress.mjs
Normal file
22
tests/operations/tests/LZNT1Decompress.mjs
Normal file
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* LZNT1 Decompress tests.
|
||||
*
|
||||
* @author 0xThiebaut [thiebaut.dev]
|
||||
* @copyright Crown Copyright 2023
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "LZNT1 Decompress",
|
||||
input: "\x1a\xb0\x00compress\x00edtestda\x04ta\x07\x88alot",
|
||||
expectedOutput: "compressedtestdatacompressedalot",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "LZNT1 Decompress",
|
||||
args: []
|
||||
}
|
||||
],
|
||||
}
|
||||
]);
|
77
tests/operations/tests/MurmurHash3.mjs
Normal file
77
tests/operations/tests/MurmurHash3.mjs
Normal file
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* MurmurHash3 tests
|
||||
* @author AliceGrey [alice@grey.systems]
|
||||
* @copyright Crown Copyright 2024
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "To MurmurHash3: nothing",
|
||||
input: "",
|
||||
expectedOutput: "0",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "MurmurHash3",
|
||||
args: [0],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To MurmurHash3: 1",
|
||||
input: "1",
|
||||
expectedOutput: "2484513939",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "MurmurHash3",
|
||||
args: [0],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To MurmurHash3: Hello World!",
|
||||
input: "Hello World!",
|
||||
expectedOutput: "3691591037",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "MurmurHash3",
|
||||
args: [0],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To MurmurHash3: Hello World! with seed",
|
||||
input: "Hello World!",
|
||||
expectedOutput: "1148600031",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "MurmurHash3",
|
||||
args: [1337],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To MurmurHash3: foo",
|
||||
input: "foo",
|
||||
expectedOutput: "4138058784",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "MurmurHash3",
|
||||
args: [0],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To MurmurHash3: foo signed",
|
||||
input: "foo",
|
||||
expectedOutput: "-156908512",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "MurmurHash3",
|
||||
args: [0, true],
|
||||
},
|
||||
],
|
||||
}
|
||||
]);
|
66
tests/operations/tests/RisonEncodeDecode.mjs
Normal file
66
tests/operations/tests/RisonEncodeDecode.mjs
Normal file
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* @author sg5506844 [sg5506844@gmail.com]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Rison Encode: Encoding example 1",
|
||||
input: JSON.stringify({ any: "json", yes: true }),
|
||||
expectedOutput: "(any:json,yes:!t)",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Rison Encode",
|
||||
args: ["Encode"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Rison Encode: Encoding example 2",
|
||||
input: JSON.stringify({ supportsObjects: true, ints: 435 }),
|
||||
expectedOutput: "ints:435,supportsObjects:!t",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Rison Encode",
|
||||
args: ["Encode Object"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Rison Encode: Encoding example 3",
|
||||
input: JSON.stringify(["A", "B", { supportsObjects: true }]),
|
||||
expectedOutput: "A,B,(supportsObjects:!t)",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Rison Encode",
|
||||
args: ["Encode Array"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Rison Encode: Object for an array",
|
||||
input: JSON.stringify({ supportsObjects: true, ints: 435 }),
|
||||
expectedOutput: "Rison Encode - rison.encode_array expects an array argument",
|
||||
expectedError: "Rison Encode - rison.encode_array expects an array argument",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Rison Encode",
|
||||
args: ["Encode Array"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Rison Decode: Decoding example 1",
|
||||
input: "(any:json,yes:!t)",
|
||||
expectedOutput: JSON.stringify({ any: "json", yes: true }, null, 4),
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Rison Decode",
|
||||
args: ["Decode"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
Loading…
Reference in a new issue