mirror of
https://github.com/gchq/CyberChef
synced 2025-12-28 14:13:51 +00:00
Compare commits
102 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7cc6c7363 | ||
|
|
a5bc1c6f9e | ||
|
|
c2212f9ab3 | ||
|
|
3fb5bf14a6 | ||
|
|
f32b7d5de5 | ||
|
|
7a58567659 | ||
|
|
4be0a436f2 | ||
|
|
1e71fc91a1 | ||
|
|
4430ea55c4 | ||
|
|
4b6cebc068 | ||
|
|
1197859865 | ||
|
|
293a95e938 | ||
|
|
9a3464a5ec | ||
|
|
55dddd3ef9 | ||
|
|
23956480b7 | ||
|
|
6dbaf6a36c | ||
|
|
1d8c7dcb97 | ||
|
|
41c8a5aff0 | ||
|
|
597fba2fd0 | ||
|
|
f7be8d720b | ||
|
|
0a5038e533 | ||
|
|
5feba30956 | ||
|
|
23a228bbd9 | ||
|
|
598813ff88 | ||
|
|
118f0130d8 | ||
|
|
e77d3a4b7d | ||
|
|
bf0bd620f1 | ||
|
|
62edd76d7e | ||
|
|
cef6585cab | ||
|
|
481241b88d | ||
|
|
93c0c7cc10 | ||
|
|
01d9536bbd | ||
|
|
24a7c75926 | ||
|
|
c2eea7a9f7 | ||
|
|
e2812cadb5 | ||
|
|
cde958af16 | ||
|
|
bf70589b3c | ||
|
|
78d1114869 | ||
|
|
ab83caa77b | ||
|
|
42dd03bb84 | ||
|
|
cb09949fb9 | ||
|
|
0c6eac3b21 | ||
|
|
5e771c521c | ||
|
|
b8afbf7458 | ||
|
|
be59efbd6b | ||
|
|
99ccd06f23 | ||
|
|
f4d75f88a9 | ||
|
|
9112bd4936 | ||
|
|
3e513efd59 | ||
|
|
4100a22c7f | ||
|
|
71078d9332 | ||
|
|
72ba579e1e | ||
|
|
5fd2512a9b | ||
|
|
3a1a6a94d2 | ||
|
|
928178716a | ||
|
|
08419a20c0 | ||
|
|
60506ee2d1 | ||
|
|
6e411c9dd9 | ||
|
|
86db43e6dd | ||
|
|
252ac0bdaa | ||
|
|
4d8b1721bc | ||
|
|
fd390bc61b | ||
|
|
813a151524 | ||
|
|
c06502cd76 | ||
|
|
974ce1fd12 | ||
|
|
d2dc50fe8e | ||
|
|
ec50105e34 | ||
|
|
86ebed132d | ||
|
|
47ccafcbb2 | ||
|
|
798f013219 | ||
|
|
61e6423d95 | ||
|
|
44c2b71e6c | ||
|
|
b806be3f49 | ||
|
|
2750284eea | ||
|
|
5366f1a2eb | ||
|
|
b459c15d74 | ||
|
|
8a02b35d7d | ||
|
|
d4441823aa | ||
|
|
a6fa0628f2 | ||
|
|
8e5aa2c393 | ||
|
|
1118ff598d | ||
|
|
09e93b4639 | ||
|
|
4814922e67 | ||
|
|
81d1007bb7 | ||
|
|
04036e001e | ||
|
|
725b0d42f8 | ||
|
|
071c1bdea6 | ||
|
|
7386c145ef | ||
|
|
25ca8d85a6 | ||
|
|
c60ed2c403 | ||
|
|
7d41d4d030 | ||
|
|
6d77fe6eb3 | ||
|
|
40d3c8b071 | ||
|
|
02ec4a3bfd | ||
|
|
7a4ebbf47e | ||
|
|
2e7ce477d7 | ||
|
|
c1a22ef639 | ||
|
|
0a7b78b7ee | ||
|
|
e1cb62848c | ||
|
|
acf5c733c2 | ||
|
|
7c25e29515 | ||
|
|
7c72871c02 |
@@ -2,6 +2,9 @@
|
||||
All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master).
|
||||
|
||||
|
||||
### [9.12.0] - 2019-12-20
|
||||
- 'Normalise Unicode' operation added [@matthieuxyz] | [#912]
|
||||
|
||||
### [9.11.0] - 2019-11-06
|
||||
- Implemented CFB, OFB, and CTR modes for Blowfish operations [@cbeuw] | [#653]
|
||||
|
||||
@@ -197,6 +200,7 @@ All major and minor version changes will be documented in this file. Details of
|
||||
|
||||
|
||||
|
||||
[9.12.0]: https://github.com/gchq/CyberChef/releases/tag/v9.12.0
|
||||
[9.11.0]: https://github.com/gchq/CyberChef/releases/tag/v9.11.0
|
||||
[9.10.0]: https://github.com/gchq/CyberChef/releases/tag/v9.10.0
|
||||
[9.9.0]: https://github.com/gchq/CyberChef/releases/tag/v9.9.0
|
||||
@@ -281,6 +285,7 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[@jarrodconnolly]: https://github.com/jarrodconnolly
|
||||
[@VirtualColossus]: https://github.com/VirtualColossus
|
||||
[@cbeuw]: https://github.com/cbeuw
|
||||
[@matthieuxyz]: https://github.com/matthieuxyz
|
||||
|
||||
[#95]: https://github.com/gchq/CyberChef/pull/299
|
||||
[#173]: https://github.com/gchq/CyberChef/pull/173
|
||||
@@ -344,3 +349,4 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[#632]: https://github.com/gchq/CyberChef/pull/632
|
||||
[#653]: https://github.com/gchq/CyberChef/pull/653
|
||||
[#865]: https://github.com/gchq/CyberChef/pull/865
|
||||
[#912]: https://github.com/gchq/CyberChef/pull/912
|
||||
|
||||
@@ -219,6 +219,7 @@ module.exports = function (grunt) {
|
||||
options: {
|
||||
webpack: webpackConfig,
|
||||
host: "0.0.0.0",
|
||||
port: grunt.option("port") || 8080,
|
||||
disableHostCheck: true,
|
||||
overlay: true,
|
||||
inline: false,
|
||||
@@ -275,7 +276,7 @@ module.exports = function (grunt) {
|
||||
connect: {
|
||||
prod: {
|
||||
options: {
|
||||
port: 8000,
|
||||
port: grunt.option("port") || 8000,
|
||||
base: "build/prod/"
|
||||
}
|
||||
}
|
||||
|
||||
3492
package-lock.json
generated
3492
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
39
package.json
39
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "9.11.8",
|
||||
"version": "9.12.1",
|
||||
"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,20 +36,20 @@
|
||||
"node >= 10"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.7.2",
|
||||
"@babel/plugin-transform-runtime": "^7.6.2",
|
||||
"@babel/preset-env": "^7.7.1",
|
||||
"autoprefixer": "^9.7.2",
|
||||
"@babel/core": "^7.7.5",
|
||||
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||
"@babel/preset-env": "^7.7.6",
|
||||
"autoprefixer": "^9.7.3",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.0",
|
||||
"chromedriver": "^78.0.1",
|
||||
"chromedriver": "^80.0.1",
|
||||
"colors": "^1.4.0",
|
||||
"copy-webpack-plugin": "^5.0.5",
|
||||
"css-loader": "^3.2.0",
|
||||
"eslint": "^6.6.0",
|
||||
"css-loader": "^3.2.1",
|
||||
"eslint": "^6.7.2",
|
||||
"exports-loader": "^0.7.0",
|
||||
"file-loader": "^4.2.0",
|
||||
"file-loader": "^5.0.2",
|
||||
"grunt": "^1.0.4",
|
||||
"grunt-accessibility": "~6.0.0",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
@@ -65,17 +65,17 @@
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"imports-loader": "^0.8.0",
|
||||
"mini-css-extract-plugin": "^0.8.0",
|
||||
"nightwatch": "^1.2.4",
|
||||
"nightwatch": "^1.3.4",
|
||||
"node-sass": "^4.13.0",
|
||||
"postcss-css-variables": "^0.13.0",
|
||||
"postcss-css-variables": "^0.14.0",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"prompt": "^1.0.0",
|
||||
"sass-loader": "^8.0.0",
|
||||
"sitemap": "^5.1.0",
|
||||
"style-loader": "^1.0.0",
|
||||
"svg-url-loader": "^3.0.2",
|
||||
"url-loader": "^2.2.0",
|
||||
"style-loader": "^1.0.1",
|
||||
"svg-url-loader": "^3.0.3",
|
||||
"url-loader": "^3.0.0",
|
||||
"webpack": "^4.41.2",
|
||||
"webpack-bundle-analyzer": "^3.6.0",
|
||||
"webpack-dev-server": "^3.9.0",
|
||||
@@ -84,20 +84,20 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.7.0",
|
||||
"@babel/runtime": "^7.7.2",
|
||||
"@babel/runtime": "^7.7.6",
|
||||
"arrive": "^2.4.1",
|
||||
"avsc": "^5.4.16",
|
||||
"babel-plugin-transform-builtin-extend": "1.1.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"blakejs": "^1.1.0",
|
||||
"bootstrap": "4.3.1",
|
||||
"bootstrap-colorpicker": "^3.1.2",
|
||||
"bootstrap": "4.4.1",
|
||||
"bootstrap-colorpicker": "^3.2.0",
|
||||
"bootstrap-material-design": "^4.1.2",
|
||||
"bson": "^4.0.2",
|
||||
"chi-squared": "^1.1.0",
|
||||
"codepage": "^1.14.0",
|
||||
"core-js": "^3.4.1",
|
||||
"core-js": "^3.4.8",
|
||||
"crypto-api": "^0.8.5",
|
||||
"crypto-js": "^3.1.9-1",
|
||||
"ctph.js": "0.0.5",
|
||||
@@ -113,7 +113,7 @@
|
||||
"file-saver": "^2.0.2",
|
||||
"geodesy": "^1.1.3",
|
||||
"highlight.js": "^9.16.2",
|
||||
"jimp": "^0.8.5",
|
||||
"jimp": "^0.9.3",
|
||||
"jquery": "3.4.1",
|
||||
"js-crc": "^0.2.0",
|
||||
"js-sha3": "^0.8.0",
|
||||
@@ -147,6 +147,7 @@
|
||||
"ssdeep.js": "0.0.2",
|
||||
"tesseract.js": "^2.0.0-alpha.15",
|
||||
"ua-parser-js": "^0.7.20",
|
||||
"unorm": "^1.6.0",
|
||||
"utf8": "^3.0.0",
|
||||
"vkbeautify": "^0.99.3",
|
||||
"xmldom": "^0.1.27",
|
||||
|
||||
@@ -73,10 +73,10 @@ class Chef {
|
||||
// The threshold is specified in KiB.
|
||||
const threshold = (options.ioDisplayThreshold || 1024) * 1024;
|
||||
const returnType =
|
||||
this.dish.size > threshold ?
|
||||
Dish.ARRAY_BUFFER :
|
||||
this.dish.type === Dish.HTML ?
|
||||
Dish.HTML :
|
||||
this.dish.type === Dish.HTML ?
|
||||
Dish.HTML :
|
||||
this.dish.size > threshold ?
|
||||
Dish.ARRAY_BUFFER :
|
||||
Dish.STRING;
|
||||
|
||||
return {
|
||||
|
||||
@@ -113,6 +113,7 @@ class Ingredient {
|
||||
return data;
|
||||
}
|
||||
case "number":
|
||||
if (data === null) return data;
|
||||
number = parseFloat(data);
|
||||
if (isNaN(number)) {
|
||||
const sample = Utils.truncate(data.toString(), 10);
|
||||
|
||||
@@ -591,6 +591,44 @@ class Utils {
|
||||
return utf8 ? Utils.byteArrayToUtf8(arr) : Utils.byteArrayToChars(arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the Shannon entropy for a given set of data.
|
||||
*
|
||||
* @param {Uint8Array|ArrayBuffer} input
|
||||
* @returns {number}
|
||||
*/
|
||||
static calculateShannonEntropy(data) {
|
||||
if (data instanceof ArrayBuffer) {
|
||||
data = new Uint8Array(data);
|
||||
}
|
||||
const prob = [],
|
||||
occurrences = new Array(256).fill(0);
|
||||
|
||||
// Count occurrences of each byte in the input
|
||||
let i;
|
||||
for (i = 0; i < data.length; i++) {
|
||||
occurrences[data[i]]++;
|
||||
}
|
||||
|
||||
// Store probability list
|
||||
for (i = 0; i < occurrences.length; i++) {
|
||||
if (occurrences[i] > 0) {
|
||||
prob.push(occurrences[i] / data.length);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate Shannon entropy
|
||||
let entropy = 0,
|
||||
p;
|
||||
|
||||
for (i = 0; i < prob.length; i++) {
|
||||
p = prob[i];
|
||||
entropy += p * Math.log(p) / Math.log(2);
|
||||
}
|
||||
|
||||
return -entropy;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses CSV data and returns it as a two dimensional array or strings.
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"URL Decode",
|
||||
"Escape Unicode Characters",
|
||||
"Unescape Unicode Characters",
|
||||
"Normalise Unicode",
|
||||
"To Quoted Printable",
|
||||
"From Quoted Printable",
|
||||
"To Punycode",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import DishType from "./DishType.mjs";
|
||||
import { isNodeEnvironment } from "../Utils.mjs";
|
||||
import Utils, { isNodeEnvironment } from "../Utils.mjs";
|
||||
|
||||
|
||||
/**
|
||||
@@ -16,13 +16,14 @@ class DishListFile extends DishType {
|
||||
/**
|
||||
* convert the given value to a ArrayBuffer
|
||||
*/
|
||||
static toArrayBuffer() {
|
||||
static async toArrayBuffer() {
|
||||
DishListFile.checkForValue(this.value);
|
||||
|
||||
if (isNodeEnvironment()) {
|
||||
this.value = this.value.map(file => Uint8Array.from(file.data));
|
||||
} else {
|
||||
this.value = await DishListFile.concatenateTypedArraysWithTypedElements(...this.value);
|
||||
}
|
||||
this.value = DishListFile.concatenateTypedArrays(...this.value).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,27 @@ class DishListFile extends DishType {
|
||||
this.value = [new File(this.value, "unknown")];
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates a list of typed elements together.
|
||||
*
|
||||
* @param {Uint8Array[]} arrays
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
static async concatenateTypedArraysWithTypedElements(...arrays) {
|
||||
let totalLength = 0;
|
||||
for (const arr of arrays) {
|
||||
totalLength += arr.size;
|
||||
}
|
||||
const myArray = new Uint8Array(totalLength);
|
||||
|
||||
let offset = 0;
|
||||
for (const arr of arrays) {
|
||||
const data = await Utils.readFile(arr);
|
||||
myArray.set(data, offset);
|
||||
offset += data.length;
|
||||
}
|
||||
return myArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates a list of Uint8Arrays together
|
||||
|
||||
@@ -164,3 +164,15 @@ export const IO_FORMAT = {
|
||||
"Simplified Chinese GB18030 (54936)": 54936,
|
||||
};
|
||||
|
||||
/**
|
||||
* Unicode Normalisation Forms
|
||||
*
|
||||
* @author Matthieu [m@tthieu.xyz]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Character encoding format mappings.
|
||||
*/
|
||||
export const UNICODE_NORMALISATION_FORMS = ["NFD", "NFC", "NFKD", "NFKC"];
|
||||
|
||||
@@ -280,7 +280,7 @@ export const FILE_SIGNATURES = {
|
||||
9: 0x0,
|
||||
10: [0x0, 0x1]
|
||||
},
|
||||
extractor: null
|
||||
extractor: extractICO
|
||||
},
|
||||
{
|
||||
name: "Radiance High Dynamic Range image",
|
||||
@@ -1008,8 +1008,7 @@ export const FILE_SIGNATURES = {
|
||||
0: 0x7b,
|
||||
1: 0x5c,
|
||||
2: 0x72,
|
||||
3: 0x74,
|
||||
4: 0x66
|
||||
3: 0x74
|
||||
},
|
||||
extractor: extractRTF
|
||||
},
|
||||
@@ -1282,17 +1281,30 @@ export const FILE_SIGNATURES = {
|
||||
extension: "dylib",
|
||||
mime: "application/octet-stream",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0xca,
|
||||
1: 0xfe,
|
||||
2: 0xba,
|
||||
3: 0xbe,
|
||||
4: 0x00,
|
||||
5: 0x00,
|
||||
6: 0x00,
|
||||
7: [0x01, 0x02, 0x03]
|
||||
},
|
||||
extractor: null
|
||||
signature: [
|
||||
{
|
||||
0: 0xca,
|
||||
1: 0xfe,
|
||||
2: 0xba,
|
||||
3: 0xbe,
|
||||
4: 0x00,
|
||||
5: 0x00,
|
||||
6: 0x00,
|
||||
7: [0x01, 0x02, 0x03]
|
||||
},
|
||||
{
|
||||
0: 0xce,
|
||||
1: 0xfa,
|
||||
2: 0xed,
|
||||
3: 0xfe,
|
||||
4: 0x07,
|
||||
5: 0x00,
|
||||
6: 0x00,
|
||||
7: 0x00,
|
||||
8: [0x01, 0x02, 0x03]
|
||||
}
|
||||
],
|
||||
extractor: extractMACHO
|
||||
},
|
||||
{
|
||||
name: "MacOS Mach-O 64-bit object",
|
||||
@@ -1305,7 +1317,7 @@ export const FILE_SIGNATURES = {
|
||||
2: 0xed,
|
||||
3: 0xfe
|
||||
},
|
||||
extractor: null
|
||||
extractor: extractMACHO
|
||||
},
|
||||
{
|
||||
name: "Adobe Flash",
|
||||
@@ -1404,7 +1416,7 @@ export const FILE_SIGNATURES = {
|
||||
260: 0x61,
|
||||
261: 0x72
|
||||
},
|
||||
extractor: null
|
||||
extractor: extractTAR
|
||||
},
|
||||
{
|
||||
name: "Roshal Archive",
|
||||
@@ -2720,6 +2732,154 @@ export function extractZIP(bytes, offset) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MACHO extractor
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {number} offset
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function extractMACHO(bytes, offset) {
|
||||
|
||||
// Magic bytes.
|
||||
const MHCIGAM64 = "207250237254";
|
||||
const MHMAGIC64 = "254237250207";
|
||||
const MHCIGAM = "206250237254";
|
||||
|
||||
|
||||
/**
|
||||
* Checks to see if the file is 64-bit.
|
||||
*
|
||||
* @param {string} magic
|
||||
* @returns {bool}
|
||||
*/
|
||||
function isMagic64(magic) {
|
||||
return magic === MHCIGAM64 || magic === MHMAGIC64;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the endianness of the file.
|
||||
*
|
||||
* @param {string} magic
|
||||
* @returns {bool}
|
||||
*/
|
||||
function shouldSwapBytes(magic) {
|
||||
return magic === MHCIGAM || magic === MHCIGAM64;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Jumps through segment information and calculates the sum of the segement sizes.
|
||||
*
|
||||
* @param {Stream} stream
|
||||
* @param {number} offset
|
||||
* @param {string} isSwap
|
||||
* @param {number} ncmds
|
||||
* @returns {number}
|
||||
*/
|
||||
function dumpSegmentCommands(stream, offset, isSwap, ncmds) {
|
||||
let total = 0;
|
||||
const LCSEGEMENT64 = 0x19;
|
||||
const LCSEGEMENT = 0x1;
|
||||
|
||||
for (let i = 0; i < ncmds; i++) {
|
||||
|
||||
// Move to start of segment.
|
||||
stream.moveTo(offset);
|
||||
const cmd = stream.readInt(4, isSwap);
|
||||
if (cmd === LCSEGEMENT64) {
|
||||
|
||||
// Move to size of segment field.
|
||||
stream.moveTo(offset + 48);
|
||||
|
||||
// Extract size of segement.
|
||||
total += stream.readInt(8, isSwap);
|
||||
stream.moveTo(offset + 4);
|
||||
|
||||
// Move to offset of next segment.
|
||||
offset += stream.readInt(4, isSwap);
|
||||
} else if (cmd === LCSEGEMENT) {
|
||||
stream.moveTo(offset + 36);
|
||||
|
||||
// Extract size of segement.
|
||||
total += stream.readInt(4, isSwap);
|
||||
stream.moveTo(offset + 4);
|
||||
offset += stream.readInt(4, isSwap);
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the number of command segments.
|
||||
*
|
||||
* @param {Stream} stream
|
||||
* @param {bool} is64
|
||||
* @param {string} isSwap
|
||||
* @returns {number}
|
||||
*/
|
||||
function dumpMachHeader(stream, is64, isSwap) {
|
||||
let loadCommandsOffset = 28;
|
||||
if (is64)
|
||||
loadCommandsOffset += 4;
|
||||
|
||||
// Move to number of commands field.
|
||||
stream.moveTo(16);
|
||||
const ncmds = stream.readInt(4, isSwap);
|
||||
return dumpSegmentCommands(stream, loadCommandsOffset, isSwap, ncmds);
|
||||
}
|
||||
|
||||
|
||||
const stream = new Stream(bytes.slice(offset));
|
||||
const magic = stream.getBytes(4).join("");
|
||||
|
||||
// Move to the end of the final segment.
|
||||
stream.moveTo(dumpMachHeader(stream, isMagic64(magic), shouldSwapBytes(magic) ? "le" : "be"));
|
||||
return stream.carve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TAR extractor.
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {number} offset
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function extractTAR(bytes, offset) {
|
||||
const stream = new Stream(bytes.slice(offset));
|
||||
while (stream.hasMore()) {
|
||||
|
||||
// Move to ustar identifier.
|
||||
stream.moveForwardsBy(0x101);
|
||||
if (stream.getBytes(5).join("") !== [0x75, 0x73, 0x74, 0x61, 0x72].join("")) {
|
||||
// Reverse back to the end of the last section.
|
||||
stream.moveBackwardsBy(0x106);
|
||||
break;
|
||||
}
|
||||
|
||||
// Move back to file size field.
|
||||
stream.moveBackwardsBy(0x8a);
|
||||
let fsize = 0;
|
||||
|
||||
// Read file size field.
|
||||
stream.getBytes(11).forEach((element, index) => {
|
||||
fsize += (element - 48).toString();
|
||||
});
|
||||
|
||||
// Round number up from octet to nearest 512.
|
||||
fsize = (Math.ceil(parseInt(fsize, 8) / 512) * 512);
|
||||
|
||||
// Move forwards to the end of that file.
|
||||
stream.moveForwardsBy(fsize + 0x179);
|
||||
}
|
||||
stream.consumeWhile(0x00);
|
||||
return stream.carve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* PNG extractor.
|
||||
*
|
||||
@@ -2772,6 +2932,32 @@ export function extractBMP(bytes, offset) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ICO extractor.
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {number} offset
|
||||
*/
|
||||
export function extractICO(bytes, offset) {
|
||||
const stream = new Stream(bytes.slice(offset));
|
||||
|
||||
// Move to number of files there are.
|
||||
stream.moveTo(4);
|
||||
|
||||
// Read the number of files stored in the ICO
|
||||
const numberFiles = stream.readInt(2, "le");
|
||||
|
||||
// Move forward to the last file header.
|
||||
stream.moveForwardsBy(8 + ((numberFiles-1) * 16));
|
||||
const fileSize = stream.readInt(4, "le");
|
||||
const fileOffset = stream.readInt(4, "le");
|
||||
|
||||
// Move to the end of the last file.
|
||||
stream.moveTo(fileOffset + fileSize);
|
||||
return stream.carve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* WAV extractor.
|
||||
*
|
||||
@@ -2786,7 +2972,7 @@ export function extractWAV(bytes, offset) {
|
||||
stream.moveTo(4);
|
||||
|
||||
// Move to file size.
|
||||
stream.moveTo(stream.readInt(4, "le") - 4);
|
||||
stream.moveTo(stream.readInt(4, "le"));
|
||||
|
||||
return stream.carve();
|
||||
}
|
||||
@@ -2914,15 +3100,127 @@ export function extractSQLITE(bytes, offset) {
|
||||
export function extractPListXML(bytes, offset) {
|
||||
const stream = new Stream(bytes.slice(offset));
|
||||
|
||||
// Find closing tag (</plist>)
|
||||
stream.continueUntil([0x3c, 0x2f, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x3e]);
|
||||
stream.moveForwardsBy(8);
|
||||
let braceCount = 0;
|
||||
|
||||
// Continue to the first (<plist).
|
||||
stream.continueUntil([0x3c, 0x70, 0x6c, 0x69, 0x73, 0x74]);
|
||||
stream.moveForwardsBy(6);
|
||||
braceCount++;
|
||||
|
||||
// While we have an unequal amount of braces.
|
||||
while (braceCount > 0 && stream.hasMore()) {
|
||||
if (stream.readInt(1) === 0x3c) {
|
||||
|
||||
// If we hit an <plist.
|
||||
if (stream.getBytes(5).join("") === [0x70, 0x6c, 0x69, 0x73, 0x74].join("")) {
|
||||
braceCount++;
|
||||
} else {
|
||||
stream.moveBackwardsBy(5);
|
||||
}
|
||||
|
||||
// If we hit an </plist>.
|
||||
if (stream.getBytes(7).join("") === [0x2f, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x3e].join("")) {
|
||||
braceCount--;
|
||||
} else {
|
||||
stream.moveBackwardsBy(7);
|
||||
}
|
||||
}
|
||||
}
|
||||
stream.consumeIf(0x0a);
|
||||
|
||||
return stream.carve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* OLE2 extractor.
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {number} offset
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function extractOLE2(bytes, offset) {
|
||||
const stream = new Stream(bytes.slice(offset));
|
||||
const entries = [
|
||||
[[0x52, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x20, 0x00, 0x45, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x72, 0x00, 0x79], 19, "Root Entry"],
|
||||
[[0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6b], 15, "Workbook"],
|
||||
[[0x43, 0x00, 0x75, 0x00, 0x72, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72], 23, "Current User"],
|
||||
[[0x50, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x65, 0x00, 0x72, 0x00, 0x50, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74], 37, "PowerPoint Document"],
|
||||
[[0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74], 23, "WordDocument"],
|
||||
[[0x44, 0x00, 0x61, 0x00, 0x74, 0x00, 0x61], 7, "Data"],
|
||||
[[0x50, 0x00, 0x69, 0x00, 0x63, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73], 15, "Pictures"],
|
||||
[[0x31, 0x00, 0x54, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65], 11, "1Table"],
|
||||
[[0x05, 0x00, 0x53, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e], 37, "SummaryInformation"],
|
||||
[[0x05, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x53, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e], 53, "DocumentSummaryInformation"],
|
||||
[[0x43, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x4f, 0x00, 0x62, 0x00, 0x6a], 13, "Comp Obj"],
|
||||
[[0x01, 0x00], 2, "Entry"]
|
||||
];
|
||||
let endianness = "le";
|
||||
|
||||
// Move to endianess field.
|
||||
stream.moveForwardsBy(28);
|
||||
if (stream.readInt(2, endianness) === 0xfffe)
|
||||
endianness = "be";
|
||||
|
||||
// Calculate the size of the normal sectors.
|
||||
const sizeOfSector = 2 ** stream.readInt(2, endianness);
|
||||
|
||||
// Move to root directory offset field.
|
||||
stream.moveTo(48);
|
||||
|
||||
// Read root directory offset.
|
||||
const rootStuff = stream.readInt(4, endianness);
|
||||
|
||||
// Calculate root directory offset.
|
||||
let total = 512 + (rootStuff * sizeOfSector);
|
||||
stream.moveTo(total);
|
||||
|
||||
// While valid directory entries.
|
||||
let found = true;
|
||||
while (found) {
|
||||
found = false;
|
||||
|
||||
// Attempt to determine what directory entry it is.
|
||||
for (const element of entries) {
|
||||
|
||||
// If the byte pattern matches.
|
||||
if (stream.getBytes(element[1]).join("") === element[0].join("")) {
|
||||
stream.moveBackwardsBy(element[1]);
|
||||
found = true;
|
||||
|
||||
// Move forwards by the size of the comp obj.
|
||||
if (element[2] === "Comp Obj") {
|
||||
|
||||
// The size of the Comp Obj entry - 128. Since we add 128 later.
|
||||
total += 128 * 6;
|
||||
stream.moveTo(total);
|
||||
} else if (element[2] === "Entry") {
|
||||
|
||||
// If there is an entry move backwards by 126 to then move forwards by 128. Hence a total displacement of 2.
|
||||
stream.moveBackwardsBy(126);
|
||||
}
|
||||
break;
|
||||
}
|
||||
stream.moveBackwardsBy(element[1]);
|
||||
}
|
||||
|
||||
// If we have found a valid entry, move forwards by 128.
|
||||
if (found) {
|
||||
|
||||
// Every entry is at least 128 in size, some are bigger which is dealt with by the above if statement.
|
||||
total += 128;
|
||||
stream.moveForwardsBy(128);
|
||||
}
|
||||
}
|
||||
|
||||
// Round up to a multiple of 512.
|
||||
total = Math.ceil(total / 512) * 512;
|
||||
|
||||
stream.moveTo(total);
|
||||
return stream.carve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* GZIP extractor.
|
||||
*
|
||||
|
||||
@@ -23,25 +23,39 @@ import Utils from "../Utils.mjs";
|
||||
*
|
||||
* // returns "0a:14:1e"
|
||||
* toHex([10,20,30], ":");
|
||||
*
|
||||
* // returns "0x0a,0x14,0x1e"
|
||||
* toHex([10,20,30], "0x", 2, ",")
|
||||
*/
|
||||
export function toHex(data, delim=" ", padding=2) {
|
||||
export function toHex(data, delim=" ", padding=2, extraDelim="", lineSize=0) {
|
||||
if (!data) return "";
|
||||
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
|
||||
|
||||
let output = "";
|
||||
const prepend = (delim === "0x" || delim === "\\x");
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
output += data[i].toString(16).padStart(padding, "0") + delim;
|
||||
const hex = data[i].toString(16).padStart(padding, "0");
|
||||
output += prepend ? delim + hex : hex + delim;
|
||||
|
||||
if (extraDelim) {
|
||||
output += extraDelim;
|
||||
}
|
||||
// Add LF after each lineSize amount of bytes but not at the end
|
||||
if ((i !== data.length - 1) && ((i + 1) % lineSize === 0)) {
|
||||
output += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Add \x or 0x to beginning
|
||||
if (delim === "0x") output = "0x" + output;
|
||||
if (delim === "\\x") output = "\\x" + output;
|
||||
|
||||
if (delim.length)
|
||||
return output.slice(0, -delim.length);
|
||||
else
|
||||
// Remove the extraDelim at the end (if there is one)
|
||||
// and remove the delim at the end, but if it's prepended there's nothing to remove
|
||||
const rTruncLen = extraDelim.length + (prepend ? 0 : delim.length);
|
||||
if (rTruncLen) {
|
||||
// If rTruncLen === 0 then output.slice(0,0) will be returned, which is nothing
|
||||
return output.slice(0, -rTruncLen);
|
||||
} else {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +101,7 @@ export function toHexFast(data) {
|
||||
*/
|
||||
export function fromHex(data, delim="Auto", byteLen=2) {
|
||||
if (delim !== "None") {
|
||||
const delimRegex = delim === "Auto" ? /[^a-f\d]/gi : Utils.regexRep(delim);
|
||||
const delimRegex = delim === "Auto" ? /[^a-f\d]|(0x)/gi : Utils.regexRep(delim);
|
||||
data = data.replace(delimRegex, "");
|
||||
}
|
||||
|
||||
@@ -102,7 +116,7 @@ export function fromHex(data, delim="Auto", byteLen=2) {
|
||||
/**
|
||||
* To Hexadecimal delimiters.
|
||||
*/
|
||||
export const TO_HEX_DELIM_OPTIONS = ["Space", "Percent", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"];
|
||||
export const TO_HEX_DELIM_OPTIONS = ["Space", "Percent", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "0x with comma", "\\x", "None"];
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -403,7 +403,7 @@ class Magic {
|
||||
await recipe.execute(dish);
|
||||
// Return an empty buffer if the recipe did not run to completion
|
||||
if (recipe.lastRunOp === recipe.opList[recipe.opList.length - 1]) {
|
||||
return dish.get(Dish.ARRAY_BUFFER);
|
||||
return await dish.get(Dish.ARRAY_BUFFER);
|
||||
} else {
|
||||
return new ArrayBuffer();
|
||||
}
|
||||
|
||||
@@ -63,9 +63,9 @@ class DNSOverHTTPS extends Operation {
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Validate DNSSEC",
|
||||
name: "Disable DNSSEC validation",
|
||||
type: "boolean",
|
||||
value: true
|
||||
value: false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -147,7 +147,8 @@ const MORSE_TABLE = {
|
||||
"=": "<dash><dot><dot><dot><dash>",
|
||||
"&": "<dot><dash><dot><dot><dot>",
|
||||
"_": "<dot><dot><dash><dash><dot><dash>",
|
||||
"$": "<dot><dot><dot><dash><dot><dot><dash>"
|
||||
"$": "<dot><dot><dot><dash><dot><dot><dash>",
|
||||
" ": "<dot><dot><dot><dot><dot><dot><dot>"
|
||||
};
|
||||
|
||||
export default FromMorseCode;
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min.js";
|
||||
import gunzip from "zlibjs/bin/gunzip.min.js";
|
||||
|
||||
const Zlib = zlibAndGzip.Zlib;
|
||||
const Zlib = gunzip.Zlib;
|
||||
|
||||
/**
|
||||
* Gunzip operation
|
||||
@@ -42,8 +42,8 @@ class Gunzip extends Operation {
|
||||
* @returns {File}
|
||||
*/
|
||||
run(input, args) {
|
||||
const gunzip = new Zlib.Gunzip(new Uint8Array(input));
|
||||
return new Uint8Array(gunzip.decompress()).buffer;
|
||||
const gzipObj = new Zlib.Gunzip(new Uint8Array(input));
|
||||
return new Uint8Array(gzipObj.decompress()).buffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib.mjs";
|
||||
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min.js";
|
||||
import gzip from "zlibjs/bin/gzip.min.js";
|
||||
|
||||
const Zlib = zlibAndGzip.Zlib;
|
||||
const Zlib = gzip.Zlib;
|
||||
|
||||
/**
|
||||
* Gzip operation
|
||||
@@ -73,12 +73,15 @@ class Gzip extends Operation {
|
||||
options.filename = filename;
|
||||
}
|
||||
if (comment.length) {
|
||||
options.flags.fcommenct = true;
|
||||
options.flags.comment = true;
|
||||
options.comment = comment;
|
||||
}
|
||||
|
||||
const gzip = new Zlib.Gzip(new Uint8Array(input), options);
|
||||
return new Uint8Array(gzip.compress()).buffer;
|
||||
const gzipObj = new Zlib.Gzip(new Uint8Array(input), options);
|
||||
const compressed = new Uint8Array(gzipObj.compress());
|
||||
if (options.flags.comment && !(compressed[3] & 0x10)) {
|
||||
compressed[3] |= 0x10;
|
||||
}
|
||||
return compressed.buffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
62
src/core/operations/NormaliseUnicode.mjs
Normal file
62
src/core/operations/NormaliseUnicode.mjs
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @author Matthieu [m@tthieu.xyz]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import {UNICODE_NORMALISATION_FORMS} from "../lib/ChrEnc.mjs";
|
||||
import unorm from "unorm";
|
||||
|
||||
/**
|
||||
* Normalise Unicode operation
|
||||
*/
|
||||
class NormaliseUnicode extends Operation {
|
||||
|
||||
/**
|
||||
* NormaliseUnicode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Normalise Unicode";
|
||||
this.module = "Encodings";
|
||||
this.description = "Transform Unicode characters to one of the Normalisation Forms";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Unicode_equivalence#Normal_forms";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Normal Form",
|
||||
type: "option",
|
||||
value: UNICODE_NORMALISATION_FORMS
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [normalForm] = args;
|
||||
|
||||
switch (normalForm) {
|
||||
case "NFD":
|
||||
return unorm.nfd(input);
|
||||
case "NFC":
|
||||
return unorm.nfc(input);
|
||||
case "NFKD":
|
||||
return unorm.nfkd(input);
|
||||
case "NFKC":
|
||||
return unorm.nfc(input);
|
||||
default:
|
||||
throw new OperationError("Unknown Normalisation Form");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default NormaliseUnicode;
|
||||
@@ -1,10 +1,12 @@
|
||||
/**
|
||||
* @author masq [github.cyberchef@masq.cc]
|
||||
* @author n1073645
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* To Case Insensitive Regex operation
|
||||
@@ -32,7 +34,61 @@ class ToCaseInsensitiveRegex extends Operation {
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
return input.replace(/[a-z]/ig, m => `[${m.toLowerCase()}${m.toUpperCase()}]`);
|
||||
|
||||
/**
|
||||
* Simulates look behind behaviour since javascript doesn't support it.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {string}
|
||||
*/
|
||||
function preProcess(input) {
|
||||
let result = "";
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
const temp = input.charAt(i);
|
||||
if (temp.match(/[a-zA-Z]/g) && (input.charAt(i-1) !== "-") && (input.charAt(i+1) !== "-"))
|
||||
result += "[" + temp.toLowerCase() + temp.toUpperCase() + "]";
|
||||
else
|
||||
result += temp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
RegExp(input);
|
||||
} catch (error) {
|
||||
throw new OperationError("Invalid Regular Expression (Please note this version of node does not support look behinds).");
|
||||
}
|
||||
|
||||
// Example: [test] -> [[tT][eE][sS][tT]]
|
||||
return preProcess(input)
|
||||
|
||||
// Example: [A-Z] -> [A-Za-z]
|
||||
.replace(/([A-Z]-[A-Z]|[a-z]-[a-z])/g, m => `${m[0].toUpperCase()}-${m[2].toUpperCase()}${m[0].toLowerCase()}-${m[2].toLowerCase()}`)
|
||||
|
||||
// Example: [H-d] -> [A-DH-dh-z]
|
||||
.replace(/[A-Z]-[a-z]/g, m => `A-${m[2].toUpperCase()}${m}${m[0].toLowerCase()}-z`)
|
||||
|
||||
// Example: [!-D] -> [!-Da-d]
|
||||
.replace(/\\?[ -@]-[A-Z]/g, m => `${m}a-${m[2].toLowerCase()}`)
|
||||
|
||||
// Example: [%-^] -> [%-^a-z]
|
||||
.replace(/\\?[ -@]-\\?[[-`]/g, m => `${m}a-z`)
|
||||
|
||||
// Example: [K-`] -> [K-`k-z]
|
||||
.replace(/[A-Z]-\\?[[-`]/g, m => `${m}${m[0].toLowerCase()}-z`)
|
||||
|
||||
// Example: [[-}] -> [[-}A-Z]
|
||||
.replace(/\\?[[-`]-\\?[{-~]/g, m => `${m}A-Z`)
|
||||
|
||||
// Example: [b-}] -> [b-}B-Z]
|
||||
.replace(/[a-z]-\\?[{-~]/g, m => `${m}${m[0].toUpperCase()}-Z`)
|
||||
|
||||
// Example: [<-j] -> [<-z]
|
||||
.replace(/\\?[ -@]-[a-z]/g, m => `${m[0]}-z`)
|
||||
|
||||
// Example: [^-j] -> [A-J^-j]
|
||||
.replace(/\\?[[-`]-[a-z]/g, m => `A-${m[2].toUpperCase()}${m}`);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,11 @@ class ToHex extends Operation {
|
||||
name: "Delimiter",
|
||||
type: "option",
|
||||
value: TO_HEX_DELIM_OPTIONS
|
||||
},
|
||||
{
|
||||
name: "Bytes per line",
|
||||
type: "number",
|
||||
value: 0
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -40,8 +45,16 @@ class ToHex extends Operation {
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const delim = Utils.charRep(args[0] || "Space");
|
||||
return toHex(new Uint8Array(input), delim, 2);
|
||||
let delim, comma;
|
||||
if (args[0] === "0x with comma") {
|
||||
delim = "0x";
|
||||
comma = ",";
|
||||
} else {
|
||||
delim = Utils.charRep(args[0] || "Space");
|
||||
}
|
||||
const lineSize = args[1];
|
||||
|
||||
return toHex(new Uint8Array(input), delim, 2, comma, lineSize);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,17 +67,31 @@ class ToHex extends Operation {
|
||||
* @returns {Object[]} pos
|
||||
*/
|
||||
highlight(pos, args) {
|
||||
const delim = Utils.charRep(args[0] || "Space"),
|
||||
len = delim === "\r\n" ? 1 : delim.length;
|
||||
|
||||
pos[0].start = pos[0].start * (2 + len);
|
||||
pos[0].end = pos[0].end * (2 + len) - len;
|
||||
|
||||
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
|
||||
if (delim === "0x" || delim === "\\x") {
|
||||
pos[0].start += 2;
|
||||
pos[0].end += 2;
|
||||
let delim, commaLen;
|
||||
if (args[0] === "0x with comma") {
|
||||
delim = "0x";
|
||||
commaLen = 1;
|
||||
} else {
|
||||
delim = Utils.charRep(args[0] || "Space");
|
||||
}
|
||||
|
||||
const lineSize = args[1],
|
||||
len = (delim === "\r\n" ? 1 : delim.length) + commaLen;
|
||||
|
||||
const countLF = function(p) {
|
||||
// Count the number of LFs from 0 upto p
|
||||
return (p / lineSize | 0) - (p >= lineSize && p % lineSize === 0);
|
||||
};
|
||||
|
||||
pos[0].start = pos[0].start * (2 + len) + countLF(pos[0].start);
|
||||
pos[0].end = pos[0].end * (2 + len) + countLF(pos[0].end);
|
||||
|
||||
// if the deliminators are not prepended, trim the trailing deliminator
|
||||
if (!(delim === "0x" || delim === "\\x")) {
|
||||
pos[0].end -= delim.length;
|
||||
}
|
||||
// if there is comma, trim the trailing comma
|
||||
pos[0].end -= commaLen;
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -78,20 +105,26 @@ class ToHex extends Operation {
|
||||
* @returns {Object[]} pos
|
||||
*/
|
||||
highlightReverse(pos, args) {
|
||||
const delim = Utils.charRep(args[0] || "Space"),
|
||||
len = delim === "\r\n" ? 1 : delim.length,
|
||||
width = len + 2;
|
||||
|
||||
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
|
||||
if (delim === "0x" || delim === "\\x") {
|
||||
if (pos[0].start > 1) pos[0].start -= 2;
|
||||
else pos[0].start = 0;
|
||||
if (pos[0].end > 1) pos[0].end -= 2;
|
||||
else pos[0].end = 0;
|
||||
let delim, commaLen;
|
||||
if (args[0] === "0x with comma") {
|
||||
delim = "0x";
|
||||
commaLen = 1;
|
||||
} else {
|
||||
delim = Utils.charRep(args[0] || "Space");
|
||||
}
|
||||
|
||||
pos[0].start = pos[0].start === 0 ? 0 : Math.round(pos[0].start / width);
|
||||
pos[0].end = pos[0].end === 0 ? 0 : Math.ceil(pos[0].end / width);
|
||||
const lineSize = args[1],
|
||||
len = (delim === "\r\n" ? 1 : delim.length) + commaLen,
|
||||
width = len + 2;
|
||||
|
||||
const countLF = function(p) {
|
||||
// Count the number of LFs from 0 up to p
|
||||
const lineLength = width * lineSize;
|
||||
return (p / lineLength | 0) - (p >= lineLength && p % lineLength === 0);
|
||||
};
|
||||
|
||||
pos[0].start = pos[0].start === 0 ? 0 : Math.round((pos[0].start - countLF(pos[0].start)) / width);
|
||||
pos[0].end = pos[0].end === 0 ? 0 : Math.ceil((pos[0].end - countLF(pos[0].end)) / width);
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,8 @@ const MORSE_TABLE = {
|
||||
"=": "<dash><dot><dot><dot><dash>",
|
||||
"&": "<dot><dash><dot><dot><dot>",
|
||||
"_": "<dot><dot><dash><dash><dot><dash>",
|
||||
"$": "<dot><dot><dot><dash><dot><dot><dash>"
|
||||
"$": "<dot><dot><dot><dash><dot><dot><dash>",
|
||||
" ": "<dot><dot><dot><dot><dot><dot><dot>"
|
||||
};
|
||||
|
||||
export default ToMorseCode;
|
||||
|
||||
2
src/core/vendor/gost/gostEngine.mjs
vendored
2
src/core/vendor/gost/gostEngine.mjs
vendored
@@ -383,7 +383,7 @@ if (root.importScripts) {
|
||||
* method with data parameter: algorithm, method and arg.<br>
|
||||
* Call method execute and postMessage() results to onmessage event handler
|
||||
* in the main process.<br>
|
||||
* If error occured onerror event handler executed in main process.
|
||||
* If error occurred onerror event handler executed in main process.
|
||||
*
|
||||
* @memberOf gostEngine
|
||||
* @name onmessage
|
||||
|
||||
@@ -197,6 +197,7 @@ class Manager {
|
||||
this.addMultiEventListener("#output-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter);
|
||||
this.addMultiEventListener("#output-html", "mousedown dblclick select", this.highlighter.outputHtmlMousedown, this.highlighter);
|
||||
this.addDynamicListener("#output-file-download", "click", this.output.downloadFile, this.output);
|
||||
this.addDynamicListener("#output-file-show-all", "click", this.output.showAllFile, this.output);
|
||||
this.addDynamicListener("#output-file-slice i", "click", this.output.displayFileSlice, this.output);
|
||||
document.getElementById("show-file-overlay").addEventListener("click", this.output.showFileOverlayClick.bind(this.output));
|
||||
this.addDynamicListener(".extract-file,.extract-file i", "click", this.output.extractFileClick, this.output);
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<meta name="description" content="The Cyber Swiss Army Knife - a web app for encryption, encoding, compression and data analysis" />
|
||||
<meta name="keywords" content="base64, hex, decode, encode, encrypt, decrypt, compress, decompress, regex, regular expressions, hash, crypt, hexadecimal, user agent, url, certificate, x.509, parser, JSON, gzip, md5, sha1, aes, des, blowfish, xor" />
|
||||
|
||||
<link rel="icon" type="image/ico" href="<%- require('../static/images/favicon.ico') %>" />
|
||||
<link rel="icon" type="image/ico" href="<%- require('../static/images/favicon.ico').default %>" />
|
||||
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
@@ -197,7 +197,7 @@
|
||||
</button>
|
||||
|
||||
<button type="button" class="mx-2 btn btn-lg btn-success btn-raised btn-block" id="bake">
|
||||
<img aria-hidden="true" src="<%- require('../static/images/cook_male-32x32.png') %>" alt="Chef Icon"/>
|
||||
<img aria-hidden="true" src="<%- require('../static/images/cook_male-32x32.png').default %>" alt="Chef Icon"/>
|
||||
<span>Bake!</span>
|
||||
</button>
|
||||
|
||||
@@ -271,7 +271,7 @@
|
||||
<div class="file-overlay" id="file-overlay"></div>
|
||||
<div style="position: relative; height: 100%;">
|
||||
<div class="io-card card">
|
||||
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon" id="input-file-thumbnail"/>
|
||||
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png').default %>" alt="File icon" id="input-file-thumbnail"/>
|
||||
<div class="card-body">
|
||||
<button type="button" class="close" id="input-file-close">×</button>
|
||||
Name: <span id="input-file-name"></span><br>
|
||||
@@ -346,24 +346,26 @@
|
||||
<div id="output-highlighter" class="no-select"></div>
|
||||
<div id="output-html"></div>
|
||||
<textarea id="output-text" readonly="readonly" spellcheck="false"></textarea>
|
||||
<img id="show-file-overlay" aria-hidden="true" src="<%- require('../static/images/file-32x32.png') %>" alt="Show file overlay" title="Show file overlay"/>
|
||||
<img id="show-file-overlay" aria-hidden="true" src="<%- require('../static/images/file-32x32.png').default %>" alt="Show file overlay" title="Show file overlay"/>
|
||||
<div id="output-file">
|
||||
<div class="file-overlay"></div>
|
||||
<div style="position: relative; height: 100%;">
|
||||
<div class="io-card card">
|
||||
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon"/>
|
||||
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png').default %>" alt="File icon"/>
|
||||
<div class="card-body">
|
||||
Size: <span id="output-file-size"></span><br>
|
||||
<button id="output-file-download" type="button" class="btn btn-primary btn-outline">Download</button>
|
||||
<button id="output-file-show-all" type="button" class="btn btn-warning btn-outline" data-toggle="tooltip" title="Warning: This could crash your browser. Use at your own risk.">Show all</button>
|
||||
<div class="input-group">
|
||||
<span class="input-group-btn">
|
||||
<button id="output-file-slice" type="button" class="btn btn-secondary bmd-btn-icon" title="View slice">
|
||||
<span class="input-group-prepend">
|
||||
<button id="output-file-slice" type="button" class="btn btn-secondary bmd-btn-icon" data-toggle="tooltip" title="View slice">
|
||||
<i class="material-icons">search</i>
|
||||
</button>
|
||||
</span>
|
||||
<input type="number" class="form-control" id="output-file-slice-from" placeholder="From" value="0" step="1024" min="0">
|
||||
<input type="number" class="form-control" id="output-file-slice-from" placeholder="From" value="0" step="128" min="0">
|
||||
<div class="input-group-addon">to</div>
|
||||
<input type="number" class="form-control" id="output-file-slice-to" placeholder="To" value="2048" step="1024" min="0">
|
||||
<input type="number" class="form-control" id="output-file-slice-to" placeholder="To" value="256" step="128" min="0">
|
||||
<div class="input-group-addon">KiB</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -489,6 +491,15 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group option-item">
|
||||
<label for="preserveCR" class="bmd-label-floating"> Preserve carriage returns (0x0d)</label>
|
||||
<select class="form-control" option="preserveCR" id="preserveCR" data-toggle="tooltip" data-placement="bottom" data-offset="-10%" data-html="true" title="HTML textareas don't support carriage returns, so if we want to preserve them in our input, we have to disable editing.<br><br>The default option is to only do this for high-entropy inputs, but you can force the choice using this dropdown.">
|
||||
<option value="entropy">For high-entropy inputs</option>
|
||||
<option value="always">Always</option>
|
||||
<option value="never">Never</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group option-item">
|
||||
<label for="errorTimeout" class="bmd-label-floating">Operation error timeout in ms (0 for never)</label>
|
||||
<input type="number" class="form-control" option="errorTimeout" id="errorTimeout">
|
||||
@@ -573,13 +584,6 @@
|
||||
Keep the current tab in sync between the input and output
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox option-item">
|
||||
<label for="preserveCR" data-toggle="tooltip" data-placement="right" data-html="true" title="As HTML textareas don't support carriage returns, editing input must be turned off to preserve them.<br><br>When this option is enabled, editing is disabled for pasted text that contains carriage returns. Otherwise, editing will remain enabled but carriage returns will not be preserved.">
|
||||
<input type="checkbox" option="preserveCR" id="preserveCR">
|
||||
Preserve carriage returns when pasting an input
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" id="reset-options">Reset options to default</button>
|
||||
@@ -622,7 +626,7 @@
|
||||
<h5 class="modal-title">CyberChef - The Cyber Swiss Army Knife</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<img aria-hidden="true" class="about-img-left" src="<%- require('../static/images/cyberchef-128x128.png') %>" alt="CyberChef Logo"/>
|
||||
<img aria-hidden="true" class="about-img-left" src="<%- require('../static/images/cyberchef-128x128.png').default %>" alt="CyberChef Logo"/>
|
||||
<p class="subtext">
|
||||
Version <%= htmlWebpackPlugin.options.version %><br>
|
||||
Compile time: <%= htmlWebpackPlugin.options.compileTime %>
|
||||
@@ -745,7 +749,7 @@
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
<a href="https://github.com/gchq/CyberChef">
|
||||
<img aria-hidden="true" style="position: absolute; top: 0; right: 0; border: 0;" src="<%- require('../static/images/fork_me.png') %>" alt="Fork me on GitHub">
|
||||
<img aria-hidden="true" style="position: absolute; top: 0; right: 0; border: 0;" src="<%- require('../static/images/fork_me.png').default %>" alt="Fork me on GitHub">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -49,13 +49,12 @@ function main() {
|
||||
attemptHighlight: true,
|
||||
theme: "classic",
|
||||
useMetaKey: false,
|
||||
ioDisplayThreshold: 512,
|
||||
ioDisplayThreshold: 2048,
|
||||
logLevel: "info",
|
||||
autoMagic: true,
|
||||
imagePreview: true,
|
||||
syncTabs: true,
|
||||
preserveCR: true,
|
||||
userSetCR: false
|
||||
preserveCR: "entropy"
|
||||
};
|
||||
|
||||
document.removeEventListener("DOMContentLoaded", main, false);
|
||||
|
||||
@@ -98,6 +98,11 @@
|
||||
.io-card.card input[type=number] {
|
||||
padding-right: 6px;
|
||||
padding-left: 6px;
|
||||
height: unset;
|
||||
}
|
||||
|
||||
.io-card.card .input-group {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
#files .card-header .float-right a:hover {
|
||||
|
||||
@@ -476,7 +476,7 @@ class InputWaiter {
|
||||
*/
|
||||
resetFileThumb() {
|
||||
const fileThumb = document.getElementById("input-file-thumbnail");
|
||||
fileThumb.src = require("../static/images/file-128x128.png");
|
||||
fileThumb.src = require("../static/images/file-128x128.png").default;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -767,7 +767,9 @@ class InputWaiter {
|
||||
// and manually fire inputChange()
|
||||
inputText.value = val;
|
||||
inputText.setSelectionRange(selStart + pastedData.length, selStart + pastedData.length);
|
||||
this.debounceInputChange(e);
|
||||
// Don't debounce here otherwise the keyup event for the Ctrl key will cancel an autobake
|
||||
// (at least for large inputs)
|
||||
this.inputChange(e, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -858,31 +860,29 @@ class InputWaiter {
|
||||
if (input.indexOf("\r") < 0) return false;
|
||||
|
||||
const optionsStr = "This behaviour can be changed in the <a href='#' onclick='document.getElementById(\"options\").click()'>Options pane</a>";
|
||||
if (!this.app.options.userSetCR) {
|
||||
// User has not set a CR preference yet
|
||||
let preserve = await new Promise(function(resolve, reject) {
|
||||
this.app.confirm(
|
||||
"Carriage Return Detected",
|
||||
"A <a href='https://wikipedia.org/wiki/Carriage_return'>carriage return</a> (<code>\\r</code>, <code>0x0d</code>) was detected in your input. As HTML textareas <a href='https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element'>can't display carriage returns</a>, editing must be turned off to preserve them. <br>Alternatively, you can enable editing but your carriage returns will not be preserved.<br><br>This preference will be saved but can be toggled in the options pane.",
|
||||
"Preserve Carriage Returns",
|
||||
"Enable Editing", resolve, this);
|
||||
}.bind(this));
|
||||
if (preserve === undefined) {
|
||||
// The confirm pane was closed without picking a specific choice
|
||||
this.app.alert(`Not preserving carriage returns.\n${optionsStr}`, 5000);
|
||||
preserve = false;
|
||||
}
|
||||
this.manager.options.updateOption("preserveCR", preserve);
|
||||
this.manager.options.updateOption("userSetCR", true);
|
||||
} else {
|
||||
if (this.app.options.preserveCR) {
|
||||
this.app.alert(`A carriage return (\\r, 0x0d) was detected in your input, so editing has been disabled to preserve it.<br>${optionsStr}`, 10000);
|
||||
} else {
|
||||
this.app.alert(`A carriage return (\\r, 0x0d) was detected in your input. Editing is remaining enabled, but carriage returns will not be preserved.<br>${optionsStr}`, 10000);
|
||||
}
|
||||
const preserveStr = `A carriage return (\\r, 0x0d) was detected in your input. To preserve it, editing has been disabled.<br>${optionsStr}`;
|
||||
const dontPreserveStr = `A carriage return (\\r, 0x0d) was detected in your input. It has not been preserved.<br>${optionsStr}`;
|
||||
|
||||
switch (this.app.options.preserveCR) {
|
||||
case "always":
|
||||
this.app.alert(preserveStr, 6000);
|
||||
return true;
|
||||
case "never":
|
||||
this.app.alert(dontPreserveStr, 6000);
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.app.options.preserveCR;
|
||||
// Only preserve for high-entropy inputs
|
||||
const data = Utils.strToArrayBuffer(input);
|
||||
const entropy = Utils.calculateShannonEntropy(data);
|
||||
|
||||
if (entropy > 6) {
|
||||
this.app.alert(preserveStr, 6000);
|
||||
return true;
|
||||
}
|
||||
|
||||
this.app.alert(dontPreserveStr, 6000);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1122,8 +1122,8 @@ class OutputWaiter {
|
||||
showFileOverlay = document.getElementById("show-file-overlay"),
|
||||
sliceFromEl = document.getElementById("output-file-slice-from"),
|
||||
sliceToEl = document.getElementById("output-file-slice-to"),
|
||||
sliceFrom = parseInt(sliceFromEl.value, 10),
|
||||
sliceTo = parseInt(sliceToEl.value, 10),
|
||||
sliceFrom = parseInt(sliceFromEl.value, 10) * 1024,
|
||||
sliceTo = parseInt(sliceToEl.value, 10) * 1024,
|
||||
output = this.outputs[this.manager.tabs.getActiveOutputTab()].data;
|
||||
|
||||
let str;
|
||||
@@ -1137,6 +1137,39 @@ class OutputWaiter {
|
||||
showFileOverlay.style.display = "block";
|
||||
outputText.value = Utils.printable(str, true);
|
||||
|
||||
outputText.style.display = "block";
|
||||
outputHtml.style.display = "none";
|
||||
outputFile.style.display = "none";
|
||||
outputHighlighter.display = "block";
|
||||
inputHighlighter.display = "block";
|
||||
|
||||
this.toggleLoader(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for showing an entire file at user's discretion (even if it's way too big)
|
||||
*/
|
||||
async showAllFile() {
|
||||
document.querySelector("#output-loader .loading-msg").textContent = "Loading entire file at user instruction. This may cause a crash...";
|
||||
this.toggleLoader(true);
|
||||
const outputText = document.getElementById("output-text"),
|
||||
outputHtml = document.getElementById("output-html"),
|
||||
outputFile = document.getElementById("output-file"),
|
||||
outputHighlighter = document.getElementById("output-highlighter"),
|
||||
inputHighlighter = document.getElementById("input-highlighter"),
|
||||
showFileOverlay = document.getElementById("show-file-overlay"),
|
||||
output = this.outputs[this.manager.tabs.getActiveOutputTab()].data;
|
||||
|
||||
let str;
|
||||
if (output.type === "ArrayBuffer") {
|
||||
str = Utils.arrayBufferToStr(output.result);
|
||||
} else {
|
||||
str = Utils.arrayBufferToStr(await this.getDishBuffer(output.dish));
|
||||
}
|
||||
|
||||
outputText.classList.remove("blur");
|
||||
showFileOverlay.style.display = "none";
|
||||
outputText.value = Utils.printable(str, true);
|
||||
|
||||
outputText.style.display = "block";
|
||||
outputHtml.style.display = "none";
|
||||
|
||||
@@ -51,6 +51,7 @@ class RecipeWaiter {
|
||||
}
|
||||
}.bind(this),
|
||||
onSort: function(evt) {
|
||||
this.updateZIndices();
|
||||
if (evt.from.id === "rec-list") {
|
||||
document.dispatchEvent(this.manager.statechange);
|
||||
}
|
||||
@@ -149,6 +150,19 @@ class RecipeWaiter {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the z-index property on each operation to make sure that operations higher in the list
|
||||
* have a higher index, meaning dropdowns are not hidden underneath subsequent operations.
|
||||
*/
|
||||
updateZIndices() {
|
||||
const operations = document.getElementById("rec-list").children;
|
||||
for (let i = 0; i < operations.length; i++) {
|
||||
const operation = operations[i];
|
||||
operation.style.zIndex = 100 + operations.length - i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler for favourite dragover events.
|
||||
* If the element being dragged is an operation, displays a visual cue so that the user knows it can
|
||||
@@ -466,6 +480,7 @@ class RecipeWaiter {
|
||||
log.debug(`'${e.target.querySelector(".op-title").textContent}' added to recipe`);
|
||||
|
||||
this.triggerArgEvents(e.target);
|
||||
this.updateZIndices();
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
}
|
||||
|
||||
|
||||
@@ -187,8 +187,8 @@ TestRegister.addApiTests([
|
||||
const dish = new Dish([file1, file2], Dish.LIST_FILE);
|
||||
|
||||
dish.get(Dish.ARRAY_BUFFER);
|
||||
assert.deepStrictEqual(dish.value, new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b]).buffer);
|
||||
assert.strictEqual(dish.value.byteLength, 11);
|
||||
assert.deepStrictEqual(dish.value, [new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]), new Uint8Array([0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b])]);
|
||||
assert.strictEqual(dish.value.length, 2);
|
||||
|
||||
dish.get(Dish.LIST_FILE);
|
||||
const dataArray = new Uint8Array(dish.value[0].data);
|
||||
|
||||
@@ -41,8 +41,11 @@ import "./tests/DateTime.mjs";
|
||||
import "./tests/ExtractEmailAddresses.mjs";
|
||||
import "./tests/Fork.mjs";
|
||||
import "./tests/FromDecimal.mjs";
|
||||
import "./tests/Gzip.mjs";
|
||||
import "./tests/Gunzip.mjs";
|
||||
import "./tests/Hash.mjs";
|
||||
import "./tests/HaversineDistance.mjs";
|
||||
import "./tests/Hex.mjs";
|
||||
import "./tests/Hexdump.mjs";
|
||||
import "./tests/Image.mjs";
|
||||
import "./tests/IndexOfCoincidence.mjs";
|
||||
@@ -57,6 +60,7 @@ import "./tests/MS.mjs";
|
||||
import "./tests/Magic.mjs";
|
||||
import "./tests/MorseCode.mjs";
|
||||
import "./tests/NetBIOS.mjs";
|
||||
import "./tests/NormaliseUnicode.mjs";
|
||||
import "./tests/OTP.mjs";
|
||||
import "./tests/PGP.mjs";
|
||||
import "./tests/PHP.mjs";
|
||||
|
||||
58
tests/operations/tests/Gunzip.mjs
Normal file
58
tests/operations/tests/Gunzip.mjs
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Gunzip Tests.
|
||||
*
|
||||
* @author n1073645 [n1073645@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Gunzip: No comment, no checksum and no filename",
|
||||
input: "1f8b0800f7c8f85d00ff0dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
|
||||
expectedOutput: "The quick brown fox jumped over the slow dog",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hex",
|
||||
args: ["None"]
|
||||
},
|
||||
{
|
||||
op: "Gunzip",
|
||||
args: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Gunzip: No comment, no checksum and filename",
|
||||
input: "1f8b080843c9f85d00ff66696c656e616d65000dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
|
||||
expectedOutput: "The quick brown fox jumped over the slow dog",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hex",
|
||||
args: ["None"]
|
||||
},
|
||||
{
|
||||
op: "Gunzip",
|
||||
args: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Gunzip: Has a comment, no checksum and has a filename",
|
||||
input: "1f8b08186fc9f85d00ff66696c656e616d6500636f6d6d656e74000dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
|
||||
expectedOutput: "The quick brown fox jumped over the slow dog",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hex",
|
||||
args: ["None"]
|
||||
},
|
||||
{
|
||||
op: "Gunzip",
|
||||
args: []
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
89
tests/operations/tests/Gzip.mjs
Normal file
89
tests/operations/tests/Gzip.mjs
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Gzip Tests.
|
||||
*
|
||||
* @author n1073645 [n1073645@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Gzip: No comment, no checksum and no filename",
|
||||
input: "The quick brown fox jumped over the slow dog",
|
||||
expectedOutput: "0dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Gzip",
|
||||
args: ["Dynamic Huffman Coding", "", "", false]
|
||||
},
|
||||
{
|
||||
op: "Drop bytes",
|
||||
args: [0, 10, false]
|
||||
},
|
||||
{
|
||||
op: "To Hex",
|
||||
args: ["None"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Gzip: No comment, no checksum and has a filename",
|
||||
input: "The quick brown fox jumped over the slow dog",
|
||||
expectedOutput: "636f6d6d656e74000dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Gzip",
|
||||
args: ["Dynamic Huffman Coding", "comment", "", false]
|
||||
},
|
||||
{
|
||||
op: "Drop bytes",
|
||||
args: [0, 10, false]
|
||||
},
|
||||
{
|
||||
op: "To Hex",
|
||||
args: ["None"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Gzip: Has a comment, no checksum and no filename",
|
||||
input: "The quick brown fox jumped over the slow dog",
|
||||
expectedOutput: "636f6d6d656e74000dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Gzip",
|
||||
args: ["Dynamic Huffman Coding", "", "comment", false]
|
||||
},
|
||||
{
|
||||
op: "Drop bytes",
|
||||
args: [0, 10, false]
|
||||
},
|
||||
{
|
||||
op: "To Hex",
|
||||
args: ["None"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Gzip: Has a comment, no checksum and has a filename",
|
||||
input: "The quick brown fox jumped over the slow dog",
|
||||
expectedOutput: "66696c656e616d6500636f6d6d656e74000dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Gzip",
|
||||
args: ["Dynamic Huffman Coding", "filename", "comment", false]
|
||||
},
|
||||
{
|
||||
op: "Drop bytes",
|
||||
args: [0, 10, false]
|
||||
},
|
||||
{
|
||||
op: "To Hex",
|
||||
args: ["None"]
|
||||
}
|
||||
]
|
||||
},
|
||||
]);
|
||||
97
tests/operations/tests/Hex.mjs
Normal file
97
tests/operations/tests/Hex.mjs
Normal file
@@ -0,0 +1,97 @@
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "ASCII to Hex stream",
|
||||
input: "aberystwyth",
|
||||
expectedOutput: "6162657279737477797468",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "To Hex",
|
||||
"args": [
|
||||
"None",
|
||||
0
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "ASCII to Hex with colon deliminator ",
|
||||
input: "aberystwyth",
|
||||
expectedOutput: "61:62:65:72:79:73:74:77:79:74:68",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "To Hex",
|
||||
"args": [
|
||||
"Colon",
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "ASCII to 0x Hex with comma",
|
||||
input: "aberystwyth",
|
||||
expectedOutput: "0x61,0x62,0x65,0x72,0x79,0x73,0x74,0x77,0x79,0x74,0x68",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "To Hex",
|
||||
"args": [
|
||||
"0x with comma",
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "ASCII to 0x Hex with comma and line breaks",
|
||||
input: "aberystwyth",
|
||||
expectedOutput: "0x61,0x62,0x65,0x72,\n0x79,0x73,0x74,0x77,\n0x79,0x74,0x68",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "To Hex",
|
||||
"args": [
|
||||
"0x with comma",
|
||||
4
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Hex stream to UTF-8",
|
||||
input: "e69591e69591e5ada9e5ad90",
|
||||
expectedOutput: "救救孩子",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "From Hex",
|
||||
"args": [
|
||||
"Auto"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
},
|
||||
{
|
||||
name: "Multiline 0x hex to ASCII",
|
||||
input: "0x49,0x20,0x73,0x61,0x77,0x20,0x6d,0x79,0x73,0x65,0x6c,0x66,0x20,0x73,0x69,\
|
||||
0x74,0x74,0x69,0x6e,0x67,0x20,0x69,0x6e,0x20,0x74,0x68,0x65,0x20,0x63,0x72,\
|
||||
0x6f,0x74,0x63,0x68,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x74,0x68,0x69,\
|
||||
0x73,0x20,0x66,0x69,0x67,0x20,0x74,0x72,0x65,0x65,0x2c,0x20,0x73,0x74,0x61,\
|
||||
0x72,0x76,0x69,0x6e,0x67,0x20,0x74,0x6f,0x20,0x64,0x65,0x61,0x74,0x68,0x2c,\
|
||||
0x20,0x6a,0x75,0x73,0x74,0x20,0x62,0x65,0x63,0x61,0x75,0x73,0x65,0x20,0x49,\
|
||||
0x20,0x63,0x6f,0x75,0x6c,0x64,0x6e,0x27,0x74,0x20,0x6d,0x61,0x6b,0x65,0x20,\
|
||||
0x75,0x70,0x20,0x6d,0x79,0x20,0x6d,0x69,0x6e,0x64,0x20,0x77,0x68,0x69,0x63,\
|
||||
0x68,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x66,0x69,0x67,0x73,0x20,0x49,\
|
||||
0x20,0x77,0x6f,0x75,0x6c,0x64,0x20,0x63,0x68,0x6f,0x6f,0x73,0x65,0x2e",
|
||||
expectedOutput: "I saw myself sitting in the crotch of the this fig tree, starving to death, just because I couldn't make up my mind which of the figs I would choose.",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "From Hex",
|
||||
"args": [
|
||||
"Auto"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
]);
|
||||
54
tests/operations/tests/NormaliseUnicode.mjs
Normal file
54
tests/operations/tests/NormaliseUnicode.mjs
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Text Encoding Brute Force tests.
|
||||
*
|
||||
* @author Matthieu [m@tthieux.xyz]
|
||||
*
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Normalise Unicode - NFD",
|
||||
input: "\u00c7\u0043\u0327\u2160",
|
||||
expectedMatch: /C\u0327C\u0327\u2160/,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Normalise Unicode",
|
||||
args: ["NFD"],
|
||||
},
|
||||
],
|
||||
}, {
|
||||
name: "Normalise Unicode - NFC",
|
||||
input: "\u00c7\u0043\u0327\u2160",
|
||||
expectedMatch: /\u00C7\u00C7\u2160/,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Normalise Unicode",
|
||||
args: ["NFC"],
|
||||
},
|
||||
],
|
||||
}, {
|
||||
name: "Normalise Unicode - NFKD",
|
||||
input: "\u00c7\u0043\u0327\u2160",
|
||||
expectedMatch: /C\u0327C\u0327I/,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Normalise Unicode",
|
||||
args: ["NFKD"],
|
||||
},
|
||||
],
|
||||
}, {
|
||||
name: "Normalise Unicode - NFKC",
|
||||
input: "\u00c7\u0043\u0327\u2160",
|
||||
expectedMatch: /\u00C7\u00C7\u2160/,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Normalise Unicode",
|
||||
args: ["NFKC"],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -53,4 +53,136 @@ TestRegister.addTests([
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Case Insensitive Regex: [A-Z] -> [A-Za-z]",
|
||||
input: "[A-Z]",
|
||||
expectedOutput: "[A-Za-z]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Case Insensitive Regex",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Case Insensitive Regex: [a-z] -> [A-Za-z]",
|
||||
input: "[a-z]",
|
||||
expectedOutput: "[A-Za-z]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Case Insensitive Regex",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Case Insensitive Regex: [H-d] -> [A-DH-dh-z]",
|
||||
input: "[H-d]",
|
||||
expectedOutput: "[A-DH-dh-z]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Case Insensitive Regex",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Case Insensitive Regex: [!-D] -> [!-Da-d]",
|
||||
input: "[!-D]",
|
||||
expectedOutput: "[!-Da-d]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Case Insensitive Regex",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Case Insensitive Regex: [%-^] -> [%-^a-z]",
|
||||
input: "[%-^]",
|
||||
expectedOutput: "[%-^a-z]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Case Insensitive Regex",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Case Insensitive Regex: [K-`] -> [K-`k-z]",
|
||||
input: "[K-`]",
|
||||
expectedOutput: "[K-`k-z]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Case Insensitive Regex",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Case Insensitive Regex: [[-}] -> [[-}A-Z]",
|
||||
input: "[[-}]",
|
||||
expectedOutput: "[[-}A-Z]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Case Insensitive Regex",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Case Insensitive Regex: [b-}] -> [b-}B-Z]",
|
||||
input: "[b-}]",
|
||||
expectedOutput: "[b-}B-Z]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Case Insensitive Regex",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Case Insensitive Regex: [<-j] -> [<-z]",
|
||||
input: "[<-j]",
|
||||
expectedOutput: "[<-z]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Case Insensitive Regex",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Case Insensitive Regex: [^-j] -> [A-J^-j]",
|
||||
input: "[^-j]",
|
||||
expectedOutput: "[A-J^-j]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Case Insensitive Regex",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Case Insensitive Regex: not simple test",
|
||||
input: "Mozilla[A-Z0-9]+[A-Z]Mozilla[0-9whatA-Z][H-d][!-H][a-~](.)+",
|
||||
expectedOutput: "[mM][oO][zZ][iI][lL][lL][aA][A-Za-z0-9]+[A-Za-z][mM][oO][zZ][iI][lL][lL][aA][0-9[wW][hH][aA][tT]A-Za-z][A-DH-dh-z][!-Ha-h][a-~A-Z](.)+",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Case Insensitive Regex",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Case Insensitive Regex: erroneous test",
|
||||
input: "Mozilla[A-Z",
|
||||
expectedOutput: "Invalid Regular Expression (Please note this version of node does not support look behinds).",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Case Insensitive Regex",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
}
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user