mirror of
https://github.com/gchq/CyberChef
synced 2025-12-05 23:53:27 +00:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
124ff83206 | ||
|
|
2c9e67ad1d | ||
|
|
75a554e215 | ||
|
|
849d41ee56 | ||
|
|
e18ec5f2b2 | ||
|
|
50e4daeaf2 | ||
|
|
f7f07f2cb5 | ||
|
|
e2161ec934 | ||
|
|
1e4d2ba90d | ||
|
|
caf794b01d | ||
|
|
e81122739b | ||
|
|
a0aa363203 | ||
|
|
bad0816115 | ||
|
|
53a3f3d452 | ||
|
|
ff94172b3c | ||
|
|
af71ca6a25 | ||
|
|
0e7989111f | ||
|
|
8ee3742216 | ||
|
|
194eb184f3 | ||
|
|
98f59ace3a | ||
|
|
c1fb6d9776 | ||
|
|
fc7d2c2f52 | ||
|
|
0fea84ed7a | ||
|
|
5e7f8e3976 | ||
|
|
fe4c5f5899 | ||
|
|
d47b7b9242 | ||
|
|
09b6661e35 | ||
|
|
57b1667b69 | ||
|
|
5e9380b550 | ||
|
|
fc8a0480fb | ||
|
|
317327d097 | ||
|
|
ecd0ac2521 | ||
|
|
f9eaf67db2 | ||
|
|
99e0c8d5e3 | ||
|
|
7a951d86d8 | ||
|
|
d9dfaec84c | ||
|
|
80719ae368 | ||
|
|
9407809356 | ||
|
|
f7e958e7a1 | ||
|
|
5d75f8636a | ||
|
|
22aaeb3ff5 | ||
|
|
8c29ce95e2 | ||
|
|
049fc66785 | ||
|
|
029c55fd53 | ||
|
|
7b433b9bd6 | ||
|
|
5ec210990b | ||
|
|
a7a0cacddb | ||
|
|
e61ced93d6 | ||
|
|
df122da1d2 | ||
|
|
4e00ac9300 | ||
|
|
4ca2a30249 | ||
|
|
08a31523b2 | ||
|
|
4b29a61065 | ||
|
|
06c83cb44c | ||
|
|
75a5fc0ddc | ||
|
|
946d165aa0 | ||
|
|
224d79be05 | ||
|
|
435ed587a5 | ||
|
|
81082ea001 | ||
|
|
1d3229a729 | ||
|
|
a9e60d3450 | ||
|
|
f9ddee7d80 | ||
|
|
ef0d3b73b0 | ||
|
|
5368040e83 | ||
|
|
b9b4147c2f | ||
|
|
caae0ec5ca | ||
|
|
2b47631f4d | ||
|
|
298e8e8491 | ||
|
|
6ad3728314 | ||
|
|
772f9a806e | ||
|
|
ae8d1f2178 | ||
|
|
9bc6c46dc3 |
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://travis-ci.org/gchq/CyberChef)
|
||||
[](https://david-dm.org/gchq/CyberChef)
|
||||
[](https://www.npmjs.com/package/cyberchef)
|
||||
[](https://www.npmjs.com/package/cyberchef)
|
||||

|
||||
[](https://github.com/gchq/CyberChef/blob/master/LICENSE)
|
||||
[](https://gitter.im/gchq/CyberChef?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
@@ -64,9 +64,9 @@ You can use as many operations as you like in simple or complex ways. Some examp
|
||||
- Highlighting
|
||||
- When you highlight text in the input or output, the offset and length values will be displayed and, if possible, the corresponding data will be highlighted in the output or input respectively (example: [highlight the word 'question' in the input to see where it appears in the output][10]).
|
||||
- Save to file and load from file
|
||||
- You can save the output to a file at any time or load a file by dragging and dropping it into the input field (note that files larger than about 500kb may cause your browser to hang or even crash due to the way that browsers handle large amounts of textual data).
|
||||
- You can save the output to a file at any time or load a file by dragging and dropping it into the input field. Files up to around 500MB are supported (depending on your browser), however some operations may take a very long time to run over this much data.
|
||||
- CyberChef is entirely client-side
|
||||
- It should be noted that none of your input or recipe configuration is ever sent to the CyberChef web server - all processing is carried out within your browser, on your own computer.
|
||||
- It should be noted that none of your recipe configuration or input (either text or files) is ever sent to the CyberChef web server - all processing is carried out within your browser, on your own computer.
|
||||
- Due to this feature, CyberChef can be compiled into a single HTML file. You can download this file and drop it into a virtual machine, share it with other people, or use it independently on your desktop.
|
||||
|
||||
|
||||
|
||||
1150
package-lock.json
generated
1150
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "6.6.0",
|
||||
"version": "7.0.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",
|
||||
@@ -36,7 +36,7 @@
|
||||
"css-loader": "^0.28.7",
|
||||
"exports-loader": "^0.6.4",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"file-loader": "^1.1.5",
|
||||
"file-loader": "^1.1.6",
|
||||
"grunt": ">=1.0.1",
|
||||
"grunt-accessibility": "~5.0.0",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
@@ -56,13 +56,13 @@
|
||||
"less-loader": "^4.0.5",
|
||||
"postcss-css-variables": "^0.8.0",
|
||||
"postcss-import": "^11.0.0",
|
||||
"postcss-loader": "^2.0.8",
|
||||
"style-loader": "^0.19.0",
|
||||
"postcss-loader": "^2.0.9",
|
||||
"style-loader": "^0.19.1",
|
||||
"url-loader": "^0.6.2",
|
||||
"val-loader": "^1.1.0",
|
||||
"web-resource-inliner": "^4.2.0",
|
||||
"webpack": "^3.8.1",
|
||||
"webpack-dev-server": "^2.9.4",
|
||||
"webpack": "^3.10.0",
|
||||
"webpack-dev-server": "^2.9.7",
|
||||
"webpack-node-externals": "^1.6.0",
|
||||
"worker-loader": "^1.1.0"
|
||||
},
|
||||
@@ -78,24 +78,27 @@
|
||||
"esmangle": "^1.0.1",
|
||||
"esprima": "^4.0.0",
|
||||
"exif-parser": "^0.1.12",
|
||||
"file-saver": "^1.3.3",
|
||||
"google-code-prettify": "^1.0.5",
|
||||
"jquery": "^3.2.1",
|
||||
"js-crc": "^0.2.0",
|
||||
"js-sha3": "^0.6.1",
|
||||
"js-sha3": "^0.7.0",
|
||||
"jsbn": "^1.1.0",
|
||||
"jsonpath": "^1.0.0",
|
||||
"jsrsasign": "8.0.4",
|
||||
"lodash": "^4.17.4",
|
||||
"moment": "^2.19.2",
|
||||
"moment": "^2.20.1",
|
||||
"moment-timezone": "^0.5.14",
|
||||
"node-md6": "^0.1.0",
|
||||
"nwmatcher": "^1.4.3",
|
||||
"otp": "^0.1.3",
|
||||
"sladex-blowfish": "^0.8.1",
|
||||
"sortablejs": "^1.7.0",
|
||||
"split.js": "^1.3.5",
|
||||
"utf8": "^3.0.0",
|
||||
"vkbeautify": "^0.99.3",
|
||||
"xmldom": "^0.1.27",
|
||||
"xpath": "0.0.24",
|
||||
"xpath": "0.0.27",
|
||||
"zlibjs": "^0.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -19,7 +19,7 @@ const Chef = function() {
|
||||
/**
|
||||
* Runs the recipe over the input.
|
||||
*
|
||||
* @param {string} inputText - The input data as a string
|
||||
* @param {string|ArrayBuffer} input - The input data as a string or ArrayBuffer
|
||||
* @param {Object[]} recipeConfig - The recipe configuration object
|
||||
* @param {Object} options - The options object storing various user choices
|
||||
* @param {boolean} options.attempHighlight - Whether or not to attempt highlighting
|
||||
@@ -33,7 +33,7 @@ const Chef = function() {
|
||||
* @returns {number} response.duration - The number of ms it took to execute the recipe
|
||||
* @returns {number} response.error - The error object thrown by a failed operation (false if no error)
|
||||
*/
|
||||
Chef.prototype.bake = async function(inputText, recipeConfig, options, progress, step) {
|
||||
Chef.prototype.bake = async function(input, recipeConfig, options, progress, step) {
|
||||
let startTime = new Date().getTime(),
|
||||
recipe = new Recipe(recipeConfig),
|
||||
containsFc = recipe.containsFlowControl(),
|
||||
@@ -62,7 +62,8 @@ Chef.prototype.bake = async function(inputText, recipeConfig, options, progress,
|
||||
|
||||
// If starting from scratch, load data
|
||||
if (progress === 0) {
|
||||
this.dish.set(inputText, Dish.STRING);
|
||||
const type = input instanceof ArrayBuffer ? Dish.ARRAY_BUFFER : Dish.STRING;
|
||||
this.dish.set(input, type);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -75,10 +76,16 @@ Chef.prototype.bake = async function(inputText, recipeConfig, options, progress,
|
||||
progress = err.progress;
|
||||
}
|
||||
|
||||
// Depending on the size of the output, we may send it back as a string or an ArrayBuffer.
|
||||
// This can prevent unnecessary casting as an ArrayBuffer can be easily downloaded as a file.
|
||||
// The threshold is specified in KiB.
|
||||
const threshold = (options.outputFileThreshold || 1024) * 1024;
|
||||
const returnType = this.dish.size() > threshold ? Dish.ARRAY_BUFFER : Dish.STRING;
|
||||
|
||||
return {
|
||||
result: this.dish.type === Dish.HTML ?
|
||||
this.dish.get(Dish.HTML) :
|
||||
this.dish.get(Dish.STRING),
|
||||
this.dish.get(returnType),
|
||||
type: Dish.enumLookup(this.dish.type),
|
||||
progress: progress,
|
||||
duration: new Date().getTime() - startTime,
|
||||
|
||||
@@ -8,12 +8,12 @@ import Utils from "./Utils.js";
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @class
|
||||
* @param {byteArray|string|number} value - The value of the input data.
|
||||
* @param {byteArray|string|number|ArrayBuffer} value - The value of the input data.
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
*/
|
||||
const Dish = function(value, type) {
|
||||
this.value = value || typeof value == "string" ? value : null;
|
||||
this.type = type || Dish.BYTE_ARRAY;
|
||||
this.value = value || typeof value === "string" ? value : null;
|
||||
this.type = type || Dish.BYTE_ARRAY;
|
||||
};
|
||||
|
||||
|
||||
@@ -41,6 +41,12 @@ Dish.NUMBER = 2;
|
||||
* @enum
|
||||
*/
|
||||
Dish.HTML = 3;
|
||||
/**
|
||||
* Dish data type enum for ArrayBuffers.
|
||||
* @readonly
|
||||
* @enum
|
||||
*/
|
||||
Dish.ARRAY_BUFFER = 4;
|
||||
|
||||
|
||||
/**
|
||||
@@ -64,6 +70,9 @@ Dish.typeEnum = function(typeStr) {
|
||||
case "html":
|
||||
case "HTML":
|
||||
return Dish.HTML;
|
||||
case "arrayBuffer":
|
||||
case "ArrayBuffer":
|
||||
return Dish.ARRAY_BUFFER;
|
||||
default:
|
||||
throw "Invalid data type string. No matching enum.";
|
||||
}
|
||||
@@ -87,6 +96,8 @@ Dish.enumLookup = function(typeEnum) {
|
||||
return "number";
|
||||
case Dish.HTML:
|
||||
return "html";
|
||||
case Dish.ARRAY_BUFFER:
|
||||
return "ArrayBuffer";
|
||||
default:
|
||||
throw "Invalid data type enum. No matching type.";
|
||||
}
|
||||
@@ -96,12 +107,12 @@ Dish.enumLookup = function(typeEnum) {
|
||||
/**
|
||||
* Sets the data value and type and then validates them.
|
||||
*
|
||||
* @param {byteArray|string|number} value - The value of the input data.
|
||||
* @param {byteArray|string|number|ArrayBuffer} value - The value of the input data.
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
*/
|
||||
Dish.prototype.set = function(value, type) {
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
this.type = type;
|
||||
|
||||
if (!this.valid()) {
|
||||
const sample = Utils.truncate(JSON.stringify(this.value), 13);
|
||||
@@ -114,7 +125,7 @@ Dish.prototype.set = function(value, type) {
|
||||
* Returns the value of the data in the type format specified.
|
||||
*
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
* @returns {byteArray|string|number} The value of the output data.
|
||||
* @returns {byteArray|string|number|ArrayBuffer} The value of the output data.
|
||||
*/
|
||||
Dish.prototype.get = function(type) {
|
||||
if (this.type !== type) {
|
||||
@@ -134,20 +145,23 @@ Dish.prototype.translate = function(toType) {
|
||||
switch (this.type) {
|
||||
case Dish.STRING:
|
||||
this.value = this.value ? Utils.strToByteArray(this.value) : [];
|
||||
this.type = Dish.BYTE_ARRAY;
|
||||
break;
|
||||
case Dish.NUMBER:
|
||||
this.value = typeof this.value == "number" ? Utils.strToByteArray(this.value.toString()) : [];
|
||||
this.type = Dish.BYTE_ARRAY;
|
||||
break;
|
||||
case Dish.HTML:
|
||||
this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : [];
|
||||
this.type = Dish.BYTE_ARRAY;
|
||||
break;
|
||||
case Dish.ARRAY_BUFFER:
|
||||
// Array.from() would be nicer here, but it's slightly slower
|
||||
this.value = Array.prototype.slice.call(new Uint8Array(this.value));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.type = Dish.BYTE_ARRAY;
|
||||
|
||||
// Convert from byteArray to toType
|
||||
switch (toType) {
|
||||
case Dish.STRING:
|
||||
@@ -159,6 +173,10 @@ Dish.prototype.translate = function(toType) {
|
||||
this.value = this.value ? parseFloat(Utils.byteArrayToUtf8(this.value)) : 0;
|
||||
this.type = Dish.NUMBER;
|
||||
break;
|
||||
case Dish.ARRAY_BUFFER:
|
||||
this.value = new Uint8Array(this.value).buffer;
|
||||
this.type = Dish.ARRAY_BUFFER;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -180,7 +198,7 @@ Dish.prototype.valid = function() {
|
||||
|
||||
// Check that every value is a number between 0 - 255
|
||||
for (let i = 0; i < this.value.length; i++) {
|
||||
if (typeof this.value[i] != "number" ||
|
||||
if (typeof this.value[i] !== "number" ||
|
||||
this.value[i] < 0 ||
|
||||
this.value[i] > 255) {
|
||||
return false;
|
||||
@@ -189,18 +207,38 @@ Dish.prototype.valid = function() {
|
||||
return true;
|
||||
case Dish.STRING:
|
||||
case Dish.HTML:
|
||||
if (typeof this.value == "string") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return typeof this.value === "string";
|
||||
case Dish.NUMBER:
|
||||
if (typeof this.value == "number") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return typeof this.value === "number";
|
||||
case Dish.ARRAY_BUFFER:
|
||||
return this.value instanceof ArrayBuffer;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Determines how much space the Dish takes up.
|
||||
* Numbers in JavaScript are 64-bit floating point, however for the purposes of the Dish,
|
||||
* we measure how many bytes are taken up when the number is written as a string.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
Dish.prototype.size = function() {
|
||||
switch (this.type) {
|
||||
case Dish.BYTE_ARRAY:
|
||||
case Dish.STRING:
|
||||
case Dish.HTML:
|
||||
return this.value.length;
|
||||
case Dish.NUMBER:
|
||||
return this.value.toString().length;
|
||||
case Dish.ARRAY_BUFFER:
|
||||
return this.value.byteLength;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export default Dish;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CryptoJS from "crypto-js";
|
||||
import utf8 from "utf8";
|
||||
|
||||
|
||||
/**
|
||||
@@ -64,58 +64,6 @@ const Utils = {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Adds leading zeros to strings
|
||||
*
|
||||
* @param {string} str - String to add leading characters to.
|
||||
* @param {number} max - Maximum width of the string.
|
||||
* @param {char} [chr='0'] - The character to pad with.
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
* // returns "0a"
|
||||
* Utils.padLeft("a", 2);
|
||||
*
|
||||
* // returns "000a"
|
||||
* Utils.padLeft("a", 4);
|
||||
*
|
||||
* // returns "xxxa"
|
||||
* Utils.padLeft("a", 4, "x");
|
||||
*
|
||||
* // returns "bcabchello"
|
||||
* Utils.padLeft("hello", 10, "abc");
|
||||
*/
|
||||
padLeft: function(str, max, chr) {
|
||||
chr = chr || "0";
|
||||
let startIndex = chr.length - (max - str.length);
|
||||
startIndex = startIndex < 0 ? 0 : startIndex;
|
||||
return str.length < max ?
|
||||
Utils.padLeft(chr.slice(startIndex, chr.length) + str, max, chr) : str;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Adds trailing spaces to strings.
|
||||
*
|
||||
* @param {string} str - String to add trailing characters to.
|
||||
* @param {number} max - Maximum width of the string.
|
||||
* @param {char} [chr='0'] - The character to pad with.
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
* // returns "a "
|
||||
* Utils.padRight("a", 4);
|
||||
*
|
||||
* // returns "axxx"
|
||||
* Utils.padRight("a", 4, "x");
|
||||
*/
|
||||
padRight: function(str, max, chr) {
|
||||
chr = chr || " ";
|
||||
return str.length < max ?
|
||||
Utils.padRight(str + chr.slice(0, max-str.length), max, chr) : str;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Adds trailing bytes to a byteArray.
|
||||
*
|
||||
@@ -152,14 +100,6 @@ const Utils = {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @alias Utils.padLeft
|
||||
*/
|
||||
pad: function(str, max, chr) {
|
||||
return Utils.padLeft(str, max, chr);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Truncates a long string to max length and adds suffix.
|
||||
*
|
||||
@@ -201,7 +141,7 @@ const Utils = {
|
||||
hex: function(c, length) {
|
||||
c = typeof c == "string" ? Utils.ord(c) : c;
|
||||
length = length || 2;
|
||||
return Utils.pad(c.toString(16), length);
|
||||
return c.toString(16).padStart(length, "0");
|
||||
},
|
||||
|
||||
|
||||
@@ -222,7 +162,7 @@ const Utils = {
|
||||
bin: function(c, length) {
|
||||
c = typeof c == "string" ? Utils.ord(c) : c;
|
||||
length = length || 8;
|
||||
return Utils.pad(c.toString(2), length);
|
||||
return c.toString(2).padStart(length, "0");
|
||||
},
|
||||
|
||||
|
||||
@@ -340,6 +280,39 @@ const Utils = {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Coverts data of varying types to a byteArray.
|
||||
* Accepts hex, Base64, UTF8 and Latin1 strings.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {string} type - One of "Hex", "Base64", "UTF8" or "Latin1"
|
||||
* @returns {byteArray}
|
||||
*
|
||||
* @example
|
||||
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
|
||||
* Utils.convertToByteArray("Привет", "utf8");
|
||||
*
|
||||
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
|
||||
* Utils.convertToByteArray("d097d0b4d180d0b0d0b2d181d182d0b2d183d0b9d182d0b5", "hex");
|
||||
*
|
||||
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
|
||||
* Utils.convertToByteArray("0JfQtNGA0LDQstGB0YLQstGD0LnRgtC1", "base64");
|
||||
*/
|
||||
convertToByteArray: function(str, type) {
|
||||
switch (type.toLowerCase()) {
|
||||
case "hex":
|
||||
return Utils.fromHex(str);
|
||||
case "base64":
|
||||
return Utils.fromBase64(str, null, "byteArray");
|
||||
case "utf8":
|
||||
return Utils.strToUtf8ByteArray(str);
|
||||
case "latin1":
|
||||
default:
|
||||
return Utils.strToByteArray(str);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Converts a string to a byte array.
|
||||
* Treats the string as UTF-8 if any values are over 255.
|
||||
@@ -381,17 +354,17 @@ const Utils = {
|
||||
* Utils.strToUtf8ByteArray("你好");
|
||||
*/
|
||||
strToUtf8ByteArray: function(str) {
|
||||
let wordArray = CryptoJS.enc.Utf8.parse(str),
|
||||
byteArray = Utils.wordArrayToByteArray(wordArray);
|
||||
const utf8Str = utf8.encode(str);
|
||||
|
||||
if (str.length !== wordArray.sigBytes) {
|
||||
if (str.length !== utf8Str.length) {
|
||||
if (ENVIRONMENT_IS_WORKER()) {
|
||||
self.setOption("attemptHighlight", false);
|
||||
} else if (ENVIRONMENT_IS_WEB()) {
|
||||
window.app.options.attemptHighlight = false;
|
||||
}
|
||||
}
|
||||
return byteArray;
|
||||
|
||||
return Utils.strToByteArray(utf8Str);
|
||||
},
|
||||
|
||||
|
||||
@@ -443,26 +416,21 @@ const Utils = {
|
||||
* Utils.byteArrayToUtf8([228,189,160,229,165,189]);
|
||||
*/
|
||||
byteArrayToUtf8: function(byteArray) {
|
||||
const str = Utils.byteArrayToChars(byteArray);
|
||||
try {
|
||||
// Try to output data as UTF-8 string
|
||||
const words = [];
|
||||
for (let i = 0; i < byteArray.length; i++) {
|
||||
words[i >>> 2] |= byteArray[i] << (24 - (i % 4) * 8);
|
||||
}
|
||||
let wordArray = new CryptoJS.lib.WordArray.init(words, byteArray.length),
|
||||
str = CryptoJS.enc.Utf8.stringify(wordArray);
|
||||
const utf8Str = utf8.decode(str);
|
||||
|
||||
if (str.length !== wordArray.sigBytes) {
|
||||
if (str.length !== utf8Str.length) {
|
||||
if (ENVIRONMENT_IS_WORKER()) {
|
||||
self.setOption("attemptHighlight", false);
|
||||
} else if (ENVIRONMENT_IS_WEB()) {
|
||||
window.app.options.attemptHighlight = false;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
return utf8Str;
|
||||
} catch (err) {
|
||||
// If it fails, treat it as ANSI
|
||||
return Utils.byteArrayToChars(byteArray);
|
||||
return str;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -470,7 +438,7 @@ const Utils = {
|
||||
/**
|
||||
* Converts a charcode array to a string.
|
||||
*
|
||||
* @param {byteArray} byteArray
|
||||
* @param {byteArray|Uint8Array} byteArray
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
@@ -491,33 +459,25 @@ const Utils = {
|
||||
|
||||
|
||||
/**
|
||||
* Converts a CryptoJS.lib.WordArray to a byteArray.
|
||||
*
|
||||
* @param {CryptoJS.lib.WordArray} wordArray
|
||||
* @returns {byteArray}
|
||||
*
|
||||
* Converts an ArrayBuffer to a string.
|
||||
*
|
||||
* @param {ArrayBuffer} arrayBuffer
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
* // returns [84, 101, 115, 116]
|
||||
* Utils.wordArrayToByteArray(CryptoJS.enc.Hex.parse("54657374"));
|
||||
* // returns "hello"
|
||||
* Utils.arrayBufferToStr(Uint8Array.from([104,101,108,108,111]).buffer);
|
||||
*/
|
||||
wordArrayToByteArray: function(wordArray) {
|
||||
if (wordArray.sigBytes <= 0) return [];
|
||||
|
||||
let words = wordArray.words,
|
||||
byteArray = [];
|
||||
|
||||
for (let i = 0; i < wordArray.sigBytes; i++) {
|
||||
byteArray.push((words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
|
||||
}
|
||||
|
||||
return byteArray;
|
||||
arrayBufferToStr: function(arrayBuffer) {
|
||||
const byteArray = Array.prototype.slice.call(new Uint8Array(arrayBuffer));
|
||||
return Utils.byteArrayToUtf8(byteArray);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Base64's the input byte array using the given alphabet, returning a string.
|
||||
*
|
||||
* @param {byteArray|string} data
|
||||
* @param {byteArray|Uint8Array|string} data
|
||||
* @param {string} [alphabet]
|
||||
* @returns {string}
|
||||
*
|
||||
@@ -636,7 +596,7 @@ const Utils = {
|
||||
/**
|
||||
* Convert a byte array into a hex string.
|
||||
*
|
||||
* @param {byteArray} data
|
||||
* @param {Uint8Array|byteArray} data
|
||||
* @param {string} [delim=" "]
|
||||
* @param {number} [padding=2]
|
||||
* @returns {string}
|
||||
@@ -656,7 +616,7 @@ const Utils = {
|
||||
let output = "";
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
output += Utils.pad(data[i].toString(16), padding) + delim;
|
||||
output += data[i].toString(16).padStart(padding, "0") + delim;
|
||||
}
|
||||
|
||||
// Add \x or 0x to beginning
|
||||
@@ -859,7 +819,7 @@ const Utils = {
|
||||
*
|
||||
* fragment = *( pchar / "/" / "?" )
|
||||
* query = *( pchar / "/" / "?" )
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
@@ -1248,21 +1208,6 @@ const Utils = {
|
||||
"None": /\s+/g // Included here to remove whitespace when there shouldn't be any
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* A mapping of string formats to their classes in the CryptoJS library.
|
||||
* @constant
|
||||
*/
|
||||
format: {
|
||||
"Hex": CryptoJS.enc.Hex,
|
||||
"Base64": CryptoJS.enc.Base64,
|
||||
"UTF8": CryptoJS.enc.Utf8,
|
||||
"UTF16": CryptoJS.enc.Utf16,
|
||||
"UTF16LE": CryptoJS.enc.Utf16LE,
|
||||
"UTF16BE": CryptoJS.enc.Utf16BE,
|
||||
"Latin1": CryptoJS.enc.Latin1,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default Utils;
|
||||
@@ -1376,29 +1321,43 @@ String.prototype.count = function(chr) {
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Library overrides ///////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Override for the CryptoJS Hex encoding parser to remove whitespace before attempting to parse
|
||||
* the hex string.
|
||||
*
|
||||
* @param {string} hexStr
|
||||
* @returns {CryptoJS.lib.WordArray}
|
||||
/*
|
||||
* Polyfills
|
||||
*/
|
||||
CryptoJS.enc.Hex.parse = function (hexStr) {
|
||||
// Remove whitespace
|
||||
hexStr = hexStr.replace(/\s/g, "");
|
||||
|
||||
// Shortcut
|
||||
const hexStrLength = hexStr.length;
|
||||
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
|
||||
if (!String.prototype.padStart) {
|
||||
String.prototype.padStart = function padStart(targetLength, padString) {
|
||||
targetLength = targetLength>>0; //floor if number or convert non-number to 0;
|
||||
padString = String((typeof padString !== "undefined" ? padString : " "));
|
||||
if (this.length > targetLength) {
|
||||
return String(this);
|
||||
} else {
|
||||
targetLength = targetLength-this.length;
|
||||
if (targetLength > padString.length) {
|
||||
padString += padString.repeat(targetLength/padString.length); //append to original to ensure we are longer than needed
|
||||
}
|
||||
return padString.slice(0, targetLength) + String(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Convert
|
||||
const words = [];
|
||||
for (let i = 0; i < hexStrLength; i += 2) {
|
||||
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
|
||||
}
|
||||
|
||||
return new CryptoJS.lib.WordArray.init(words, hexStrLength / 2);
|
||||
};
|
||||
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
|
||||
if (!String.prototype.padEnd) {
|
||||
String.prototype.padEnd = function padEnd(targetLength, padString) {
|
||||
targetLength = targetLength>>0; //floor if number or convert non-number to 0;
|
||||
padString = String((typeof padString !== "undefined" ? padString : " "));
|
||||
if (this.length > targetLength) {
|
||||
return String(this);
|
||||
} else {
|
||||
targetLength = targetLength-this.length;
|
||||
if (targetLength > padString.length) {
|
||||
padString += padString.repeat(targetLength/padString.length); //append to original to ensure we are longer than needed
|
||||
}
|
||||
return String(this) + padString.slice(0, targetLength);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ const Categories = [
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Logical operations",
|
||||
name: "Arithmetic / Logic",
|
||||
ops: [
|
||||
"XOR",
|
||||
"XOR Brute Force",
|
||||
@@ -122,6 +122,13 @@ const Categories = [
|
||||
"AND",
|
||||
"ADD",
|
||||
"SUB",
|
||||
"Sum",
|
||||
"Subtract",
|
||||
"Multiply",
|
||||
"Divide",
|
||||
"Mean",
|
||||
"Median",
|
||||
"Standard Deviation",
|
||||
"Bit shift left",
|
||||
"Bit shift right",
|
||||
"Rotate left",
|
||||
@@ -302,6 +309,7 @@ const Categories = [
|
||||
ops: [
|
||||
"Entropy",
|
||||
"Frequency distribution",
|
||||
"Chi Square",
|
||||
"Detect File Type",
|
||||
"Scan for Embedded Files",
|
||||
"Disassemble x86",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import Arithmetic from "../operations/Arithmetic.js";
|
||||
import Base from "../operations/Base.js";
|
||||
import Base58 from "../operations/Base58.js";
|
||||
import Base64 from "../operations/Base64.js";
|
||||
@@ -244,7 +245,7 @@ const OperationConfig = {
|
||||
description: "Base64 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.<br><br>This operation encodes data in an ASCII Base64 string.<br><br>e.g. <code>hello</code> becomes <code>aGVsbG8=</code>",
|
||||
highlight: "func",
|
||||
highlightReverse: "func",
|
||||
inputType: "byteArray",
|
||||
inputType: "ArrayBuffer",
|
||||
outputType: "string",
|
||||
args: [
|
||||
{
|
||||
@@ -519,6 +520,97 @@ const OperationConfig = {
|
||||
}
|
||||
]
|
||||
},
|
||||
"Sum": {
|
||||
module: "Default",
|
||||
description: "Adds together a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>18.5</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
type: "option",
|
||||
value: Arithmetic.DELIM_OPTIONS
|
||||
}
|
||||
]
|
||||
},
|
||||
"Subtract": {
|
||||
module: "Default",
|
||||
description: "Subtracts a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>1.5</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
type: "option",
|
||||
value: Arithmetic.DELIM_OPTIONS
|
||||
}
|
||||
]
|
||||
},
|
||||
"Multiply": {
|
||||
module: "Default",
|
||||
description: "Multiplies a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>40</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
type: "option",
|
||||
value: Arithmetic.DELIM_OPTIONS
|
||||
}
|
||||
]
|
||||
},
|
||||
"Divide": {
|
||||
module: "Default",
|
||||
description: "Divides a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>2.5</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
type: "option",
|
||||
value: Arithmetic.DELIM_OPTIONS
|
||||
}
|
||||
]
|
||||
},
|
||||
"Mean": {
|
||||
module: "Default",
|
||||
description: "Computes the mean (average) of a number list. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5 .5</code> becomes <code>4.75</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
type: "option",
|
||||
value: Arithmetic.DELIM_OPTIONS
|
||||
}
|
||||
]
|
||||
},
|
||||
"Median": {
|
||||
module: "Default",
|
||||
description: "Computes the median of a number list. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 1 .5</code> becomes <code>4.5</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
type: "option",
|
||||
value: Arithmetic.DELIM_OPTIONS
|
||||
}
|
||||
]
|
||||
},
|
||||
"Standard Deviation": {
|
||||
module: "Default",
|
||||
description: "Computes the standard deviation of a number list. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>4.089281382128433</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
type: "option",
|
||||
value: Arithmetic.DELIM_OPTIONS
|
||||
}
|
||||
]
|
||||
},
|
||||
"From Hex": {
|
||||
module: "Default",
|
||||
description: "Converts a hexadecimal byte string back into its raw value.<br><br>e.g. <code>ce 93 ce b5 ce b9 ce ac 20 cf 83 ce bf cf 85 0a</code> becomes the UTF-8 encoded string <code>Γειά σου</code>",
|
||||
@@ -539,7 +631,7 @@ const OperationConfig = {
|
||||
description: "Converts the input string to hexadecimal bytes separated by the specified delimiter.<br><br>e.g. The UTF-8 encoded string <code>Γειά σου</code> becomes <code>ce 93 ce b5 ce b9 ce ac 20 cf 83 ce bf cf 85 0a</code>",
|
||||
highlight: "func",
|
||||
highlightReverse: "func",
|
||||
inputType: "byteArray",
|
||||
inputType: "ArrayBuffer",
|
||||
outputType: "string",
|
||||
args: [
|
||||
{
|
||||
@@ -690,7 +782,7 @@ const OperationConfig = {
|
||||
description: "Creates a hexdump of the input data, displaying both the hexadecimal values of each byte and an ASCII representation alongside.",
|
||||
highlight: "func",
|
||||
highlightReverse: "func",
|
||||
inputType: "byteArray",
|
||||
inputType: "ArrayBuffer",
|
||||
outputType: "string",
|
||||
args: [
|
||||
{
|
||||
@@ -3195,7 +3287,7 @@ const OperationConfig = {
|
||||
"Frequency distribution": {
|
||||
module: "Default",
|
||||
description: "Displays the distribution of bytes in the data as a graph.",
|
||||
inputType: "byteArray",
|
||||
inputType: "ArrayBuffer",
|
||||
outputType: "html",
|
||||
args: [
|
||||
{
|
||||
@@ -3205,6 +3297,13 @@ const OperationConfig = {
|
||||
}
|
||||
]
|
||||
},
|
||||
"Chi Square": {
|
||||
module: "Default",
|
||||
description: "Calculates the Chi Square distribution of values.",
|
||||
inputType: "ArrayBuffer",
|
||||
outputType: "number",
|
||||
args: []
|
||||
},
|
||||
"Numberwang": {
|
||||
module: "Default",
|
||||
description: "Based on the popular gameshow by Mitchell and Webb.",
|
||||
@@ -3280,14 +3379,14 @@ const OperationConfig = {
|
||||
"Detect File Type": {
|
||||
module: "Default",
|
||||
description: "Attempts to guess the MIME (Multipurpose Internet Mail Extensions) type of the data based on 'magic bytes'.<br><br>Currently supports the following file types: 7z, amr, avi, bmp, bz2, class, cr2, crx, dex, dmg, doc, elf, eot, epub, exe, flac, flv, gif, gz, ico, iso, jpg, jxr, m4a, m4v, mid, mkv, mov, mp3, mp4, mpg, ogg, otf, pdf, png, ppt, ps, psd, rar, rtf, sqlite, swf, tar, tar.z, tif, ttf, utf8, vmdk, wav, webm, webp, wmv, woff, woff2, xls, xz, zip.",
|
||||
inputType: "byteArray",
|
||||
inputType: "ArrayBuffer",
|
||||
outputType: "string",
|
||||
args: []
|
||||
},
|
||||
"Scan for Embedded Files": {
|
||||
module: "Default",
|
||||
description: "Scans the data for potential embedded files by looking for magic bytes at all offsets. This operation is prone to false positives.<br><br>WARNING: Files over about 100KB in size will take a VERY long time to process.",
|
||||
inputType: "byteArray",
|
||||
inputType: "ArrayBuffer",
|
||||
outputType: "string",
|
||||
args: [
|
||||
{
|
||||
@@ -3641,7 +3740,7 @@ const OperationConfig = {
|
||||
"<br><br>",
|
||||
"EXIF data from photos usually contains information about the image file itself as well as the device used to create it.",
|
||||
].join("\n"),
|
||||
inputType: "byteArray",
|
||||
inputType: "ArrayBuffer",
|
||||
outputType: "string",
|
||||
args: [],
|
||||
},
|
||||
@@ -3840,7 +3939,7 @@ const OperationConfig = {
|
||||
"Generate HOTP": {
|
||||
module: "Default",
|
||||
description: "The HMAC-based One-Time Password algorithm (HOTP) is an algorithm that computes a one-time password from a shared secret key and an incrementing counter. It has been adopted as Internet Engineering Task Force standard RFC 4226, is the cornerstone of Initiative For Open Authentication (OATH), and is used in a number of two-factor authentication systems.<br><br>Enter the secret as the input or leave it blank for a random secret to be generated.",
|
||||
inputType: "string",
|
||||
inputType: "byteArray",
|
||||
outputType: "string",
|
||||
args: [
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import FlowControl from "../../FlowControl.js";
|
||||
import Arithmetic from "../../operations/Arithmetic.js";
|
||||
import Base from "../../operations/Base.js";
|
||||
import Base58 from "../../operations/Base58.js";
|
||||
import Base64 from "../../operations/Base64.js";
|
||||
@@ -37,8 +38,8 @@ import UUID from "../../operations/UUID.js";
|
||||
*
|
||||
* Libraries:
|
||||
* - Utils.js
|
||||
* - CryptoJS
|
||||
* - otp
|
||||
* - crypto
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
@@ -142,6 +143,7 @@ OpModules.Default = {
|
||||
"Microsoft Script Decoder": MS.runDecodeScript,
|
||||
"Entropy": Entropy.runEntropy,
|
||||
"Frequency distribution": Entropy.runFreqDistrib,
|
||||
"Chi Square": Entropy.runChiSq,
|
||||
"Detect File Type": FileType.runDetect,
|
||||
"Scan for Embedded Files": FileType.runScanForEmbeddedFiles,
|
||||
"Generate UUID": UUID.runGenerateV4,
|
||||
@@ -157,6 +159,13 @@ OpModules.Default = {
|
||||
"Return": FlowControl.runReturn,
|
||||
"Comment": FlowControl.runComment,
|
||||
"PHP Deserialize": PHP.runDeserialize,
|
||||
"Sum": Arithmetic.runSum,
|
||||
"Subtract": Arithmetic.runSub,
|
||||
"Multiply": Arithmetic.runMulti,
|
||||
"Divide": Arithmetic.runDiv,
|
||||
"Mean": Arithmetic.runMean,
|
||||
"Median": Arithmetic.runMedian,
|
||||
"Standard Deviation": Arithmetic.runStdDev,
|
||||
|
||||
|
||||
/*
|
||||
|
||||
252
src/core/operations/Arithmetic.js
Normal file
252
src/core/operations/Arithmetic.js
Normal file
@@ -0,0 +1,252 @@
|
||||
import Utils from "../Utils.js";
|
||||
|
||||
|
||||
/**
|
||||
* Math operations on numbers.
|
||||
*
|
||||
* @author bwhitn [brian.m.whitney@outlook.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
const Arithmetic = {
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
DELIM_OPTIONS: ["Line feed", "Space", "Comma", "Semi-colon", "Colon", "CRLF"],
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string based on a delimiter and calculates the sum of numbers.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
runSum: function(input, args) {
|
||||
const val = Arithmetic._sum(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string based on a delimiter and subtracts all the numbers.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
runSub: function(input, args) {
|
||||
let val = Arithmetic._sub(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string based on a delimiter and multiplies the numbers.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
runMulti: function(input, args) {
|
||||
let val = Arithmetic._multi(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string based on a delimiter and divides the numbers.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
runDiv: function(input, args) {
|
||||
let val = Arithmetic._div(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string based on a delimiter and computes the mean (average).
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
runMean: function(input, args) {
|
||||
let val = Arithmetic._mean(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string based on a delimiter and finds the median.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
runMedian: function(input, args) {
|
||||
let val = Arithmetic._median(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* splits a string based on a delimiter and computes the standard deviation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
runStdDev: function(input, args) {
|
||||
let val = Arithmetic._stdDev(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Converts a string array to a number array.
|
||||
*
|
||||
* @private
|
||||
* @param {string[]} input
|
||||
* @param {string} delim
|
||||
* @returns {number[]}
|
||||
*/
|
||||
_createNumArray: function(input, delim) {
|
||||
delim = Utils.charRep[delim || "Space"];
|
||||
let splitNumbers = input.split(delim),
|
||||
numbers = [],
|
||||
num;
|
||||
|
||||
for (let i = 0; i < splitNumbers.length; i++) {
|
||||
if (splitNumbers[i].indexOf(".") >= 0) {
|
||||
num = parseFloat(splitNumbers[i].trim());
|
||||
} else {
|
||||
num = parseInt(splitNumbers[i].trim(), 0);
|
||||
}
|
||||
if (!isNaN(num)) {
|
||||
numbers.push(num);
|
||||
}
|
||||
}
|
||||
return numbers;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Adds an array of numbers and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
*/
|
||||
_sum: function(data) {
|
||||
if (data.length > 0) {
|
||||
return data.reduce((acc, curr) => acc + curr);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Subtracts an array of numbers and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
*/
|
||||
_sub: function(data) {
|
||||
if (data.length > 0) {
|
||||
return data.reduce((acc, curr) => acc - curr);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Multiplies an array of numbers and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
*/
|
||||
_multi: function(data) {
|
||||
if (data.length > 0) {
|
||||
return data.reduce((acc, curr) => acc * curr);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Divides an array of numbers and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
*/
|
||||
_div: function(data) {
|
||||
if (data.length > 0) {
|
||||
return data.reduce((acc, curr) => acc / curr);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Computes mean of a number array and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
*/
|
||||
_mean: function(data) {
|
||||
if (data.length > 0) {
|
||||
return Arithmetic._sum(data) / data.length;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Computes median of a number array and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
*/
|
||||
_median: function (data) {
|
||||
if ((data.length % 2) === 0) {
|
||||
let first, second;
|
||||
data.sort(function(a, b){
|
||||
return a - b;
|
||||
});
|
||||
first = data[Math.floor(data.length / 2)];
|
||||
second = data[Math.floor(data.length / 2) - 1];
|
||||
return Arithmetic._mean([first, second]);
|
||||
} else {
|
||||
return data[Math.floor(data.length / 2)];
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Computes standard deviation of a number array and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
*/
|
||||
_stdDev: function (data) {
|
||||
if (data.length > 0) {
|
||||
let avg = Arithmetic._mean(data);
|
||||
let devSum = 0;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
devSum += (data[i] - avg) ** 2;
|
||||
}
|
||||
return Math.sqrt(devSum / data.length);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default Arithmetic;
|
||||
@@ -134,11 +134,11 @@ const BCD = {
|
||||
switch (outputFormat) {
|
||||
case "Nibbles":
|
||||
return nibbles.map(n => {
|
||||
return Utils.padLeft(n.toString(2), 4);
|
||||
return n.toString(2).padStart(4, "0");
|
||||
}).join(" ");
|
||||
case "Bytes":
|
||||
return bytes.map(b => {
|
||||
return Utils.padLeft(b.toString(2), 8);
|
||||
return b.toString(2).padStart(8, "0");
|
||||
}).join(" ");
|
||||
case "Raw":
|
||||
default:
|
||||
|
||||
@@ -40,13 +40,13 @@ const Base64 = {
|
||||
/**
|
||||
* To Base64 operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runTo: function(input, args) {
|
||||
const alphabet = args[0] || Base64.ALPHABET;
|
||||
return Utils.toBase64(input, alphabet);
|
||||
return Utils.toBase64(new Uint8Array(input), alphabet);
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ const BitwiseOp = {
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
KEY_FORMAT: ["Hex", "Base64", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1"],
|
||||
KEY_FORMAT: ["Hex", "Base64", "UTF8", "Latin1"],
|
||||
|
||||
/**
|
||||
* XOR operation.
|
||||
@@ -77,12 +77,10 @@ const BitwiseOp = {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runXor: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || ""),
|
||||
const key = Utils.convertToByteArray(args[0].string || "", args[0].option),
|
||||
scheme = args[1],
|
||||
nullPreserving = args[2];
|
||||
|
||||
key = Utils.wordArrayToByteArray(key);
|
||||
|
||||
return BitwiseOp._bitOp(input, key, BitwiseOp._xor, nullPreserving, scheme);
|
||||
},
|
||||
|
||||
@@ -200,8 +198,7 @@ const BitwiseOp = {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runAnd: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || "");
|
||||
key = Utils.wordArrayToByteArray(key);
|
||||
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
|
||||
|
||||
return BitwiseOp._bitOp(input, key, BitwiseOp._and);
|
||||
},
|
||||
@@ -215,8 +212,7 @@ const BitwiseOp = {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runOr: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || "");
|
||||
key = Utils.wordArrayToByteArray(key);
|
||||
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
|
||||
|
||||
return BitwiseOp._bitOp(input, key, BitwiseOp._or);
|
||||
},
|
||||
@@ -230,8 +226,7 @@ const BitwiseOp = {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runAdd: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || "");
|
||||
key = Utils.wordArrayToByteArray(key);
|
||||
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
|
||||
|
||||
return BitwiseOp._bitOp(input, key, BitwiseOp._add);
|
||||
},
|
||||
@@ -245,8 +240,7 @@ const BitwiseOp = {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runSub: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || "");
|
||||
key = Utils.wordArrayToByteArray(key);
|
||||
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
|
||||
|
||||
return BitwiseOp._bitOp(input, key, BitwiseOp._sub);
|
||||
},
|
||||
|
||||
@@ -31,13 +31,13 @@ const ByteRepr = {
|
||||
/**
|
||||
* To Hex operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runToHex: function(input, args) {
|
||||
const delim = Utils.charRep[args[0] || "Space"];
|
||||
return Utils.toHex(input, delim, 2);
|
||||
return Utils.toHex(new Uint8Array(input), delim, 2);
|
||||
},
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ const ByteRepr = {
|
||||
// 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;
|
||||
pos[0].end += 2;
|
||||
}
|
||||
return pos;
|
||||
},
|
||||
@@ -266,7 +266,7 @@ const ByteRepr = {
|
||||
padding = 8;
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
output += Utils.pad(input[i].toString(2), padding) + delim;
|
||||
output += input[i].toString(2).padStart(padding, "0") + delim;
|
||||
}
|
||||
|
||||
if (delim.length) {
|
||||
|
||||
@@ -61,9 +61,9 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
_enc: function (algo, input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || ""),
|
||||
iv = Utils.format[args[1].option].parse(args[1].string || ""),
|
||||
salt = Utils.format[args[2].option].parse(args[2].string || ""),
|
||||
let key = Cipher._format[args[0].option].parse(args[0].string || ""),
|
||||
iv = Cipher._format[args[1].option].parse(args[1].string || ""),
|
||||
salt = Cipher._format[args[2].option].parse(args[2].string || ""),
|
||||
mode = CryptoJS.mode[args[3]],
|
||||
padding = CryptoJS.pad[args[4]],
|
||||
resultOption = args[5].toLowerCase(),
|
||||
@@ -83,12 +83,12 @@ const Cipher = {
|
||||
|
||||
let result = "";
|
||||
if (resultOption === "show all") {
|
||||
result += "Key: " + encrypted.key.toString(Utils.format[outputFormat]);
|
||||
result += "\nIV: " + encrypted.iv.toString(Utils.format[outputFormat]);
|
||||
if (encrypted.salt) result += "\nSalt: " + encrypted.salt.toString(Utils.format[outputFormat]);
|
||||
result += "\n\nCiphertext: " + encrypted.ciphertext.toString(Utils.format[outputFormat]);
|
||||
result += "Key: " + encrypted.key.toString(Cipher._format[outputFormat]);
|
||||
result += "\nIV: " + encrypted.iv.toString(Cipher._format[outputFormat]);
|
||||
if (encrypted.salt) result += "\nSalt: " + encrypted.salt.toString(Cipher._format[outputFormat]);
|
||||
result += "\n\nCiphertext: " + encrypted.ciphertext.toString(Cipher._format[outputFormat]);
|
||||
} else {
|
||||
result = encrypted[resultOption].toString(Utils.format[outputFormat]);
|
||||
result = encrypted[resultOption].toString(Cipher._format[outputFormat]);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -105,9 +105,9 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
_dec: function (algo, input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || ""),
|
||||
iv = Utils.format[args[1].option].parse(args[1].string || ""),
|
||||
salt = Utils.format[args[2].option].parse(args[2].string || ""),
|
||||
let key = Cipher._format[args[0].option].parse(args[0].string || ""),
|
||||
iv = Cipher._format[args[1].option].parse(args[1].string || ""),
|
||||
salt = Cipher._format[args[2].option].parse(args[2].string || ""),
|
||||
mode = CryptoJS.mode[args[3]],
|
||||
padding = CryptoJS.pad[args[4]],
|
||||
inputFormat = args[5],
|
||||
@@ -118,7 +118,7 @@ const Cipher = {
|
||||
return "No input";
|
||||
}
|
||||
|
||||
const ciphertext = Utils.format[inputFormat].parse(input);
|
||||
const ciphertext = Cipher._format[inputFormat].parse(input);
|
||||
|
||||
if (iv.sigBytes === 0) {
|
||||
// Use passphrase rather than key. Need to convert it to a string.
|
||||
@@ -136,7 +136,7 @@ const Cipher = {
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = decrypted.toString(Utils.format[outputFormat]);
|
||||
result = decrypted.toString(Cipher._format[outputFormat]);
|
||||
} catch (err) {
|
||||
result = "Decrypt error: " + err.message;
|
||||
}
|
||||
@@ -260,7 +260,7 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runBlowfishEnc: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1),
|
||||
let key = Cipher._format[args[0].option].parse(args[0].string).toString(Cipher._format.Latin1),
|
||||
mode = args[1],
|
||||
outputFormat = args[2];
|
||||
|
||||
@@ -272,7 +272,7 @@ const Cipher = {
|
||||
}),
|
||||
enc = CryptoJS.enc.Hex.parse(encHex);
|
||||
|
||||
return enc.toString(Utils.format[outputFormat]);
|
||||
return enc.toString(Cipher._format[outputFormat]);
|
||||
},
|
||||
|
||||
|
||||
@@ -284,13 +284,13 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runBlowfishDec: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1),
|
||||
let key = Cipher._format[args[0].option].parse(args[0].string).toString(Cipher._format.Latin1),
|
||||
mode = args[1],
|
||||
inputFormat = args[2];
|
||||
|
||||
if (key.length === 0) return "Enter a key";
|
||||
|
||||
input = Utils.format[inputFormat].parse(input);
|
||||
input = Cipher._format[inputFormat].parse(input);
|
||||
|
||||
return Blowfish.decrypt(input.toString(CryptoJS.enc.Base64), key, {
|
||||
outputType: 0, // This actually means inputType. The library is weird.
|
||||
@@ -329,14 +329,14 @@ const Cipher = {
|
||||
salt = CryptoJS.enc.Hex.parse(args[3] || ""),
|
||||
inputFormat = args[4],
|
||||
outputFormat = args[5],
|
||||
passphrase = Utils.format[inputFormat].parse(input),
|
||||
passphrase = Cipher._format[inputFormat].parse(input),
|
||||
key = CryptoJS.PBKDF2(passphrase, salt, {
|
||||
keySize: keySize,
|
||||
hasher: CryptoJS.algo[hasher],
|
||||
iterations: iterations,
|
||||
});
|
||||
|
||||
return key.toString(Utils.format[outputFormat]);
|
||||
return key.toString(Cipher._format[outputFormat]);
|
||||
},
|
||||
|
||||
|
||||
@@ -354,14 +354,14 @@ const Cipher = {
|
||||
salt = CryptoJS.enc.Hex.parse(args[3] || ""),
|
||||
inputFormat = args[4],
|
||||
outputFormat = args[5],
|
||||
passphrase = Utils.format[inputFormat].parse(input),
|
||||
passphrase = Cipher._format[inputFormat].parse(input),
|
||||
key = CryptoJS.EvpKDF(passphrase, salt, {
|
||||
keySize: keySize,
|
||||
hasher: CryptoJS.algo[hasher],
|
||||
iterations: iterations,
|
||||
});
|
||||
|
||||
return key.toString(Utils.format[outputFormat]);
|
||||
return key.toString(Cipher._format[outputFormat]);
|
||||
},
|
||||
|
||||
|
||||
@@ -373,11 +373,11 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runRc4: function (input, args) {
|
||||
let message = Utils.format[args[1]].parse(input),
|
||||
passphrase = Utils.format[args[0].option].parse(args[0].string),
|
||||
let message = Cipher._format[args[1]].parse(input),
|
||||
passphrase = Cipher._format[args[0].option].parse(args[0].string),
|
||||
encrypted = CryptoJS.RC4.encrypt(message, passphrase);
|
||||
|
||||
return encrypted.ciphertext.toString(Utils.format[args[2]]);
|
||||
return encrypted.ciphertext.toString(Cipher._format[args[2]]);
|
||||
},
|
||||
|
||||
|
||||
@@ -395,12 +395,12 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runRc4drop: function (input, args) {
|
||||
let message = Utils.format[args[1]].parse(input),
|
||||
passphrase = Utils.format[args[0].option].parse(args[0].string),
|
||||
let message = Cipher._format[args[1]].parse(input),
|
||||
passphrase = Cipher._format[args[0].option].parse(args[0].string),
|
||||
drop = args[3],
|
||||
encrypted = CryptoJS.RC4Drop.encrypt(message, passphrase, { drop: drop });
|
||||
|
||||
return encrypted.ciphertext.toString(Utils.format[args[2]]);
|
||||
return encrypted.ciphertext.toString(Cipher._format[args[2]]);
|
||||
},
|
||||
|
||||
|
||||
@@ -783,6 +783,23 @@ const Cipher = {
|
||||
return output;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* A mapping of string formats to their classes in the CryptoJS library.
|
||||
*
|
||||
* @private
|
||||
* @constant
|
||||
*/
|
||||
_format: {
|
||||
"Hex": CryptoJS.enc.Hex,
|
||||
"Base64": CryptoJS.enc.Base64,
|
||||
"UTF8": CryptoJS.enc.Utf8,
|
||||
"UTF16": CryptoJS.enc.Utf16,
|
||||
"UTF16LE": CryptoJS.enc.Utf16LE,
|
||||
"UTF16BE": CryptoJS.enc.Utf16BE,
|
||||
"Latin1": CryptoJS.enc.Latin1,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default Cipher;
|
||||
@@ -827,3 +844,27 @@ CryptoJS.kdf.OpenSSL.execute = function (password, keySize, ivSize, salt) {
|
||||
// Return params
|
||||
return CryptoJS.lib.CipherParams.create({ key: key, iv: iv, salt: salt });
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Override for the CryptoJS Hex encoding parser to remove whitespace before attempting to parse
|
||||
* the hex string.
|
||||
*
|
||||
* @param {string} hexStr
|
||||
* @returns {CryptoJS.lib.WordArray}
|
||||
*/
|
||||
CryptoJS.enc.Hex.parse = function (hexStr) {
|
||||
// Remove whitespace
|
||||
hexStr = hexStr.replace(/\s/g, "");
|
||||
|
||||
// Shortcut
|
||||
const hexStrLength = hexStr.length;
|
||||
|
||||
// Convert
|
||||
const words = [];
|
||||
for (let i = 0; i < hexStrLength; i += 2) {
|
||||
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
|
||||
}
|
||||
|
||||
return new CryptoJS.lib.WordArray.init(words, hexStrLength / 2);
|
||||
};
|
||||
|
||||
@@ -2,9 +2,10 @@ import {camelCase, kebabCase, snakeCase} from "lodash";
|
||||
|
||||
import Utils from "../Utils.js";
|
||||
import vkbeautify from "vkbeautify";
|
||||
import {DOMParser as dom} from "xmldom";
|
||||
import {DOMParser} from "xmldom";
|
||||
import xpath from "xpath";
|
||||
import jpath from "jsonpath";
|
||||
import nwmatcher from "nwmatcher";
|
||||
import prettyPrintOne from "imports-loader?window=>global!exports-loader?prettyPrintOne!google-code-prettify/bin/prettify.min.js";
|
||||
|
||||
|
||||
@@ -336,7 +337,7 @@ const Code = {
|
||||
|
||||
let doc;
|
||||
try {
|
||||
doc = new dom().parseFromString(input);
|
||||
doc = new DOMParser().parseFromString(input, "application/xml");
|
||||
} catch (err) {
|
||||
return "Invalid input XML.";
|
||||
}
|
||||
@@ -423,7 +424,7 @@ const Code = {
|
||||
let query = args[0],
|
||||
delimiter = args[1],
|
||||
parser = new DOMParser(),
|
||||
html,
|
||||
dom,
|
||||
result;
|
||||
|
||||
if (!query.length || !input.length) {
|
||||
@@ -431,32 +432,32 @@ const Code = {
|
||||
}
|
||||
|
||||
try {
|
||||
html = parser.parseFromString(input, "text/html");
|
||||
dom = parser.parseFromString(input);
|
||||
} catch (err) {
|
||||
return "Invalid input HTML.";
|
||||
}
|
||||
|
||||
try {
|
||||
result = html.querySelectorAll(query);
|
||||
const matcher = nwmatcher({document: dom});
|
||||
result = matcher.select(query, dom);
|
||||
} catch (err) {
|
||||
return "Invalid CSS Selector. Details:\n" + err.message;
|
||||
}
|
||||
|
||||
const nodeToString = function(node) {
|
||||
return node.toString();
|
||||
/* xmldom does not return the outerHTML value.
|
||||
switch (node.nodeType) {
|
||||
case Node.ELEMENT_NODE: return node.outerHTML;
|
||||
case Node.ATTRIBUTE_NODE: return node.value;
|
||||
case Node.COMMENT_NODE: return node.data;
|
||||
case Node.TEXT_NODE: return node.wholeText;
|
||||
case Node.DOCUMENT_NODE: return node.outerHTML;
|
||||
case node.ELEMENT_NODE: return node.outerHTML;
|
||||
case node.ATTRIBUTE_NODE: return node.value;
|
||||
case node.TEXT_NODE: return node.wholeText;
|
||||
case node.COMMENT_NODE: return node.data;
|
||||
case node.DOCUMENT_NODE: return node.outerHTML;
|
||||
default: throw new Error("Unknown Node Type: " + node.nodeType);
|
||||
}
|
||||
}*/
|
||||
};
|
||||
|
||||
return Array.apply(null, Array(result.length))
|
||||
.map(function(_, i) {
|
||||
return result[i];
|
||||
})
|
||||
return result
|
||||
.map(nodeToString)
|
||||
.join(delimiter);
|
||||
},
|
||||
|
||||
@@ -418,9 +418,9 @@ const Compress = {
|
||||
}
|
||||
};
|
||||
|
||||
const fileSize = Utils.padLeft(input.length.toString(8), 11, "0");
|
||||
const fileSize = input.length.toString(8).padStart(11, "0");
|
||||
const currentUnixTimestamp = Math.floor(Date.now() / 1000);
|
||||
const lastModTime = Utils.padLeft(currentUnixTimestamp.toString(8), 11, "0");
|
||||
const lastModTime = currentUnixTimestamp.toString(8).padStart(11, "0");
|
||||
|
||||
const file = {
|
||||
fileName: Utils.padBytesRight(args[0], 100),
|
||||
@@ -452,7 +452,7 @@ const Compress = {
|
||||
}
|
||||
});
|
||||
}
|
||||
checksum = Utils.padBytesRight(Utils.padLeft(checksum.toString(8), 7, "0"), 8);
|
||||
checksum = Utils.padBytesRight(checksum.toString(8).padStart(7, "0"), 8);
|
||||
file.checksum = checksum;
|
||||
|
||||
const tarball = new Tarball();
|
||||
|
||||
@@ -81,22 +81,23 @@ const Entropy = {
|
||||
/**
|
||||
* Frequency distribution operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
runFreqDistrib: function (input, args) {
|
||||
if (!input.length) return "No data";
|
||||
const data = new Uint8Array(input);
|
||||
if (!data.length) return "No data";
|
||||
|
||||
let distrib = new Array(256).fill(0),
|
||||
percentages = new Array(256),
|
||||
len = input.length,
|
||||
len = data.length,
|
||||
showZeroes = args[0],
|
||||
i;
|
||||
|
||||
// Count bytes
|
||||
for (i = 0; i < len; i++) {
|
||||
distrib[input[i]]++;
|
||||
distrib[data[i]]++;
|
||||
}
|
||||
|
||||
// Calculate percentages
|
||||
@@ -126,7 +127,7 @@ const Entropy = {
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (distrib[i] || showZeroes) {
|
||||
output += " " + Utils.hex(i, 2) + " (" +
|
||||
Utils.padRight(percentages[i].toFixed(2).replace(".00", "") + "%)", 8) +
|
||||
(percentages[i].toFixed(2).replace(".00", "") + "%)").padEnd(8, " ") +
|
||||
Array(Math.ceil(percentages[i])+1).join("|") + "\n";
|
||||
}
|
||||
}
|
||||
@@ -135,6 +136,32 @@ const Entropy = {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Chi Square operation.
|
||||
*
|
||||
* @param {ArrayBuffer} data
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
runChiSq: function(input, args) {
|
||||
const data = new Uint8Array(input);
|
||||
let distArray = new Array(256).fill(0),
|
||||
total = 0;
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
distArray[data[i]]++;
|
||||
}
|
||||
|
||||
for (let i = 0; i < distArray.length; i++) {
|
||||
if (distArray[i] > 0) {
|
||||
total += Math.pow(distArray[i] - data.length / 256, 2) / (data.length / 256);
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the Shannon entropy for a given chunk of data.
|
||||
*
|
||||
|
||||
@@ -15,12 +15,13 @@ const FileType = {
|
||||
/**
|
||||
* Detect File Type operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runDetect: function(input, args) {
|
||||
const type = FileType.magicType(input);
|
||||
const data = new Uint8Array(input),
|
||||
type = FileType.magicType(data);
|
||||
|
||||
if (!type) {
|
||||
return "Unknown file type. Have you tried checking the entropy of this data to determine whether it might be encrypted or compressed?";
|
||||
@@ -46,20 +47,21 @@ const FileType = {
|
||||
/**
|
||||
* Scan for Embedded Files operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runScanForEmbeddedFiles: function(input, args) {
|
||||
let output = "Scanning data for 'magic bytes' which may indicate embedded files. The following results may be false positives and should not be treat as reliable. Any suffiently long file is likely to contain these magic bytes coincidentally.\n",
|
||||
type,
|
||||
ignoreCommon = args[0],
|
||||
commonExts = ["ico", "ttf", ""],
|
||||
numFound = 0,
|
||||
numCommonFound = 0;
|
||||
const ignoreCommon = args[0],
|
||||
commonExts = ["ico", "ttf", ""],
|
||||
data = new Uint8Array(input);
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
type = FileType.magicType(input.slice(i));
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
type = FileType.magicType(data.slice(i));
|
||||
if (type) {
|
||||
if (ignoreCommon && commonExts.indexOf(type.ext) > -1) {
|
||||
numCommonFound++;
|
||||
@@ -96,7 +98,7 @@ const FileType = {
|
||||
* Given a buffer, detects magic byte sequences at specific positions and returns the
|
||||
* extension and mime type.
|
||||
*
|
||||
* @param {byteArray} buf
|
||||
* @param {Uint8Array} buf
|
||||
* @returns {Object} type
|
||||
* @returns {string} type.ext - File extension
|
||||
* @returns {string} type.mime - Mime type
|
||||
|
||||
@@ -215,9 +215,9 @@ const HTML = {
|
||||
k = k.toFixed(2);
|
||||
|
||||
let hex = "#" +
|
||||
Utils.padLeft(Math.round(r).toString(16), 2) +
|
||||
Utils.padLeft(Math.round(g).toString(16), 2) +
|
||||
Utils.padLeft(Math.round(b).toString(16), 2),
|
||||
Math.round(r).toString(16).padStart(2, "0") +
|
||||
Math.round(g).toString(16).padStart(2, "0") +
|
||||
Math.round(b).toString(16).padStart(2, "0"),
|
||||
rgb = "rgb(" + r + ", " + g + ", " + b + ")",
|
||||
rgba = "rgba(" + r + ", " + g + ", " + b + ", " + a + ")",
|
||||
hsl = "hsl(" + h + ", " + s + "%, " + l + "%)",
|
||||
|
||||
@@ -31,18 +31,19 @@ const Hexdump = {
|
||||
/**
|
||||
* To Hexdump operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runTo: function(input, args) {
|
||||
const data = new Uint8Array(input);
|
||||
const length = args[0] || Hexdump.WIDTH;
|
||||
const upperCase = args[1];
|
||||
const includeFinalLength = args[2];
|
||||
|
||||
let output = "", padding = 2;
|
||||
for (let i = 0; i < input.length; i += length) {
|
||||
const buff = input.slice(i, i+length);
|
||||
for (let i = 0; i < data.length; i += length) {
|
||||
const buff = data.slice(i, i+length);
|
||||
let hexa = "";
|
||||
for (let j = 0; j < buff.length; j++) {
|
||||
hexa += Utils.hex(buff[j], padding) + " ";
|
||||
@@ -56,10 +57,10 @@ const Hexdump = {
|
||||
}
|
||||
|
||||
output += lineNo + " " +
|
||||
Utils.padRight(hexa, (length*(padding+1))) +
|
||||
" |" + Utils.padRight(Utils.printable(Utils.byteArrayToChars(buff)), buff.length) + "|\n";
|
||||
hexa.padEnd(length*(padding+1), " ") +
|
||||
" |" + Utils.printable(Utils.byteArrayToChars(buff)).padEnd(buff.length, " ") + "|\n";
|
||||
|
||||
if (includeFinalLength && i+buff.length === input.length) {
|
||||
if (includeFinalLength && i+buff.length === data.length) {
|
||||
output += Utils.hex(i+buff.length, 8) + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,13 @@ const Image = {
|
||||
*
|
||||
* Extracts EXIF data from a byteArray, representing a JPG or a TIFF image.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runExtractEXIF(input, args) {
|
||||
try {
|
||||
const bytes = Uint8Array.from(input);
|
||||
const parser = ExifParser.create(bytes.buffer);
|
||||
const parser = ExifParser.create(input);
|
||||
const result = parser.parse();
|
||||
|
||||
let lines = [];
|
||||
@@ -53,7 +52,7 @@ const Image = {
|
||||
* @author David Moodie [davidmoodie12@gmail.com]
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runRemoveEXIF(input, args) {
|
||||
// Do nothing if input is empty
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Microsoft operations.
|
||||
* Microsoft operations.
|
||||
*
|
||||
* @author bmwhitn [brian.m.whitney@outlook.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
|
||||
@@ -26,9 +26,14 @@ const NetBIOS = {
|
||||
let output = [],
|
||||
offset = args[0];
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
output.push((input[i] >> 4) + offset);
|
||||
output.push((input[i] & 0xf) + offset);
|
||||
if (input.length <= 16) {
|
||||
let len = input.length;
|
||||
input.length = 16;
|
||||
input.fill(32, len, 16);
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
output.push((input[i] >> 4) + offset);
|
||||
output.push((input[i] & 0xf) + offset);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
@@ -46,9 +51,15 @@ const NetBIOS = {
|
||||
let output = [],
|
||||
offset = args[0];
|
||||
|
||||
for (let i = 0; i < input.length; i += 2) {
|
||||
output.push(((input[i] - offset) << 4) |
|
||||
((input[i + 1] - offset) & 0xf));
|
||||
if (input.length <= 32 && (input.length % 2) === 0) {
|
||||
for (let i = 0; i < input.length; i += 2) {
|
||||
output.push((((input[i] & 0xff) - offset) << 4) |
|
||||
(((input[i + 1] & 0xff) - offset) & 0xf));
|
||||
}
|
||||
for (let i = output.length - 1; i > 0; i--) {
|
||||
if (output[i] === 32) output.splice(i, i);
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
@@ -14,16 +14,72 @@ const Numberwang = {
|
||||
* @returns {string}
|
||||
*/
|
||||
run: function(input, args) {
|
||||
if (!input) return "Let's play Wangernumb!";
|
||||
const match = input.match(/\d+/);
|
||||
if (match) {
|
||||
return match[0] + "! That's Numberwang!";
|
||||
let output;
|
||||
if (!input) {
|
||||
output = "Let's play Wangernumb!";
|
||||
} else {
|
||||
// That's a bad miss!
|
||||
return "Sorry, that's not Numberwang. Let's rotate the board!";
|
||||
const match = input.match(/(f0rty-s1x|shinty-six|filth-hundred and neeb|-?√?\d+(\.\d+)?i?([a-z]?)%?)/i);
|
||||
if (match) {
|
||||
if (match[3]) output = match[0] + "! That's AlphaNumericWang!";
|
||||
else output = match[0] + "! That's Numberwang!";
|
||||
} else {
|
||||
// That's a bad miss!
|
||||
output = "Sorry, that's not Numberwang. Let's rotate the board!";
|
||||
}
|
||||
}
|
||||
|
||||
const rand = Math.floor(Math.random() * Numberwang._didYouKnow.length);
|
||||
return output + "\n\nDid you know: " + Numberwang._didYouKnow[rand];
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Taken from http://numberwang.wikia.com/wiki/Numberwang_Wikia
|
||||
*
|
||||
* @private
|
||||
* @constant
|
||||
*/
|
||||
_didYouKnow: [
|
||||
"Numberwang, contrary to popular belief, is a fruit and not a vegetable.",
|
||||
"Robert Webb once got WordWang while presenting an episode of Numberwang.",
|
||||
"The 6705th digit of pi is Numberwang.",
|
||||
"Numberwang was invented on a Sevenday.",
|
||||
"Contrary to popular belief, Albert Einstein always got good grades in Numberwang at school. He once scored ^4$ on a test.",
|
||||
"680 asteroids have been named after Numberwang.",
|
||||
"Archimedes is most famous for proclaiming \"That's Numberwang!\" during an epiphany about water displacement he had while taking a bath.",
|
||||
"Numberwang Day is celebrated in Japan on every day of the year apart from June 6.",
|
||||
"Biologists recently discovered Numberwang within a strand of human DNA.",
|
||||
"Numbernot is a special type of non-Numberwang number. It is divisible by 3 and the letter \"y\".",
|
||||
"Julie once got 612.04 Numberwangs in a single episode of Emmerdale.",
|
||||
"In India, it is traditional to shout out \"Numberwang!\" instead of checkmate during games of chess.",
|
||||
"There is a rule on Countdown which states that if you get Numberwang in the numbers round, you automatically win. It has only ever been invoked twice.",
|
||||
"\"Numberwang\" was the third-most common baby name for a brief period in 1722.",
|
||||
"\"The Lion King\" was loosely based on Numberwang.",
|
||||
"\"A Numberwang a day keeps the doctor away\" is how Donny Cosy, the oldest man in the world, explained how he was in such good health at the age of 136.",
|
||||
"The \"number lock\" button on a keyboard is based on the popular round of the same name in \"Numberwang\".",
|
||||
"Cambridge became the first university to offer a course in Numberwang in 1567.",
|
||||
"Schrödinger's Numberwang is a number that has been confusing dentists for centuries.",
|
||||
"\"Harry Potter and the Numberwang of Numberwang\" was rejected by publishers -41 times before it became a bestseller.",
|
||||
"\"Numberwang\" is the longest-running British game show in history; it has aired 226 seasons, each containing 19 episodes, which makes a grand total of 132 episodes.",
|
||||
"The triple Numberwang bonus was discovered by archaeologist Thomas Jefferson in Somerset.",
|
||||
"Numberwang is illegal in parts of Czechoslovakia.",
|
||||
"Numberwang was discovered in India in the 12th century.",
|
||||
"Numberwang has the chemical formula Zn4SO2(HgEs)3.",
|
||||
"The first pack of cards ever created featured two \"Numberwang\" cards instead of jokers.",
|
||||
"Julius Caesar was killed by an overdose of Numberwang.",
|
||||
"The most Numberwang musical note is G#.",
|
||||
"In 1934, the forty-third Google Doodle promoted the upcoming television show \"Numberwang on Ice\".",
|
||||
"A recent psychology study found that toddlers were 17% faster at identifying numbers which were Numberwang.",
|
||||
"There are 700 ways to commit a foul in the television show \"Numberwang\". All 700 of these fouls were committed by Julie in one single episode in 1473.",
|
||||
"Astronomers suspect God is Numberwang.",
|
||||
"Numberwang is the official beverage of Canada.",
|
||||
"In the pilot episode of \"The Price is Right\", if a contestant got the value of an item exactly right they were told \"That's Numberwang!\" and immediately won ₹5.7032.",
|
||||
"The first person to get three Numberwangs in a row was Madonna.",
|
||||
"\"Numberwang\" has the code U+46402 in Unicode.",
|
||||
"The musical note \"Numberwang\" is between D# and E♮.",
|
||||
"Numberwang was first played on the moon in 1834.",
|
||||
],
|
||||
|
||||
};
|
||||
|
||||
export default Numberwang;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import otp from "otp";
|
||||
import Base64 from "./Base64.js";
|
||||
|
||||
|
||||
/**
|
||||
* One-Time Password operations.
|
||||
*
|
||||
|
||||
@@ -121,8 +121,7 @@ const PublicKey = {
|
||||
// Format Public Key fields
|
||||
for (let i = 0; i < pkFields.length; i++) {
|
||||
pkStr += " " + pkFields[i].key + ":" +
|
||||
Utils.padLeft(
|
||||
pkFields[i].value + "\n",
|
||||
(pkFields[i].value + "\n").padStart(
|
||||
18 - (pkFields[i].key.length + 3) + pkFields[i].value.length + 1,
|
||||
" "
|
||||
);
|
||||
@@ -286,9 +285,9 @@ ${extensions}`;
|
||||
|
||||
key = fields[i].split("=")[0];
|
||||
value = fields[i].split("=")[1];
|
||||
str = Utils.padRight(key, maxKeyLen) + " = " + value + "\n";
|
||||
str = key.padEnd(maxKeyLen, " ") + " = " + value + "\n";
|
||||
|
||||
output += Utils.padLeft(str, indent + str.length, " ");
|
||||
output += str.padStart(indent + str.length, " ");
|
||||
}
|
||||
|
||||
return output.slice(0, -1);
|
||||
@@ -314,7 +313,7 @@ ${extensions}`;
|
||||
if (i === 0) {
|
||||
output += str;
|
||||
} else {
|
||||
output += Utils.padLeft(str, indent + str.length, " ");
|
||||
output += str.padStart(indent + str.length, " ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ const SeqUtils = {
|
||||
width = lines.length.toString().length;
|
||||
|
||||
for (let n = 0; n < lines.length; n++) {
|
||||
output += Utils.pad((n+1).toString(), width, " ") + " " + lines[n] + "\n";
|
||||
output += (n+1).toString().padStart(width, " ") + " " + lines[n] + "\n";
|
||||
}
|
||||
return output.slice(0, output.length-1);
|
||||
},
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import Utils from "../Utils.js";
|
||||
|
||||
|
||||
/**
|
||||
* Tidy operations.
|
||||
*
|
||||
@@ -229,11 +226,11 @@ const Tidy = {
|
||||
|
||||
if (position === "Start") {
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
output += Utils.padLeft(lines[i], lines[i].length+len, chr) + "\n";
|
||||
output += lines[i].padStart(lines[i].length+len, chr) + "\n";
|
||||
}
|
||||
} else if (position === "End") {
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
output += Utils.padRight(lines[i], lines[i].length+len, chr) + "\n";
|
||||
output += lines[i].padEnd(lines[i].length+len, chr) + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* globals unescape */
|
||||
import Utils from "../Utils.js";
|
||||
import url from "url";
|
||||
|
||||
|
||||
@@ -78,7 +77,7 @@ const URL_ = {
|
||||
|
||||
output += "Arguments:\n";
|
||||
for (let key in uri.query) {
|
||||
output += "\t" + Utils.padRight(key, padding);
|
||||
output += "\t" + key.padEnd(padding, " ");
|
||||
if (uri.query[key].length) {
|
||||
output += " = " + uri.query[key] + "\n";
|
||||
} else {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import crypto from "crypto";
|
||||
|
||||
|
||||
/**
|
||||
* UUID operations.
|
||||
*
|
||||
@@ -17,25 +20,17 @@ const UUID = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runGenerateV4: function(input, args) {
|
||||
if (window && typeof(window.crypto) !== "undefined" && typeof(window.crypto.getRandomValues) !== "undefined") {
|
||||
let buf = new Uint32Array(4),
|
||||
i = 0;
|
||||
window.crypto.getRandomValues(buf);
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
||||
let r = (buf[i >> 3] >> ((i % 8) * 4)) & 0xf,
|
||||
v = c === "x" ? r : (r & 0x3 | 0x8);
|
||||
i++;
|
||||
return v.toString(16);
|
||||
});
|
||||
} else {
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
||||
let r = Math.random() * 16 | 0,
|
||||
v = c === "x" ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
const buf = new Uint32Array(4).map(() => {
|
||||
return crypto.randomBytes(4).readUInt32BE(0, true);
|
||||
});
|
||||
let i = 0;
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
||||
let r = (buf[i >> 3] >> ((i % 8) * 4)) & 0xf,
|
||||
v = c === "x" ? r : (r & 0x3 | 0x8);
|
||||
i++;
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default UUID;
|
||||
|
||||
@@ -529,7 +529,7 @@ App.prototype.setCompileMessage = function() {
|
||||
|
||||
/**
|
||||
* Determines whether the browser supports Local Storage and if it is accessible.
|
||||
*
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
App.prototype.isLocalStorageAvailable = function() {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import Utils from "../core/Utils.js";
|
||||
|
||||
|
||||
/**
|
||||
* Waiter to handle events related to highlighting in CyberChef.
|
||||
*
|
||||
@@ -312,9 +309,9 @@ HighlighterWaiter.prototype.outputHtmlMousemove = function(e) {
|
||||
HighlighterWaiter.prototype.selectionInfo = function(start, end) {
|
||||
const len = end.toString().length;
|
||||
const width = len < 2 ? 2 : len;
|
||||
const startStr = Utils.pad(start.toString(), width, " ").replace(/ /g, " ");
|
||||
const endStr = Utils.pad(end.toString(), width, " ").replace(/ /g, " ");
|
||||
const lenStr = Utils.pad((end-start).toString(), width, " ").replace(/ /g, " ");
|
||||
const startStr = start.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
const endStr = end.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
const lenStr = (end-start).toString().padStart(width, " ").replace(/ /g, " ");
|
||||
|
||||
return "start: " + startStr + "<br>end: " + endStr + "<br>length: " + lenStr;
|
||||
};
|
||||
@@ -402,7 +399,7 @@ HighlighterWaiter.prototype.highlight = function(textarea, highlighter, pos) {
|
||||
|
||||
// Check if there is a carriage return in the output dish as this will not
|
||||
// be displayed by the HTML textarea and will mess up highlighting offsets.
|
||||
if (!this.app.dishStr || this.app.dishStr.indexOf("\r") >= 0) return false;
|
||||
if (this.manager.output.containsCR()) return false;
|
||||
|
||||
const startPlaceholder = "[startHighlight]";
|
||||
const startPlaceholderRegex = /\[startHighlight\]/g;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Utils from "../core/Utils.js";
|
||||
import LoaderWorker from "worker-loader?inline&fallback=false!./LoaderWorker.js";
|
||||
|
||||
|
||||
/**
|
||||
@@ -33,6 +33,9 @@ const InputWaiter = function(app, manager) {
|
||||
144, //Num
|
||||
145, //Scroll
|
||||
];
|
||||
|
||||
this.loaderWorker = null;
|
||||
this.fileBuffer = null;
|
||||
};
|
||||
|
||||
|
||||
@@ -42,20 +45,47 @@ const InputWaiter = function(app, manager) {
|
||||
* @returns {string}
|
||||
*/
|
||||
InputWaiter.prototype.get = function() {
|
||||
return document.getElementById("input-text").value;
|
||||
return this.fileBuffer || document.getElementById("input-text").value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the input in the input textarea.
|
||||
* Sets the input in the input area.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {string|File} input
|
||||
*
|
||||
* @fires Manager#statechange
|
||||
*/
|
||||
InputWaiter.prototype.set = function(input) {
|
||||
document.getElementById("input-text").value = input;
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
const inputText = document.getElementById("input-text");
|
||||
if (input instanceof File) {
|
||||
this.setFile(input);
|
||||
inputText.value = "";
|
||||
} else {
|
||||
inputText.value = input;
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shows file details.
|
||||
*
|
||||
* @param {File} file
|
||||
*/
|
||||
InputWaiter.prototype.setFile = function(file) {
|
||||
// Display file overlay in input area with details
|
||||
const fileOverlay = document.getElementById("input-file"),
|
||||
fileName = document.getElementById("input-file-name"),
|
||||
fileSize = document.getElementById("input-file-size"),
|
||||
fileType = document.getElementById("input-file-type"),
|
||||
fileLoaded = document.getElementById("input-file-loaded");
|
||||
|
||||
fileOverlay.style.display = "block";
|
||||
fileName.textContent = file.name;
|
||||
fileSize.textContent = file.size.toLocaleString() + " bytes";
|
||||
fileType.textContent = file.type || "unknown";
|
||||
fileLoaded.textContent = "0%";
|
||||
};
|
||||
|
||||
|
||||
@@ -69,8 +99,8 @@ InputWaiter.prototype.setInputInfo = function(length, lines) {
|
||||
let width = length.toString().length;
|
||||
width = width < 2 ? 2 : width;
|
||||
|
||||
const lengthStr = Utils.pad(length.toString(), width, " ").replace(/ /g, " ");
|
||||
const linesStr = Utils.pad(lines.toString(), width, " ").replace(/ /g, " ");
|
||||
const lengthStr = length.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
const linesStr = lines.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
|
||||
document.getElementById("input-info").innerHTML = "length: " + lengthStr + "<br>lines: " + linesStr;
|
||||
};
|
||||
@@ -118,7 +148,7 @@ InputWaiter.prototype.inputDragover = function(e) {
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
e.target.classList.add("dropping-file");
|
||||
e.target.closest("#input-text,#input-file").classList.add("dropping-file");
|
||||
};
|
||||
|
||||
|
||||
@@ -131,7 +161,8 @@ InputWaiter.prototype.inputDragover = function(e) {
|
||||
InputWaiter.prototype.inputDragleave = function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
e.target.classList.remove("dropping-file");
|
||||
document.getElementById("input-text").classList.remove("dropping-file");
|
||||
document.getElementById("input-file").classList.remove("dropping-file");
|
||||
};
|
||||
|
||||
|
||||
@@ -149,55 +180,57 @@ InputWaiter.prototype.inputDrop = function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
const el = e.target;
|
||||
const file = e.dataTransfer.files[0];
|
||||
const text = e.dataTransfer.getData("Text");
|
||||
const reader = new FileReader();
|
||||
let inputCharcode = "";
|
||||
let offset = 0;
|
||||
const CHUNK_SIZE = 20480; // 20KB
|
||||
|
||||
const setInput = function() {
|
||||
const recipeConfig = this.app.getRecipeConfig();
|
||||
if (!recipeConfig[0] || recipeConfig[0].op !== "From Hex") {
|
||||
recipeConfig.unshift({op: "From Hex", args: ["Space"]});
|
||||
this.app.setRecipeConfig(recipeConfig);
|
||||
}
|
||||
document.getElementById("input-text").classList.remove("dropping-file");
|
||||
document.getElementById("input-file").classList.remove("dropping-file");
|
||||
|
||||
this.set(inputCharcode);
|
||||
|
||||
el.classList.remove("loadingFile");
|
||||
}.bind(this);
|
||||
|
||||
const seek = function() {
|
||||
if (offset >= file.size) {
|
||||
setInput();
|
||||
return;
|
||||
}
|
||||
el.value = "Processing... " + Math.round(offset / file.size * 100) + "%";
|
||||
const slice = file.slice(offset, offset + CHUNK_SIZE);
|
||||
reader.readAsArrayBuffer(slice);
|
||||
};
|
||||
|
||||
reader.onload = function(e) {
|
||||
const data = new Uint8Array(reader.result);
|
||||
inputCharcode += Utils.toHexFast(data);
|
||||
offset += CHUNK_SIZE;
|
||||
seek();
|
||||
};
|
||||
|
||||
|
||||
el.classList.remove("dropping-file");
|
||||
if (text) {
|
||||
this.closeFile();
|
||||
this.set(text);
|
||||
return;
|
||||
}
|
||||
|
||||
if (file) {
|
||||
el.classList.add("loadingFile");
|
||||
seek();
|
||||
} else if (text) {
|
||||
this.set(text);
|
||||
this.closeFile();
|
||||
this.loaderWorker = new LoaderWorker();
|
||||
this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this));
|
||||
this.loaderWorker.postMessage({"file": file});
|
||||
this.set(file);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for messages sent back by the LoaderWorker.
|
||||
*
|
||||
* @param {MessageEvent} e
|
||||
*/
|
||||
InputWaiter.prototype.handleLoaderMessage = function(e) {
|
||||
const r = e.data;
|
||||
if (r.hasOwnProperty("progress")) {
|
||||
const fileLoaded = document.getElementById("input-file-loaded");
|
||||
fileLoaded.textContent = r.progress + "%";
|
||||
}
|
||||
|
||||
if (r.hasOwnProperty("fileBuffer")) {
|
||||
this.fileBuffer = r.fileBuffer;
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for file close events.
|
||||
*/
|
||||
InputWaiter.prototype.closeFile = function() {
|
||||
if (this.loaderWorker) this.loaderWorker.terminate();
|
||||
this.fileBuffer = null;
|
||||
document.getElementById("input-file").style.display = "none";
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for clear IO events.
|
||||
* Resets the input, output and info areas.
|
||||
@@ -205,6 +238,8 @@ InputWaiter.prototype.inputDrop = function(e) {
|
||||
* @fires Manager#statechange
|
||||
*/
|
||||
InputWaiter.prototype.clearIoClick = function() {
|
||||
this.closeFile();
|
||||
this.manager.output.closeFile();
|
||||
this.manager.highlighter.removeHighlights();
|
||||
document.getElementById("input-text").value = "";
|
||||
document.getElementById("output-text").value = "";
|
||||
|
||||
50
src/web/LoaderWorker.js
Normal file
50
src/web/LoaderWorker.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Web Worker to load large amounts of data without locking up the UI.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Respond to message from parent thread.
|
||||
*/
|
||||
self.addEventListener("message", function(e) {
|
||||
const r = e.data;
|
||||
if (r.hasOwnProperty("file")) {
|
||||
self.loadFile(r.file);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Loads a file object into an ArrayBuffer, then transfers it back to the parent thread.
|
||||
*
|
||||
* @param {File} file
|
||||
*/
|
||||
self.loadFile = function(file) {
|
||||
const reader = new FileReader();
|
||||
let data = new Uint8Array(file.size);
|
||||
let offset = 0;
|
||||
const CHUNK_SIZE = 10485760; // 10MiB
|
||||
|
||||
const seek = function() {
|
||||
if (offset >= file.size) {
|
||||
self.postMessage({"progress": 100});
|
||||
self.postMessage({"fileBuffer": data.buffer}, [data.buffer]);
|
||||
return;
|
||||
}
|
||||
self.postMessage({"progress": Math.round(offset / file.size * 100)});
|
||||
const slice = file.slice(offset, offset + CHUNK_SIZE);
|
||||
reader.readAsArrayBuffer(slice);
|
||||
};
|
||||
|
||||
reader.onload = function(e) {
|
||||
data.set(new Uint8Array(reader.result), offset);
|
||||
offset += CHUNK_SIZE;
|
||||
seek();
|
||||
};
|
||||
|
||||
seek();
|
||||
};
|
||||
@@ -135,13 +135,14 @@ Manager.prototype.initialiseEventListeners = function() {
|
||||
this.addMultiEventListener("#input-text", "keyup paste", this.input.inputChange, this.input);
|
||||
document.getElementById("reset-layout").addEventListener("click", this.app.resetLayout.bind(this.app));
|
||||
document.getElementById("clr-io").addEventListener("click", this.input.clearIoClick.bind(this.input));
|
||||
document.getElementById("input-text").addEventListener("dragover", this.input.inputDragover.bind(this.input));
|
||||
document.getElementById("input-text").addEventListener("dragleave", this.input.inputDragleave.bind(this.input));
|
||||
document.getElementById("input-text").addEventListener("drop", this.input.inputDrop.bind(this.input));
|
||||
this.addListeners("#input-text,#input-file", "dragover", this.input.inputDragover, this.input);
|
||||
this.addListeners("#input-text,#input-file", "dragleave", this.input.inputDragleave, this.input);
|
||||
this.addListeners("#input-text,#input-file", "drop", this.input.inputDrop, this.input);
|
||||
document.getElementById("input-text").addEventListener("scroll", this.highlighter.inputScroll.bind(this.highlighter));
|
||||
document.getElementById("input-text").addEventListener("mouseup", this.highlighter.inputMouseup.bind(this.highlighter));
|
||||
document.getElementById("input-text").addEventListener("mousemove", this.highlighter.inputMousemove.bind(this.highlighter));
|
||||
this.addMultiEventListener("#input-text", "mousedown dblclick select", this.highlighter.inputMousedown, this.highlighter);
|
||||
document.querySelector("#input-file .close").addEventListener("click", this.input.clearIoClick.bind(this.input));
|
||||
|
||||
// Output
|
||||
document.getElementById("save-to-file").addEventListener("click", this.output.saveClick.bind(this.output));
|
||||
@@ -157,6 +158,9 @@ Manager.prototype.initialiseEventListeners = function() {
|
||||
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(".file-switch", "click", this.output.fileSwitch, this.output);
|
||||
this.addDynamicListener("#output-file-download", "click", this.output.downloadFile, this.output);
|
||||
this.addDynamicListener("#output-file-slice", "click", this.output.displayFileSlice, this.output);
|
||||
document.getElementById("show-file-overlay").addEventListener("click", this.output.showFileOverlayClick.bind(this.output));
|
||||
|
||||
// Options
|
||||
document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Utils from "../core/Utils.js";
|
||||
import FileSaver from "file-saver";
|
||||
|
||||
|
||||
/**
|
||||
@@ -15,6 +16,9 @@ import Utils from "../core/Utils.js";
|
||||
const OutputWaiter = function(app, manager) {
|
||||
this.app = app;
|
||||
this.manager = manager;
|
||||
|
||||
this.dishBuffer = null;
|
||||
this.dishStr = null;
|
||||
};
|
||||
|
||||
|
||||
@@ -31,47 +35,150 @@ OutputWaiter.prototype.get = function() {
|
||||
/**
|
||||
* Sets the output in the output textarea.
|
||||
*
|
||||
* @param {string} dataStr - The output string/HTML
|
||||
* @param {string|ArrayBuffer} data - The output string/HTML/ArrayBuffer
|
||||
* @param {string} type - The data type of the output
|
||||
* @param {number} duration - The length of time (ms) it took to generate the output
|
||||
* @param {boolean} [preserveBuffer=false] - Whether to preserve the dishBuffer
|
||||
*/
|
||||
OutputWaiter.prototype.set = function(dataStr, type, duration) {
|
||||
OutputWaiter.prototype.set = function(data, type, duration, preserveBuffer) {
|
||||
const outputText = document.getElementById("output-text");
|
||||
const outputHtml = document.getElementById("output-html");
|
||||
const outputFile = document.getElementById("output-file");
|
||||
const outputHighlighter = document.getElementById("output-highlighter");
|
||||
const inputHighlighter = document.getElementById("input-highlighter");
|
||||
let scriptElements, lines, length;
|
||||
|
||||
if (type === "html") {
|
||||
outputText.style.display = "none";
|
||||
outputHtml.style.display = "block";
|
||||
outputHighlighter.display = "none";
|
||||
inputHighlighter.display = "none";
|
||||
if (!preserveBuffer) {
|
||||
this.closeFile();
|
||||
document.getElementById("show-file-overlay").style.display = "none";
|
||||
}
|
||||
|
||||
outputText.value = "";
|
||||
outputHtml.innerHTML = dataStr;
|
||||
switch (type) {
|
||||
case "html":
|
||||
outputText.style.display = "none";
|
||||
outputHtml.style.display = "block";
|
||||
outputFile.style.display = "none";
|
||||
outputHighlighter.display = "none";
|
||||
inputHighlighter.display = "none";
|
||||
|
||||
// Execute script sections
|
||||
const scriptElements = outputHtml.querySelectorAll("script");
|
||||
for (let i = 0; i < scriptElements.length; i++) {
|
||||
try {
|
||||
eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
outputText.value = "";
|
||||
outputHtml.innerHTML = data;
|
||||
this.dishStr = Utils.stripHtmlTags(data, true);
|
||||
length = data.length;
|
||||
lines = this.dishStr.count("\n") + 1;
|
||||
|
||||
// Execute script sections
|
||||
scriptElements = outputHtml.querySelectorAll("script");
|
||||
for (let i = 0; i < scriptElements.length; i++) {
|
||||
try {
|
||||
eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
outputText.style.display = "block";
|
||||
outputHtml.style.display = "none";
|
||||
outputHighlighter.display = "block";
|
||||
inputHighlighter.display = "block";
|
||||
break;
|
||||
case "ArrayBuffer":
|
||||
outputText.style.display = "block";
|
||||
outputHtml.style.display = "none";
|
||||
outputHighlighter.display = "none";
|
||||
inputHighlighter.display = "none";
|
||||
|
||||
outputText.value = Utils.printable(dataStr, true);
|
||||
outputHtml.innerHTML = "";
|
||||
outputText.value = "";
|
||||
outputHtml.innerHTML = "";
|
||||
this.dishStr = "";
|
||||
length = data.byteLength;
|
||||
|
||||
this.setFile(data);
|
||||
break;
|
||||
case "string":
|
||||
default:
|
||||
outputText.style.display = "block";
|
||||
outputHtml.style.display = "none";
|
||||
outputFile.style.display = "none";
|
||||
outputHighlighter.display = "block";
|
||||
inputHighlighter.display = "block";
|
||||
|
||||
outputText.value = Utils.printable(data, true);
|
||||
outputHtml.innerHTML = "";
|
||||
|
||||
lines = data.count("\n") + 1;
|
||||
length = data.length;
|
||||
this.dishStr = data;
|
||||
break;
|
||||
}
|
||||
|
||||
this.manager.highlighter.removeHighlights();
|
||||
const lines = dataStr.count("\n") + 1;
|
||||
this.setOutputInfo(dataStr.length, lines, duration);
|
||||
this.setOutputInfo(length, lines, duration);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shows file details.
|
||||
*
|
||||
* @param {ArrayBuffer} buf
|
||||
*/
|
||||
OutputWaiter.prototype.setFile = function(buf) {
|
||||
this.dishBuffer = buf;
|
||||
const file = new File([buf], "output.dat");
|
||||
|
||||
// Display file overlay in output area with details
|
||||
const fileOverlay = document.getElementById("output-file"),
|
||||
fileSize = document.getElementById("output-file-size");
|
||||
|
||||
fileOverlay.style.display = "block";
|
||||
fileSize.textContent = file.size.toLocaleString() + " bytes";
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes the output file and nulls its memory.
|
||||
*/
|
||||
OutputWaiter.prototype.closeFile = function() {
|
||||
this.dishBuffer = null;
|
||||
document.getElementById("output-file").style.display = "none";
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for file download events.
|
||||
*/
|
||||
OutputWaiter.prototype.downloadFile = function() {
|
||||
this.filename = window.prompt("Please enter a filename:", this.filename || "download.dat");
|
||||
const file = new File([this.dishBuffer], this.filename);
|
||||
|
||||
if (this.filename) FileSaver.saveAs(file, this.filename, false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for file slice display events.
|
||||
*/
|
||||
OutputWaiter.prototype.displayFileSlice = function() {
|
||||
const startTime = new Date().getTime(),
|
||||
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),
|
||||
str = Utils.arrayBufferToStr(this.dishBuffer.slice(sliceFrom, sliceTo));
|
||||
|
||||
showFileOverlay.style.display = "block";
|
||||
this.set(str, "string", new Date().getTime() - startTime, true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for show file overlay events.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
OutputWaiter.prototype.showFileOverlayClick = function(e) {
|
||||
const outputFile = document.getElementById("output-file"),
|
||||
showFileOverlay = e.target;
|
||||
|
||||
outputFile.style.display = "block";
|
||||
showFileOverlay.style.display = "none";
|
||||
this.setOutputInfo(this.dishBuffer.byteLength, null, 0);
|
||||
};
|
||||
|
||||
|
||||
@@ -86,13 +193,17 @@ OutputWaiter.prototype.setOutputInfo = function(length, lines, duration) {
|
||||
let width = length.toString().length;
|
||||
width = width < 4 ? 4 : width;
|
||||
|
||||
const lengthStr = Utils.pad(length.toString(), width, " ").replace(/ /g, " ");
|
||||
const linesStr = Utils.pad(lines.toString(), width, " ").replace(/ /g, " ");
|
||||
const timeStr = Utils.pad(duration.toString() + "ms", width, " ").replace(/ /g, " ");
|
||||
const lengthStr = length.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
const timeStr = (duration.toString() + "ms").padStart(width, " ").replace(/ /g, " ");
|
||||
|
||||
document.getElementById("output-info").innerHTML = "time: " + timeStr +
|
||||
"<br>length: " + lengthStr +
|
||||
"<br>lines: " + linesStr;
|
||||
let msg = "time: " + timeStr + "<br>length: " + lengthStr;
|
||||
|
||||
if (typeof lines === "number") {
|
||||
const linesStr = lines.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
msg += "<br>lines: " + linesStr;
|
||||
}
|
||||
|
||||
document.getElementById("output-info").innerHTML = msg;
|
||||
document.getElementById("input-selection-info").innerHTML = "";
|
||||
document.getElementById("output-selection-info").innerHTML = "";
|
||||
};
|
||||
@@ -129,24 +240,13 @@ OutputWaiter.prototype.adjustWidth = function() {
|
||||
|
||||
/**
|
||||
* Handler for save click events.
|
||||
* Saves the current output to a file, downloaded as a URL octet stream.
|
||||
* Saves the current output to a file.
|
||||
*/
|
||||
OutputWaiter.prototype.saveClick = function() {
|
||||
const data = Utils.toBase64(this.app.dishStr);
|
||||
const filename = window.prompt("Please enter a filename:", "download.dat");
|
||||
|
||||
if (filename) {
|
||||
const el = document.createElement("a");
|
||||
el.setAttribute("href", "data:application/octet-stream;base64;charset=utf-8," + data);
|
||||
el.setAttribute("download", filename);
|
||||
|
||||
// Firefox requires that the element be added to the DOM before it can be clicked
|
||||
el.style.display = "none";
|
||||
document.body.appendChild(el);
|
||||
|
||||
el.click();
|
||||
el.remove();
|
||||
if (!this.dishBuffer) {
|
||||
this.dishBuffer = new Uint8Array(Utils.strToCharcode(this.dishStr)).buffer;
|
||||
}
|
||||
this.downloadFile();
|
||||
};
|
||||
|
||||
|
||||
@@ -165,14 +265,14 @@ OutputWaiter.prototype.copyClick = function() {
|
||||
textarea.style.height = 0;
|
||||
textarea.style.border = "none";
|
||||
|
||||
textarea.value = this.app.dishStr;
|
||||
textarea.value = this.dishStr;
|
||||
document.body.appendChild(textarea);
|
||||
|
||||
// Select and copy the contents of this textarea
|
||||
let success = false;
|
||||
try {
|
||||
textarea.select();
|
||||
success = document.execCommand("copy");
|
||||
success = this.dishStr && document.execCommand("copy");
|
||||
} catch (err) {
|
||||
success = false;
|
||||
}
|
||||
@@ -195,7 +295,17 @@ OutputWaiter.prototype.copyClick = function() {
|
||||
OutputWaiter.prototype.switchClick = function() {
|
||||
this.switchOrigData = this.manager.input.get();
|
||||
document.getElementById("undo-switch").disabled = false;
|
||||
this.app.setInput(this.app.dishStr);
|
||||
if (this.dishBuffer) {
|
||||
this.manager.input.setFile(new File([this.dishBuffer], "output.dat"));
|
||||
this.manager.input.handleLoaderMessage({
|
||||
data: {
|
||||
progress: 100,
|
||||
fileBuffer: this.dishBuffer
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.app.setInput(this.dishStr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -210,7 +320,7 @@ OutputWaiter.prototype.undoSwitchClick = function() {
|
||||
|
||||
/**
|
||||
* Handler for file switch click events.
|
||||
* Moves a files data for items created via Utils.displayFilesAsHTML to the input.
|
||||
* Moves a file's data for items created via Utils.displayFilesAsHTML to the input.
|
||||
*/
|
||||
OutputWaiter.prototype.fileSwitch = function(e) {
|
||||
e.preventDefault();
|
||||
@@ -282,4 +392,14 @@ OutputWaiter.prototype.setStatusMsg = function(msg) {
|
||||
el.textContent = msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the output contains carriage returns
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
OutputWaiter.prototype.containsCR = function() {
|
||||
return this.dishStr.indexOf("\r") >= 0;
|
||||
};
|
||||
|
||||
export default OutputWaiter;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Utils from "../core/Utils.js";
|
||||
import ChefWorker from "worker-loader?inline&fallback=false!../core/ChefWorker.js";
|
||||
|
||||
/**
|
||||
@@ -111,7 +110,6 @@ WorkerWaiter.prototype.bakingComplete = function(response) {
|
||||
this.app.handleError(response.error);
|
||||
}
|
||||
|
||||
this.app.dishStr = response.type === "html" ? Utils.stripHtmlTags(response.result, true) : response.result;
|
||||
this.app.progress = response.progress;
|
||||
this.manager.recipe.updateBreakpointIndicator(response.progress);
|
||||
this.manager.output.set(response.result, response.type, response.duration);
|
||||
|
||||
@@ -181,6 +181,20 @@
|
||||
<div class="textarea-wrapper no-select">
|
||||
<div id="input-highlighter" class="no-select"></div>
|
||||
<textarea id="input-text"></textarea>
|
||||
<div id="input-file">
|
||||
<div style="position: relative; height: 100%;">
|
||||
<div class="card">
|
||||
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon"/>
|
||||
<div class="card-body">
|
||||
<button type="button" class="close" id="input-file-close">×</button>
|
||||
Name: <span id="input-file-name"></span><br>
|
||||
Size: <span id="input-file-size"></span><br>
|
||||
Type: <span id="input-file-type"></span><br>
|
||||
Loaded: <span id="input-file-loaded"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -202,6 +216,26 @@
|
||||
<div id="output-highlighter" class="no-select"></div>
|
||||
<div id="output-html"></div>
|
||||
<textarea id="output-text" readonly="readonly"></textarea>
|
||||
<img id="show-file-overlay" aria-hidden="true" src="<%- require('../static/images/file-32x32.png') %>" alt="Show file overlay" title="Show file overlay"/>
|
||||
<div id="output-file">
|
||||
<div style="position: relative; height: 100%;">
|
||||
<div class="card">
|
||||
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" 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">Download</button>
|
||||
<div class="input-group">
|
||||
<span class="input-group-btn">
|
||||
<button id="output-file-slice" type="button" class="btn btn-default" title="View slice">🔍</button>
|
||||
</span>
|
||||
<input type="number" class="form-control" id="output-file-slice-from" placeholder="From" value="0" step="1024" min="0">
|
||||
<div class="input-group-addon">to</div>
|
||||
<input type="number" class="form-control" id="output-file-slice-to" placeholder="To" value="1024" step="1024" min="0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="output-loader">
|
||||
<div class="loader"></div>
|
||||
<div class="loading-msg"></div>
|
||||
@@ -308,31 +342,35 @@
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="checkbox" option="updateUrl" id="updateUrl" checked />
|
||||
<label for="updateUrl"> Update the URL when the input or recipe changes </label>
|
||||
<label for="updateUrl"> Update the URL when the input or recipe changes</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="checkbox" option="showHighlighter" id="showHighlighter" checked />
|
||||
<label for="showHighlighter"> Highlight selected bytes in output and input (when possible) </label>
|
||||
<label for="showHighlighter"> Highlight selected bytes in output and input (when possible)</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="checkbox" option="treatAsUtf8" id="treatAsUtf8" checked />
|
||||
<label for="treatAsUtf8"> Treat output as UTF-8 if possible </label>
|
||||
<label for="treatAsUtf8"> Treat output as UTF-8 if possible</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="checkbox" option="wordWrap" id="wordWrap" checked />
|
||||
<label for="wordWrap"> Word wrap the input and output </label>
|
||||
<label for="wordWrap"> Word wrap the input and output</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="checkbox" option="showErrors" id="showErrors" checked />
|
||||
<label for="showErrors"> Operation error reporting (recommended) </label>
|
||||
<label for="showErrors"> Operation error reporting (recommended)</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="checkbox" option="useMetaKey" id="useMetaKey" />
|
||||
<label for="useMetaKey"> Use meta key for keybindings (Windows ⊞/Command ⌘) </label>
|
||||
<label for="useMetaKey"> Use meta key for keybindings (Windows ⊞/Command ⌘)</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="number" option="errorTimeout" id="errorTimeout" />
|
||||
<label for="errorTimeout"> Operation error timeout in ms (0 for never) </label>
|
||||
<label for="errorTimeout"> Operation error timeout in ms (0 for never)</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="number" option="outputFileThreshold" id="outputFileThreshold" />
|
||||
<label for="outputFileThreshold"> Size threshold for treating the output as a file (KiB)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@@ -438,8 +476,9 @@
|
||||
</a>
|
||||
</blockquote>
|
||||
<div class="collapse" id="faq-load-files">
|
||||
<p>Yes! Just drag your file over the input box and drop it. The contents of the file will be converted into hexadecimal and the 'From Hex' operation will be added to the beginning of the recipe (if it's not already there). This is so that special characters like carriage returns aren't removed by your browser.</p>
|
||||
<p>Please note that loading large files is likely to cause a crash. There's not a lot that can be done about this - browsers just aren't very good at handling and displaying large amounts of data.</p>
|
||||
<p>Yes! Just drag your file over the input box and drop it.</p>
|
||||
<p>CyberChef can handle files up to around 500MB (depending on your browser), however some of the operations may take a very long time to run over this much data.</p>
|
||||
<p>If the output is larger than a certain threshold (default 1MiB), it will be presented to you as a file available for download. Slices of the file can be viewed in the output if you need to inspect them.</p>
|
||||
</div>
|
||||
<blockquote>
|
||||
<a data-toggle="collapse" data-target="#faq-fork">
|
||||
@@ -482,7 +521,7 @@
|
||||
|
||||
|
||||
<br>
|
||||
<p>There are around 150 useful operations in CyberChef for anyone working on anything vaguely Internet-related, whether you just want to convert a timestamp to a different format, decompress gzipped data, create a SHA3 hash, or parse an X.509 certificate to find out who issued it.</p>
|
||||
<p>There are around 200 useful operations in CyberChef for anyone working on anything vaguely Internet-related, whether you just want to convert a timestamp to a different format, decompress gzipped data, create a SHA3 hash, or parse an X.509 certificate to find out who issued it.</p>
|
||||
<p>It’s the Cyber Swiss Army Knife.</p>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="keybindings" style="padding: 20px;">
|
||||
|
||||
@@ -38,15 +38,16 @@ function main() {
|
||||
];
|
||||
|
||||
const defaultOptions = {
|
||||
updateUrl: true,
|
||||
showHighlighter: true,
|
||||
treatAsUtf8: true,
|
||||
wordWrap: true,
|
||||
showErrors: true,
|
||||
errorTimeout: 4000,
|
||||
attemptHighlight: true,
|
||||
theme: "classic",
|
||||
useMetaKey: false
|
||||
updateUrl: true,
|
||||
showHighlighter: true,
|
||||
treatAsUtf8: true,
|
||||
wordWrap: true,
|
||||
showErrors: true,
|
||||
errorTimeout: 4000,
|
||||
attemptHighlight: true,
|
||||
theme: "classic",
|
||||
useMetaKey: false,
|
||||
outputFileThreshold: 1024
|
||||
};
|
||||
|
||||
document.removeEventListener("DOMContentLoaded", main, false);
|
||||
|
||||
BIN
src/web/static/images/file-128x128.png
Normal file
BIN
src/web/static/images/file-128x128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
src/web/static/images/file-32x32.png
Normal file
BIN
src/web/static/images/file-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -28,3 +28,56 @@
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
|
||||
transition: 0.3s;
|
||||
width: 400px;
|
||||
height: 150px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-family: var(--primary-font-family);
|
||||
color: var(--primary-font-colour);
|
||||
line-height: 30px;
|
||||
background-color: var(--primary-background-colour);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.card>img {
|
||||
float: left;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
margin-left: 10px;
|
||||
margin-top: 11px;
|
||||
}
|
||||
|
||||
.card-body .close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
float: left;
|
||||
padding: 16px;
|
||||
width: 250px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.card-body>.btn {
|
||||
margin-bottom: 15px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.card input[type=number] {
|
||||
padding-right: 6px;
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
}
|
||||
|
||||
.textarea-wrapper textarea,
|
||||
.textarea-wrapper div {
|
||||
.textarea-wrapper>div {
|
||||
font-family: var(--fixed-width-font-family);
|
||||
font-size: var(--fixed-width-font-size);
|
||||
color: var(--fixed-width-font-colour);
|
||||
@@ -77,6 +77,25 @@
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
#input-file,
|
||||
#output-file {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--title-background-colour);
|
||||
display: none;
|
||||
}
|
||||
|
||||
#show-file-overlay {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 15px;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.io-btn-group {
|
||||
float: right;
|
||||
margin-top: -4px;
|
||||
@@ -92,6 +111,8 @@
|
||||
font-family: var(--fixed-width-font-family);
|
||||
font-weight: normal;
|
||||
font-size: 8pt;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#input-info {
|
||||
|
||||
@@ -64,7 +64,8 @@ a:focus {
|
||||
.alert,
|
||||
.modal-content,
|
||||
.tooltip-inner,
|
||||
.dropdown-menu {
|
||||
.dropdown-menu,
|
||||
.input-group-addon {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
@@ -187,6 +188,15 @@ optgroup {
|
||||
color: var(--primary-font-colour);
|
||||
}
|
||||
|
||||
.input-group-addon:not(:first-child):not(:last-child) {
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.input-group-btn:first-child>.btn {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Bootstrap-switch */
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ import "./tests/operations/Image.js";
|
||||
import "./tests/operations/MorseCode.js";
|
||||
import "./tests/operations/MS.js";
|
||||
import "./tests/operations/PHP.js";
|
||||
import "./tests/operations/NetBIOS.js";
|
||||
import "./tests/operations/OTP.js";
|
||||
import "./tests/operations/StrUtils.js";
|
||||
import "./tests/operations/SeqUtils.js";
|
||||
|
||||
|
||||
@@ -310,4 +310,26 @@ TestRegister.addTests([
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "CSS selector",
|
||||
input: '<div id="test">\n<p class="a">hello</p>\n<p>world</p>\n<p class="a">again</p>\n</div>',
|
||||
expectedOutput: '<p class="a">hello</p>\n<p class="a">again</p>',
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "CSS selector",
|
||||
"args": ["#test p.a", "\\n"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "XPath expression",
|
||||
input: '<div id="test">\n<p class="a">hello</p>\n<p>world</p>\n<p class="a">again</p>\n</div>',
|
||||
expectedOutput: '<p class="a">hello</p>\n<p class="a">again</p>',
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XPath expression",
|
||||
"args": ["/div/p[@class=\"a\"]", "\\n"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
34
test/tests/operations/NetBIOS.js
Normal file
34
test/tests/operations/NetBIOS.js
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* NetBIOS tests.
|
||||
*
|
||||
* @author bwhitn [brian.m.whitney@outlook.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister.js";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Encode NetBIOS name",
|
||||
input: "The NetBIOS name",
|
||||
expectedOutput: "FEGIGFCAEOGFHEECEJEPFDCAGOGBGNGF",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Encode NetBIOS Name",
|
||||
args: [65],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Decode NetBIOS Name",
|
||||
input: "FEGIGFCAEOGFHEECEJEPFDCAGOGBGNGF",
|
||||
expectedOutput: "The NetBIOS name",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Decode NetBIOS Name",
|
||||
args: [65],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
23
test/tests/operations/OTP.js
Normal file
23
test/tests/operations/OTP.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* OTP HOTP tests.
|
||||
*
|
||||
* @author bwhitn [brian.m.whitney@outlook.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister.js";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Generate HOTP",
|
||||
input: "12345678901234567890",
|
||||
expectedOutput: "URI: otpauth://hotp/OTPAuthentication?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ\n\nPassword: 755224",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Generate HOTP",
|
||||
args: ["", 32, 6, 0],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
Reference in New Issue
Block a user