mirror of
https://github.com/gchq/CyberChef.git
synced 2024-11-02 14:11:02 +01:00
Merge branch 'esm' of https://github.com/gchq/CyberChef into ip-convert
This commit is contained in:
commit
ea36687205
@ -22,11 +22,11 @@ module.exports = function (grunt) {
|
||||
// Tasks
|
||||
grunt.registerTask("dev",
|
||||
"A persistent task which creates a development build whenever source files are modified.",
|
||||
["clean:dev", "concurrent:dev"]);
|
||||
["clean:dev", "exec:generateConfig", "concurrent:dev"]);
|
||||
|
||||
grunt.registerTask("node",
|
||||
"Compiles CyberChef into a single NodeJS module.",
|
||||
["clean:node", "clean:config", "webpack:node", "chmod:build"]);
|
||||
["clean:node", "clean:config", "exec:generateConfig", "webpack:node", "chmod:build"]);
|
||||
|
||||
grunt.registerTask("test",
|
||||
"A task which runs all the tests in test/tests.",
|
||||
@ -38,7 +38,7 @@ module.exports = function (grunt) {
|
||||
|
||||
grunt.registerTask("prod",
|
||||
"Creates a production-ready build. Use the --msg flag to add a compile message.",
|
||||
["eslint", "clean:prod", "webpack:web", "inline", "chmod"]);
|
||||
["eslint", "clean:prod", "exec:generateConfig", "webpack:web", "inline", "chmod"]);
|
||||
|
||||
grunt.registerTask("default",
|
||||
"Lints the code base",
|
||||
@ -46,7 +46,7 @@ module.exports = function (grunt) {
|
||||
|
||||
grunt.registerTask("inline",
|
||||
"Compiles a production build of CyberChef into a single, portable web page.",
|
||||
["webpack:webInline", "runInliner", "clean:inlineScripts"]);
|
||||
["exec:generateConfig", "webpack:webInline", "runInliner", "clean:inlineScripts"]);
|
||||
|
||||
|
||||
grunt.registerTask("runInliner", runInliner);
|
||||
|
4137
package-lock.json
generated
4137
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -65,7 +65,6 @@
|
||||
"webpack": "^4.6.0",
|
||||
"webpack-dev-server": "^3.1.3",
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"webpack-synchronizable-shell-plugin": "0.0.7",
|
||||
"worker-loader": "^1.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -60,6 +60,12 @@ class Chef {
|
||||
recipe.setBreakpoint(progress + 1, true);
|
||||
}
|
||||
|
||||
// If the previously run operation presented a different value to its
|
||||
// normal output, we need to recalculate it.
|
||||
if (recipe.lastOpPresented(progress)) {
|
||||
progress = 0;
|
||||
}
|
||||
|
||||
// If stepping with flow control, we have to start from the beginning
|
||||
// but still want to skip all previous breakpoints
|
||||
if (progress > 0 && containsFc) {
|
||||
|
@ -81,9 +81,10 @@ class Operation {
|
||||
* this behaviour.
|
||||
*
|
||||
* @param {*} data - The result of the run() function
|
||||
* @param {Object[]} args - The operation's arguments
|
||||
* @returns {*} - A human-readable version of the data
|
||||
*/
|
||||
present(data) {
|
||||
present(data, args) {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,10 @@ class Recipe {
|
||||
}
|
||||
} catch (err) {
|
||||
// Return expected errors as output
|
||||
if (err instanceof OperationError) {
|
||||
if (err instanceof OperationError ||
|
||||
(err.type && err.type === "OperationError")) {
|
||||
// Cannot rely on `err instanceof OperationError` here as extending
|
||||
// native types is not fully supported yet.
|
||||
dish.set(err.message, "string");
|
||||
return i;
|
||||
} else {
|
||||
@ -209,7 +212,10 @@ class Recipe {
|
||||
async present(dish) {
|
||||
if (!this.lastRunOp) return;
|
||||
|
||||
const output = await this.lastRunOp.present(await dish.get(this.lastRunOp.outputType));
|
||||
const output = await this.lastRunOp.present(
|
||||
await dish.get(this.lastRunOp.outputType),
|
||||
this.lastRunOp.ingValues
|
||||
);
|
||||
dish.set(output, this.lastRunOp.presentType);
|
||||
}
|
||||
|
||||
@ -267,6 +273,18 @@ class Recipe {
|
||||
return highlights;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether the previous operation has a different presentation type to its normal output.
|
||||
*
|
||||
* @param {number} progress
|
||||
* @returns {boolean}
|
||||
*/
|
||||
lastOpPresented(progress) {
|
||||
if (progress < 1) return false;
|
||||
return this.opList[progress-1].presentType !== this.opList[progress-1].outputType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Recipe;
|
||||
|
@ -15,6 +15,8 @@ class OperationError extends Error {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this.type = "OperationError";
|
||||
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, OperationError);
|
||||
}
|
||||
|
48
src/core/lib/BCD.mjs
Executable file
48
src/core/lib/BCD.mjs
Executable file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Binary Code Decimal resources.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* BCD encoding schemes.
|
||||
*/
|
||||
export const ENCODING_SCHEME = [
|
||||
"8 4 2 1",
|
||||
"7 4 2 1",
|
||||
"4 2 2 1",
|
||||
"2 4 2 1",
|
||||
"8 4 -2 -1",
|
||||
"Excess-3",
|
||||
"IBM 8 4 2 1",
|
||||
];
|
||||
|
||||
/**
|
||||
* Lookup table for the binary value of each digit representation.
|
||||
*
|
||||
* I wrote a very nice algorithm to generate 8 4 2 1 encoding programatically,
|
||||
* but unfortunately it's much easier (if less elegant) to use lookup tables
|
||||
* when supporting multiple encoding schemes.
|
||||
*
|
||||
* "Practicality beats purity" - PEP 20
|
||||
*
|
||||
* In some schemes it is possible to represent the same value in multiple ways.
|
||||
* For instance, in 4 2 2 1 encoding, 0100 and 0010 both represent 2. Support
|
||||
* has not yet been added for this.
|
||||
*/
|
||||
export const ENCODING_LOOKUP = {
|
||||
"8 4 2 1": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||
"7 4 2 1": [0, 1, 2, 3, 4, 5, 6, 8, 9, 10],
|
||||
"4 2 2 1": [0, 1, 4, 5, 8, 9, 12, 13, 14, 15],
|
||||
"2 4 2 1": [0, 1, 2, 3, 4, 11, 12, 13, 14, 15],
|
||||
"8 4 -2 -1": [0, 7, 6, 5, 4, 11, 10, 9, 8, 15],
|
||||
"Excess-3": [3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
|
||||
"IBM 8 4 2 1": [10, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||
};
|
||||
|
||||
/**
|
||||
* BCD formats.
|
||||
*/
|
||||
export const FORMAT = ["Nibbles", "Bytes", "Raw"];
|
22
src/core/lib/Base58.mjs
Executable file
22
src/core/lib/Base58.mjs
Executable file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Base58 resources.
|
||||
*
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base58 alphabet options.
|
||||
*/
|
||||
export const ALPHABET_OPTIONS = [
|
||||
{
|
||||
name: "Bitcoin",
|
||||
value: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
||||
},
|
||||
{
|
||||
name: "Ripple",
|
||||
value: "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz",
|
||||
},
|
||||
];
|
204
src/core/lib/CanvasComponents.mjs
Executable file
204
src/core/lib/CanvasComponents.mjs
Executable file
@ -0,0 +1,204 @@
|
||||
/**
|
||||
* Various components for drawing diagrams on an HTML5 canvas.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Draws a line from one point to another
|
||||
*
|
||||
* @param ctx
|
||||
* @param startX
|
||||
* @param startY
|
||||
* @param endX
|
||||
* @param endY
|
||||
*/
|
||||
export function drawLine(ctx, startX, startY, endX, endY) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(startX, startY);
|
||||
ctx.lineTo(endX, endY);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a bar chart on the canvas.
|
||||
*
|
||||
* @param canvas
|
||||
* @param scores
|
||||
* @param xAxisLabel
|
||||
* @param yAxisLabel
|
||||
* @param numXLabels
|
||||
* @param numYLabels
|
||||
* @param fontSize
|
||||
*/
|
||||
export function drawBarChart(canvas, scores, xAxisLabel, yAxisLabel, numXLabels, numYLabels, fontSize) {
|
||||
fontSize = fontSize || 15;
|
||||
if (!numXLabels || numXLabels > Math.round(canvas.width / 50)) {
|
||||
numXLabels = Math.round(canvas.width / 50);
|
||||
}
|
||||
if (!numYLabels || numYLabels > Math.round(canvas.width / 50)) {
|
||||
numYLabels = Math.round(canvas.height / 50);
|
||||
}
|
||||
|
||||
// Graph properties
|
||||
const ctx = canvas.getContext("2d"),
|
||||
leftPadding = canvas.width * 0.08,
|
||||
rightPadding = canvas.width * 0.03,
|
||||
topPadding = canvas.height * 0.08,
|
||||
bottomPadding = canvas.height * 0.15,
|
||||
graphHeight = canvas.height - topPadding - bottomPadding,
|
||||
graphWidth = canvas.width - leftPadding - rightPadding,
|
||||
base = topPadding + graphHeight,
|
||||
ceil = topPadding;
|
||||
|
||||
ctx.font = fontSize + "px Arial";
|
||||
|
||||
// Draw axis
|
||||
ctx.lineWidth = "1.0";
|
||||
ctx.strokeStyle = "#444";
|
||||
drawLine(ctx, leftPadding, base, graphWidth + leftPadding, base); // x
|
||||
drawLine(ctx, leftPadding, base, leftPadding, ceil); // y
|
||||
|
||||
// Bar properties
|
||||
const barPadding = graphWidth * 0.003,
|
||||
barWidth = (graphWidth - (barPadding * scores.length)) / scores.length,
|
||||
max = Math.max.apply(Math, scores);
|
||||
let currX = leftPadding + barPadding;
|
||||
|
||||
// Draw bars
|
||||
ctx.fillStyle = "green";
|
||||
for (let i = 0; i < scores.length; i++) {
|
||||
const h = scores[i] / max * graphHeight;
|
||||
ctx.fillRect(currX, base - h, barWidth, h);
|
||||
currX += barWidth + barPadding;
|
||||
}
|
||||
|
||||
// Mark x axis
|
||||
ctx.fillStyle = "black";
|
||||
ctx.textAlign = "center";
|
||||
currX = leftPadding + barPadding;
|
||||
if (numXLabels >= scores.length) {
|
||||
// Mark every score
|
||||
for (let i = 0; i <= scores.length; i++) {
|
||||
ctx.fillText(i, currX, base + (bottomPadding * 0.3));
|
||||
currX += barWidth + barPadding;
|
||||
}
|
||||
} else {
|
||||
// Mark some scores
|
||||
for (let i = 0; i <= numXLabels; i++) {
|
||||
const val = Math.ceil((scores.length / numXLabels) * i);
|
||||
currX = (graphWidth / numXLabels) * i + leftPadding;
|
||||
ctx.fillText(val, currX, base + (bottomPadding * 0.3));
|
||||
}
|
||||
}
|
||||
|
||||
// Mark y axis
|
||||
ctx.textAlign = "right";
|
||||
let currY;
|
||||
if (numYLabels >= max) {
|
||||
// Mark every increment
|
||||
for (let i = 0; i <= max; i++) {
|
||||
currY = base - (i / max * graphHeight) + fontSize / 3;
|
||||
ctx.fillText(i, leftPadding * 0.8, currY);
|
||||
}
|
||||
} else {
|
||||
// Mark some increments
|
||||
for (let i = 0; i <= numYLabels; i++) {
|
||||
const val = Math.ceil((max / numYLabels) * i);
|
||||
currY = base - (val / max * graphHeight) + fontSize / 3;
|
||||
ctx.fillText(val, leftPadding * 0.8, currY);
|
||||
}
|
||||
}
|
||||
|
||||
// Label x axis
|
||||
if (xAxisLabel) {
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(xAxisLabel, graphWidth / 2 + leftPadding, base + bottomPadding * 0.8);
|
||||
}
|
||||
|
||||
// Label y axis
|
||||
if (yAxisLabel) {
|
||||
ctx.save();
|
||||
const x = leftPadding * 0.3,
|
||||
y = graphHeight / 2 + topPadding;
|
||||
ctx.translate(x, y);
|
||||
ctx.rotate(-Math.PI / 2);
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(yAxisLabel, 0, 0);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a scale bar on the canvas.
|
||||
*
|
||||
* @param canvas
|
||||
* @param score
|
||||
* @param max
|
||||
* @param markings
|
||||
*/
|
||||
export function drawScaleBar(canvas, score, max, markings) {
|
||||
// Bar properties
|
||||
const ctx = canvas.getContext("2d"),
|
||||
leftPadding = canvas.width * 0.01,
|
||||
rightPadding = canvas.width * 0.01,
|
||||
topPadding = canvas.height * 0.1,
|
||||
bottomPadding = canvas.height * 0.3,
|
||||
barHeight = canvas.height - topPadding - bottomPadding,
|
||||
barWidth = canvas.width - leftPadding - rightPadding;
|
||||
|
||||
// Scale properties
|
||||
const proportion = score / max;
|
||||
|
||||
// Draw bar outline
|
||||
ctx.strokeRect(leftPadding, topPadding, barWidth, barHeight);
|
||||
|
||||
// Shade in up to proportion
|
||||
const grad = ctx.createLinearGradient(leftPadding, 0, barWidth + leftPadding, 0);
|
||||
grad.addColorStop(0, "green");
|
||||
grad.addColorStop(0.5, "gold");
|
||||
grad.addColorStop(1, "red");
|
||||
ctx.fillStyle = grad;
|
||||
ctx.fillRect(leftPadding, topPadding, barWidth * proportion, barHeight);
|
||||
|
||||
// Add markings
|
||||
let x0, y0, x1, y1;
|
||||
ctx.fillStyle = "black";
|
||||
ctx.textAlign = "center";
|
||||
ctx.font = "13px Arial";
|
||||
for (let i = 0; i < markings.length; i++) {
|
||||
// Draw min line down
|
||||
x0 = barWidth / max * markings[i].min + leftPadding;
|
||||
y0 = topPadding + barHeight + (bottomPadding * 0.1);
|
||||
x1 = x0;
|
||||
y1 = topPadding + barHeight + (bottomPadding * 0.3);
|
||||
drawLine(ctx, x0, y0, x1, y1);
|
||||
|
||||
// Draw max line down
|
||||
x0 = barWidth / max * markings[i].max + leftPadding;
|
||||
x1 = x0;
|
||||
drawLine(ctx, x0, y0, x1, y1);
|
||||
|
||||
// Join min and max lines
|
||||
x0 = barWidth / max * markings[i].min + leftPadding;
|
||||
y0 = topPadding + barHeight + (bottomPadding * 0.3);
|
||||
x1 = barWidth / max * markings[i].max + leftPadding;
|
||||
y1 = y0;
|
||||
drawLine(ctx, x0, y0, x1, y1);
|
||||
|
||||
// Add label
|
||||
if (markings[i].max >= max * 0.9) {
|
||||
ctx.textAlign = "right";
|
||||
x0 = x1;
|
||||
} else if (markings[i].max <= max * 0.1) {
|
||||
ctx.textAlign = "left";
|
||||
} else {
|
||||
x0 = x0 + (x1 - x0) / 2;
|
||||
}
|
||||
y0 = topPadding + barHeight + (bottomPadding * 0.8);
|
||||
ctx.fillText(markings[i].label, x0, y0);
|
||||
}
|
||||
}
|
41
src/core/lib/Extract.mjs
Normal file
41
src/core/lib/Extract.mjs
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Identifier extraction functions
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Runs search operations across the input data using regular expressions.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {RegExp} searchRegex
|
||||
* @param {RegExp} removeRegex - A regular expression defining results to remove from the
|
||||
* final list
|
||||
* @param {boolean} includeTotal - Whether or not to include the total number of results
|
||||
* @returns {string}
|
||||
*/
|
||||
export function search (input, searchRegex, removeRegex, includeTotal) {
|
||||
let output = "",
|
||||
total = 0,
|
||||
match;
|
||||
|
||||
while ((match = searchRegex.exec(input))) {
|
||||
// Moves pointer when an empty string is matched (prevents infinite loop)
|
||||
if (match.index === searchRegex.lastIndex) {
|
||||
searchRegex.lastIndex++;
|
||||
}
|
||||
|
||||
if (removeRegex && removeRegex.test(match[0]))
|
||||
continue;
|
||||
total++;
|
||||
output += match[0] + "\n";
|
||||
}
|
||||
|
||||
if (includeTotal)
|
||||
output = "Total found: " + total + "\n\n" + output;
|
||||
|
||||
return output;
|
||||
}
|
116
src/core/lib/PGP.mjs
Normal file
116
src/core/lib/PGP.mjs
Normal file
@ -0,0 +1,116 @@
|
||||
/**
|
||||
* PGP functions.
|
||||
*
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @author Matt C [matt@artemisbot.uk]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
import kbpgp from "kbpgp";
|
||||
import promisifyDefault from "es6-promisify";
|
||||
const promisify = promisifyDefault.promisify;
|
||||
|
||||
/**
|
||||
* Progress callback
|
||||
*/
|
||||
export const ASP = kbpgp.ASP({
|
||||
"progress_hook": info => {
|
||||
let msg = "";
|
||||
|
||||
switch (info.what) {
|
||||
case "guess":
|
||||
msg = "Guessing a prime";
|
||||
break;
|
||||
case "fermat":
|
||||
msg = "Factoring prime using Fermat's factorization method";
|
||||
break;
|
||||
case "mr":
|
||||
msg = "Performing Miller-Rabin primality test";
|
||||
break;
|
||||
case "passed_mr":
|
||||
msg = "Passed Miller-Rabin primality test";
|
||||
break;
|
||||
case "failed_mr":
|
||||
msg = "Failed Miller-Rabin primality test";
|
||||
break;
|
||||
case "found":
|
||||
msg = "Prime found";
|
||||
break;
|
||||
default:
|
||||
msg = `Stage: ${info.what}`;
|
||||
}
|
||||
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage(msg);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get size of subkey
|
||||
*
|
||||
* @param {number} keySize
|
||||
* @returns {number}
|
||||
*/
|
||||
export function getSubkeySize(keySize) {
|
||||
return {
|
||||
1024: 1024,
|
||||
2048: 1024,
|
||||
4096: 2048,
|
||||
256: 256,
|
||||
384: 256,
|
||||
}[keySize];
|
||||
}
|
||||
|
||||
/**
|
||||
* Import private key and unlock if necessary
|
||||
*
|
||||
* @param {string} privateKey
|
||||
* @param {string} [passphrase]
|
||||
* @returns {Object}
|
||||
*/
|
||||
export async function importPrivateKey(privateKey, passphrase) {
|
||||
try {
|
||||
const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
||||
armored: privateKey,
|
||||
opts: {
|
||||
"no_check_keys": true
|
||||
}
|
||||
});
|
||||
if (key.is_pgp_locked()) {
|
||||
if (passphrase) {
|
||||
await promisify(key.unlock_pgp.bind(key))({
|
||||
passphrase
|
||||
});
|
||||
} else {
|
||||
throw "Did not provide passphrase with locked private key.";
|
||||
}
|
||||
}
|
||||
return key;
|
||||
} catch (err) {
|
||||
throw `Could not import private key: ${err}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import public key
|
||||
*
|
||||
* @param {string} publicKey
|
||||
* @returns {Object}
|
||||
*/
|
||||
export async function importPublicKey (publicKey) {
|
||||
try {
|
||||
const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
||||
armored: publicKey,
|
||||
opts: {
|
||||
"no_check_keys": true
|
||||
}
|
||||
});
|
||||
return key;
|
||||
} catch (err) {
|
||||
throw `Could not import public key: ${err}`;
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* AES Decrypt operation
|
||||
@ -65,6 +66,8 @@ class AESDecrypt extends Operation {
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @throws {OperationError} if cannot decrypt input or invalid key length
|
||||
*/
|
||||
run(input, args) {
|
||||
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
||||
@ -75,12 +78,12 @@ class AESDecrypt extends Operation {
|
||||
gcmTag = Utils.convertToByteString(args[5].string, args[5].option);
|
||||
|
||||
if ([16, 24, 32].indexOf(key.length) < 0) {
|
||||
return `Invalid key length: ${key.length} bytes
|
||||
throw new OperationError(`Invalid key length: ${key.length} bytes
|
||||
|
||||
The following algorithms will be used based on the size of the key:
|
||||
16 bytes = AES-128
|
||||
24 bytes = AES-192
|
||||
32 bytes = AES-256`;
|
||||
32 bytes = AES-256`);
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
@ -96,7 +99,7 @@ The following algorithms will be used based on the size of the key:
|
||||
if (result) {
|
||||
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
|
||||
} else {
|
||||
return "Unable to decrypt input with these parameters.";
|
||||
throw new OperationError("Unable to decrypt input with these parameters.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* AES Encrypt operation
|
||||
@ -59,6 +60,8 @@ class AESEncrypt extends Operation {
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @throws {OperationError} if invalid key length
|
||||
*/
|
||||
run(input, args) {
|
||||
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
||||
@ -68,12 +71,12 @@ class AESEncrypt extends Operation {
|
||||
outputType = args[4];
|
||||
|
||||
if ([16, 24, 32].indexOf(key.length) < 0) {
|
||||
return `Invalid key length: ${key.length} bytes
|
||||
throw new OperationError(`Invalid key length: ${key.length} bytes
|
||||
|
||||
The following algorithms will be used based on the size of the key:
|
||||
16 bytes = AES-128
|
||||
24 bytes = AES-192
|
||||
32 bytes = AES-256`;
|
||||
32 bytes = AES-256`);
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
@ -42,6 +42,8 @@ class AffineCipherDecode extends Operation {
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @throws {OperationError} if a or b values are invalid
|
||||
*/
|
||||
run(input, args) {
|
||||
const alphabet = "abcdefghijklmnopqrstuvwxyz",
|
||||
|
@ -37,6 +37,8 @@ class BifidCipherDecode extends Operation {
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @throws {OperationError} if invalid key
|
||||
*/
|
||||
run(input, args) {
|
||||
const keywordStr = args[0].toUpperCase().replace("J", "I"),
|
||||
|
@ -37,6 +37,8 @@ class BifidCipherEncode extends Operation {
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @throws {OperationError} if key is invalid
|
||||
*/
|
||||
run(input, args) {
|
||||
const keywordStr = args[0].toUpperCase().replace("J", "I"),
|
||||
|
@ -41,7 +41,7 @@ class CartesianProduct extends Operation {
|
||||
* Validate input length
|
||||
*
|
||||
* @param {Object[]} sets
|
||||
* @throws {Error} if fewer than 2 sets
|
||||
* @throws {OperationError} if fewer than 2 sets
|
||||
*/
|
||||
validateSampleNumbers(sets) {
|
||||
if (!sets || sets.length < 2) {
|
||||
|
53
src/core/operations/ChiSquare.mjs
Normal file
53
src/core/operations/ChiSquare.mjs
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
/**
|
||||
* Chi Square operation
|
||||
*/
|
||||
class ChiSquare extends Operation {
|
||||
|
||||
/**
|
||||
* ChiSquare constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Chi Square";
|
||||
this.module = "Default";
|
||||
this.description = "Calculates the Chi Square distribution of values.";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "number";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
run(input, args) {
|
||||
const data = new Uint8Array(input);
|
||||
const distArray = new Array(256).fill(0);
|
||||
let 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ChiSquare;
|
133
src/core/operations/DisassembleX86.mjs
Normal file
133
src/core/operations/DisassembleX86.mjs
Normal file
@ -0,0 +1,133 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import * as disassemble from "../vendor/DisassembleX86-64";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Disassemble x86 operation
|
||||
*/
|
||||
class DisassembleX86 extends Operation {
|
||||
|
||||
/**
|
||||
* DisassembleX86 constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Disassemble x86";
|
||||
this.module = "Shellcode";
|
||||
this.description = "Disassembly is the process of translating machine language into assembly language.<br><br>This operation supports 64-bit, 32-bit and 16-bit code written for Intel or AMD x86 processors. It is particularly useful for reverse engineering shellcode.<br><br>Input should be in hexadecimal.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Bit mode",
|
||||
"type": "option",
|
||||
"value": ["64", "32", "16"]
|
||||
},
|
||||
{
|
||||
"name": "Compatibility",
|
||||
"type": "option",
|
||||
"value": [
|
||||
"Full x86 architecture",
|
||||
"Knights Corner",
|
||||
"Larrabee",
|
||||
"Cyrix",
|
||||
"Geode",
|
||||
"Centaur",
|
||||
"X86/486"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Code Segment (CS)",
|
||||
"type": "number",
|
||||
"value": 16
|
||||
},
|
||||
{
|
||||
"name": "Offset (IP)",
|
||||
"type": "number",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"name": "Show instruction hex",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "Show instruction position",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @throws {OperationError} if invalid mode value
|
||||
*/
|
||||
run(input, args) {
|
||||
const [
|
||||
mode,
|
||||
compatibility,
|
||||
codeSegment,
|
||||
offset,
|
||||
showInstructionHex,
|
||||
showInstructionPos
|
||||
] = args;
|
||||
|
||||
switch (mode) {
|
||||
case "64":
|
||||
disassemble.setBitMode(2);
|
||||
break;
|
||||
case "32":
|
||||
disassemble.setBitMode(1);
|
||||
break;
|
||||
case "16":
|
||||
disassemble.setBitMode(0);
|
||||
break;
|
||||
default:
|
||||
throw new OperationError("Invalid mode value");
|
||||
}
|
||||
|
||||
switch (compatibility) {
|
||||
case "Full x86 architecture":
|
||||
disassemble.CompatibilityMode(0);
|
||||
break;
|
||||
case "Knights Corner":
|
||||
disassemble.CompatibilityMode(1);
|
||||
break;
|
||||
case "Larrabee":
|
||||
disassemble.CompatibilityMode(2);
|
||||
break;
|
||||
case "Cyrix":
|
||||
disassemble.CompatibilityMode(3);
|
||||
break;
|
||||
case "Geode":
|
||||
disassemble.CompatibilityMode(4);
|
||||
break;
|
||||
case "Centaur":
|
||||
disassemble.CompatibilityMode(5);
|
||||
break;
|
||||
case "X86/486":
|
||||
disassemble.CompatibilityMode(6);
|
||||
break;
|
||||
}
|
||||
|
||||
disassemble.SetBasePosition(codeSegment + ":" + offset);
|
||||
disassemble.setShowInstructionHex(showInstructionHex);
|
||||
disassemble.setShowInstructionPos(showInstructionPos);
|
||||
disassemble.LoadBinCode(input.replace(/\s/g, ""));
|
||||
return disassemble.LDisassemble();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default DisassembleX86;
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Drop bytes operation
|
||||
@ -45,6 +46,8 @@ class DropBytes extends Operation {
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {ArrayBuffer}
|
||||
*
|
||||
* @throws {OperationError} if invalid input
|
||||
*/
|
||||
run(input, args) {
|
||||
const start = args[0],
|
||||
@ -52,7 +55,7 @@ class DropBytes extends Operation {
|
||||
applyToEachLine = args[2];
|
||||
|
||||
if (start < 0 || length < 0)
|
||||
throw "Error: Invalid value";
|
||||
throw new OperationError("Error: Invalid value");
|
||||
|
||||
if (!applyToEachLine) {
|
||||
const left = input.slice(0, start),
|
||||
|
96
src/core/operations/Entropy.mjs
Normal file
96
src/core/operations/Entropy.mjs
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
|
||||
/**
|
||||
* Entropy operation
|
||||
*/
|
||||
class Entropy extends Operation {
|
||||
|
||||
/**
|
||||
* Entropy constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Entropy";
|
||||
this.module = "Default";
|
||||
this.description = "Calculates the Shannon entropy of the input data which gives an idea of its randomness. 8 is the maximum.";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "number";
|
||||
this.presentType = "html";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
run(input, args) {
|
||||
const prob = [],
|
||||
uniques = input.unique(),
|
||||
str = Utils.byteArrayToChars(input);
|
||||
let i;
|
||||
|
||||
for (i = 0; i < uniques.length; i++) {
|
||||
prob.push(str.count(Utils.chr(uniques[i])) / input.length);
|
||||
}
|
||||
|
||||
let entropy = 0,
|
||||
p;
|
||||
|
||||
for (i = 0; i < prob.length; i++) {
|
||||
p = prob[i];
|
||||
entropy += p * Math.log(p) / Math.log(2);
|
||||
}
|
||||
|
||||
return -entropy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the entropy as a scale bar for web apps.
|
||||
*
|
||||
* @param {number} entropy
|
||||
* @returns {html}
|
||||
*/
|
||||
present(entropy) {
|
||||
return `Shannon entropy: ${entropy}
|
||||
<br><canvas id='chart-area'></canvas><br>
|
||||
- 0 represents no randomness (i.e. all the bytes in the data have the same value) whereas 8, the maximum, represents a completely random string.
|
||||
- Standard English text usually falls somewhere between 3.5 and 5.
|
||||
- Properly encrypted or compressed data of a reasonable length should have an entropy of over 7.5.
|
||||
|
||||
The following results show the entropy of chunks of the input data. Chunks with particularly high entropy could suggest encrypted or compressed sections.
|
||||
|
||||
<br><script>
|
||||
var canvas = document.getElementById("chart-area"),
|
||||
parentRect = canvas.parentNode.getBoundingClientRect(),
|
||||
entropy = ${entropy},
|
||||
height = parentRect.height * 0.25;
|
||||
|
||||
canvas.width = parentRect.width * 0.95;
|
||||
canvas.height = height > 150 ? 150 : height;
|
||||
|
||||
CanvasComponents.drawScaleBar(canvas, entropy, 8, [
|
||||
{
|
||||
label: "English text",
|
||||
min: 3.5,
|
||||
max: 5
|
||||
},{
|
||||
label: "Encrypted/compressed",
|
||||
min: 7.5,
|
||||
max: 8
|
||||
}
|
||||
]);
|
||||
</script>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Entropy;
|
79
src/core/operations/EscapeUnicodeCharacters.mjs
Normal file
79
src/core/operations/EscapeUnicodeCharacters.mjs
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
/**
|
||||
* Escape Unicode Characters operation
|
||||
*/
|
||||
class EscapeUnicodeCharacters extends Operation {
|
||||
|
||||
/**
|
||||
* EscapeUnicodeCharacters constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Escape Unicode Characters";
|
||||
this.module = "Default";
|
||||
this.description = "Converts characters to their unicode-escaped notations.<br><br>Supports the prefixes:<ul><li><code>\\u</code></li><li><code>%u</code></li><li><code>U+</code></li></ul>e.g. <code>σου</code> becomes <code>\\u03C3\\u03BF\\u03C5</code>";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Prefix",
|
||||
"type": "option",
|
||||
"value": ["\\u", "%u", "U+"]
|
||||
},
|
||||
{
|
||||
"name": "Encode all chars",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "Padding",
|
||||
"type": "number",
|
||||
"value": 4
|
||||
},
|
||||
{
|
||||
"name": "Uppercase hex",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const regexWhitelist = /[ -~]/i,
|
||||
[prefix, encodeAll, padding, uppercaseHex] = args;
|
||||
|
||||
let output = "",
|
||||
character = "";
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
character = input[i];
|
||||
if (!encodeAll && regexWhitelist.test(character)) {
|
||||
// It’s a printable ASCII character so don’t escape it.
|
||||
output += character;
|
||||
continue;
|
||||
}
|
||||
|
||||
let cp = character.codePointAt(0).toString(16);
|
||||
if (uppercaseHex) cp = cp.toUpperCase();
|
||||
output += prefix + cp.padStart(padding, "0");
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default EscapeUnicodeCharacters;
|
52
src/core/operations/ExtractDates.mjs
Normal file
52
src/core/operations/ExtractDates.mjs
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import { search } from "../lib/Extract";
|
||||
|
||||
/**
|
||||
* Extract dates operation
|
||||
*/
|
||||
class ExtractDates extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractDates constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract dates";
|
||||
this.module = "Regex";
|
||||
this.description = "Extracts dates in the following formats<ul><li><code>yyyy-mm-dd</code></li><li><code>dd/mm/yyyy</code></li><li><code>mm/dd/yyyy</code></li></ul>Dividers can be any of /, -, . or space";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Display total",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const displayTotal = args[0],
|
||||
date1 = "(?:19|20)\\d\\d[- /.](?:0[1-9]|1[012])[- /.](?:0[1-9]|[12][0-9]|3[01])", // yyyy-mm-dd
|
||||
date2 = "(?:0[1-9]|[12][0-9]|3[01])[- /.](?:0[1-9]|1[012])[- /.](?:19|20)\\d\\d", // dd/mm/yyyy
|
||||
date3 = "(?:0[1-9]|1[012])[- /.](?:0[1-9]|[12][0-9]|3[01])[- /.](?:19|20)\\d\\d", // mm/dd/yyyy
|
||||
regex = new RegExp(date1 + "|" + date2 + "|" + date3, "ig");
|
||||
|
||||
return search(input, regex, null, displayTotal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ExtractDates;
|
49
src/core/operations/ExtractDomains.mjs
Normal file
49
src/core/operations/ExtractDomains.mjs
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import { search } from "../lib/Extract";
|
||||
|
||||
/**
|
||||
* Extract domains operation
|
||||
*/
|
||||
class ExtractDomains extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractDomains constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract domains";
|
||||
this.module = "Regex";
|
||||
this.description = "Extracts domain names.<br>Note that this will not include paths. Use <strong>Extract URLs</strong> to find entire URLs.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Display total",
|
||||
"type": "boolean",
|
||||
"value": "Extract.DISPLAY_TOTAL"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const displayTotal = args[0],
|
||||
regex = /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b/ig;
|
||||
|
||||
return search(input, regex, null, displayTotal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ExtractDomains;
|
49
src/core/operations/ExtractEmailAddresses.mjs
Normal file
49
src/core/operations/ExtractEmailAddresses.mjs
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import { search } from "../lib/Extract";
|
||||
|
||||
/**
|
||||
* Extract email addresses operation
|
||||
*/
|
||||
class ExtractEmailAddresses extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractEmailAddresses constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract email addresses";
|
||||
this.module = "Regex";
|
||||
this.description = "Extracts all email addresses from the input.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Display total",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const displayTotal = args[0],
|
||||
regex = /\b\w[-.\w]*@[-\w]+(?:\.[-\w]+)*\.[A-Z]{2,4}\b/ig;
|
||||
|
||||
return search(input, regex, null, displayTotal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ExtractEmailAddresses;
|
78
src/core/operations/ExtractFilePaths.mjs
Normal file
78
src/core/operations/ExtractFilePaths.mjs
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import { search } from "../lib/Extract";
|
||||
|
||||
/**
|
||||
* Extract file paths operation
|
||||
*/
|
||||
class ExtractFilePaths extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractFilePaths constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract file paths";
|
||||
this.module = "Regex";
|
||||
this.description = "Extracts anything that looks like a Windows or UNIX file path.<br><br>Note that if UNIX is selected, there will likely be a lot of false positives.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Windows",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "UNIX",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "Display total",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [includeWinPath, includeUnixPath, displayTotal] = args,
|
||||
winDrive = "[A-Z]:\\\\",
|
||||
winName = "[A-Z\\d][A-Z\\d\\- '_\\(\\)~]{0,61}",
|
||||
winExt = "[A-Z\\d]{1,6}",
|
||||
winPath = winDrive + "(?:" + winName + "\\\\?)*" + winName +
|
||||
"(?:\\." + winExt + ")?",
|
||||
unixPath = "(?:/[A-Z\\d.][A-Z\\d\\-.]{0,61})+";
|
||||
let filePaths = "";
|
||||
|
||||
if (includeWinPath && includeUnixPath) {
|
||||
filePaths = winPath + "|" + unixPath;
|
||||
} else if (includeWinPath) {
|
||||
filePaths = winPath;
|
||||
} else if (includeUnixPath) {
|
||||
filePaths = unixPath;
|
||||
}
|
||||
|
||||
if (filePaths) {
|
||||
const regex = new RegExp(filePaths, "ig");
|
||||
return search(input, regex, null, displayTotal);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ExtractFilePaths;
|
91
src/core/operations/ExtractIPAddresses.mjs
Normal file
91
src/core/operations/ExtractIPAddresses.mjs
Normal file
@ -0,0 +1,91 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import { search } from "../lib/Extract";
|
||||
|
||||
/**
|
||||
* Extract IP addresses operation
|
||||
*/
|
||||
class ExtractIPAddresses extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractIPAddresses constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract IP addresses";
|
||||
this.module = "Regex";
|
||||
this.description = "Extracts all IPv4 and IPv6 addresses.<br><br>Warning: Given a string <code>710.65.0.456</code>, this will match <code>10.65.0.45</code> so always check the original input!";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "IPv4",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "IPv6",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "Remove local IPv4 addresses",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "Display total",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [includeIpv4, includeIpv6, removeLocal, displayTotal] = args,
|
||||
ipv4 = "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?",
|
||||
ipv6 = "((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})((([\\dA-F]{1,4}((?!\\3)::|:\\b|(?![\\dA-F])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})";
|
||||
let ips = "";
|
||||
|
||||
if (includeIpv4 && includeIpv6) {
|
||||
ips = ipv4 + "|" + ipv6;
|
||||
} else if (includeIpv4) {
|
||||
ips = ipv4;
|
||||
} else if (includeIpv6) {
|
||||
ips = ipv6;
|
||||
}
|
||||
|
||||
if (ips) {
|
||||
const regex = new RegExp(ips, "ig");
|
||||
|
||||
if (removeLocal) {
|
||||
const ten = "10\\..+",
|
||||
oneninetwo = "192\\.168\\..+",
|
||||
oneseventwo = "172\\.(?:1[6-9]|2\\d|3[01])\\..+",
|
||||
onetwoseven = "127\\..+",
|
||||
removeRegex = new RegExp("^(?:" + ten + "|" + oneninetwo +
|
||||
"|" + oneseventwo + "|" + onetwoseven + ")");
|
||||
|
||||
return search(input, regex, removeRegex, displayTotal);
|
||||
} else {
|
||||
return search(input, regex, null, displayTotal);
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ExtractIPAddresses;
|
49
src/core/operations/ExtractMACAddresses.mjs
Normal file
49
src/core/operations/ExtractMACAddresses.mjs
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import { search } from "../lib/Extract";
|
||||
|
||||
/**
|
||||
* Extract MAC addresses operation
|
||||
*/
|
||||
class ExtractMACAddresses extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractMACAddresses constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract MAC addresses";
|
||||
this.module = "Regex";
|
||||
this.description = "Extracts all Media Access Control (MAC) addresses from the input.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Display total",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const displayTotal = args[0],
|
||||
regex = /[A-F\d]{2}(?:[:-][A-F\d]{2}){5}/ig;
|
||||
|
||||
return search(input, regex, null, displayTotal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ExtractMACAddresses;
|
55
src/core/operations/ExtractURLs.mjs
Normal file
55
src/core/operations/ExtractURLs.mjs
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import { search } from "../lib/Extract";
|
||||
|
||||
/**
|
||||
* Extract URLs operation
|
||||
*/
|
||||
class ExtractURLs extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractURLs constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract URLs";
|
||||
this.module = "Regex";
|
||||
this.description = "Extracts Uniform Resource Locators (URLs) from the input. The protocol (http, ftp etc.) is required otherwise there will be far too many false positives.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Display total",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const displayTotal = args[0],
|
||||
protocol = "[A-Z]+://",
|
||||
hostname = "[-\\w]+(?:\\.\\w[-\\w]*)+",
|
||||
port = ":\\d+";
|
||||
let path = "/[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]*";
|
||||
|
||||
path += "(?:[.!,?]+[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]+)*";
|
||||
const regex = new RegExp(protocol + hostname + "(?:" + port +
|
||||
")?(?:" + path + ")?", "ig");
|
||||
return search(input, regex, null, displayTotal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ExtractURLs;
|
@ -7,6 +7,7 @@
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import {INPUT_DELIM_OPTIONS} from "../lib/Delim";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Filter operation
|
||||
@ -56,7 +57,7 @@ class Filter extends Operation {
|
||||
try {
|
||||
regex = new RegExp(args[1]);
|
||||
} catch (err) {
|
||||
return "Invalid regex. Details: " + err.message;
|
||||
throw new OperationError(`Invalid regex. Details: ${err.message}`);
|
||||
}
|
||||
|
||||
const regexFilter = function(value) {
|
||||
|
110
src/core/operations/FrequencyDistribution.mjs
Normal file
110
src/core/operations/FrequencyDistribution.mjs
Normal file
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Frequency distribution operation
|
||||
*/
|
||||
class FrequencyDistribution extends Operation {
|
||||
|
||||
/**
|
||||
* FrequencyDistribution constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Frequency distribution";
|
||||
this.module = "Default";
|
||||
this.description = "Displays the distribution of bytes in the data as a graph.";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "json";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Show 0%s",
|
||||
"type": "boolean",
|
||||
"value": "Entropy.FREQ_ZEROS"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {json}
|
||||
*/
|
||||
run(input, args) {
|
||||
const data = new Uint8Array(input);
|
||||
if (!data.length) throw new OperationError("No data");
|
||||
|
||||
const distrib = new Array(256).fill(0),
|
||||
percentages = new Array(256),
|
||||
len = data.length;
|
||||
let i;
|
||||
|
||||
// Count bytes
|
||||
for (i = 0; i < len; i++) {
|
||||
distrib[data[i]]++;
|
||||
}
|
||||
|
||||
// Calculate percentages
|
||||
let repr = 0;
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (distrib[i] > 0) repr++;
|
||||
percentages[i] = distrib[i] / len * 100;
|
||||
}
|
||||
|
||||
return {
|
||||
"dataLength": len,
|
||||
"percentages": percentages,
|
||||
"distribution": distrib,
|
||||
"bytesRepresented": repr
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the frequency distribution as a bar chart for web apps.
|
||||
*
|
||||
* @param {json} freq
|
||||
* @returns {html}
|
||||
*/
|
||||
present(freq, args) {
|
||||
const showZeroes = args[0];
|
||||
// Print
|
||||
let output = `<canvas id='chart-area'></canvas><br>
|
||||
Total data length: ${freq.dataLength}
|
||||
Number of bytes represented: ${freq.bytesRepresented}
|
||||
Number of bytes not represented: ${256 - freq.bytesRepresented}
|
||||
|
||||
Byte Percentage
|
||||
<script>
|
||||
var canvas = document.getElementById("chart-area"),
|
||||
parentRect = canvas.parentNode.getBoundingClientRect(),
|
||||
scores = ${JSON.stringify(freq.percentages)};
|
||||
|
||||
canvas.width = parentRect.width * 0.95;
|
||||
canvas.height = parentRect.height * 0.9;
|
||||
|
||||
CanvasComponents.drawBarChart(canvas, scores, "Byte", "Frequency %", 16, 6);
|
||||
</script>`;
|
||||
|
||||
for (let i = 0; i < 256; i++) {
|
||||
if (freq.distribution[i] || showZeroes) {
|
||||
output += " " + Utils.hex(i, 2) + " (" +
|
||||
(freq.percentages[i].toFixed(2).replace(".00", "") + "%)").padEnd(8, " ") +
|
||||
Array(Math.ceil(freq.percentages[i])+1).join("|") + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default FrequencyDistribution;
|
115
src/core/operations/FromBCD.mjs
Normal file
115
src/core/operations/FromBCD.mjs
Normal file
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import {ENCODING_SCHEME, ENCODING_LOOKUP, FORMAT} from "../lib/BCD";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
/**
|
||||
* From BCD operation
|
||||
*/
|
||||
class FromBCD extends Operation {
|
||||
|
||||
/**
|
||||
* FromBCD constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "From BCD";
|
||||
this.module = "Default";
|
||||
this.description = "Binary-Coded Decimal (BCD) is a class of binary encodings of decimal numbers where each decimal digit is represented by a fixed number of bits, usually four or eight. Special bit patterns are sometimes used for a sign.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "BigNumber";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Scheme",
|
||||
"type": "option",
|
||||
"value": ENCODING_SCHEME
|
||||
},
|
||||
{
|
||||
"name": "Packed",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "Signed",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "Input format",
|
||||
"type": "option",
|
||||
"value": FORMAT
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
run(input, args) {
|
||||
const encoding = ENCODING_LOOKUP[args[0]],
|
||||
packed = args[1],
|
||||
signed = args[2],
|
||||
inputFormat = args[3],
|
||||
nibbles = [];
|
||||
|
||||
let output = "",
|
||||
byteArray;
|
||||
|
||||
// Normalise the input
|
||||
switch (inputFormat) {
|
||||
case "Nibbles":
|
||||
case "Bytes":
|
||||
input = input.replace(/\s/g, "");
|
||||
for (let i = 0; i < input.length; i += 4) {
|
||||
nibbles.push(parseInt(input.substr(i, 4), 2));
|
||||
}
|
||||
break;
|
||||
case "Raw":
|
||||
default:
|
||||
byteArray = Utils.strToByteArray(input);
|
||||
byteArray.forEach(b => {
|
||||
nibbles.push(b >>> 4);
|
||||
nibbles.push(b & 15);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if (!packed) {
|
||||
// Discard each high nibble
|
||||
for (let i = 0; i < nibbles.length; i++) {
|
||||
nibbles.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (signed) {
|
||||
const sign = nibbles.pop();
|
||||
if (sign === 13 ||
|
||||
sign === 11) {
|
||||
// Negative
|
||||
output += "-";
|
||||
}
|
||||
}
|
||||
|
||||
nibbles.forEach(n => {
|
||||
if (isNaN(n)) throw new OperationError("Invalid input");
|
||||
const val = encoding.indexOf(n);
|
||||
if (val < 0) throw new OperationError(`Value ${Utils.bin(n, 4)} is not in the encoding scheme`);
|
||||
output += val.toString();
|
||||
});
|
||||
|
||||
return new BigNumber(output);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default FromBCD;
|
63
src/core/operations/FromBase.mjs
Normal file
63
src/core/operations/FromBase.mjs
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import BigNumber from "bignumber.js";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* From Base operation
|
||||
*/
|
||||
class FromBase extends Operation {
|
||||
|
||||
/**
|
||||
* FromBase constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "From Base";
|
||||
this.module = "Default";
|
||||
this.description = "Converts a number to decimal from a given numerical base.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "BigNumber";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Radix",
|
||||
"type": "number",
|
||||
"value": 36
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
run(input, args) {
|
||||
const radix = args[0];
|
||||
if (radix < 2 || radix > 36) {
|
||||
throw new OperationError("Error: Radix argument must be between 2 and 36");
|
||||
}
|
||||
|
||||
const number = input.replace(/\s/g, "").split(".");
|
||||
let result = new BigNumber(number[0], radix) || 0;
|
||||
|
||||
if (number.length === 1) return result;
|
||||
|
||||
// Fractional part
|
||||
for (let i = 0; i < number[1].length; i++) {
|
||||
const digit = new BigNumber(number[1][i], radix);
|
||||
result += digit.div(Math.pow(radix, i+1));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default FromBase;
|
93
src/core/operations/FromBase58.mjs
Normal file
93
src/core/operations/FromBase58.mjs
Normal file
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import {ALPHABET_OPTIONS} from "../lib/Base58";
|
||||
|
||||
/**
|
||||
* From Base58 operation
|
||||
*/
|
||||
class FromBase58 extends Operation {
|
||||
|
||||
/**
|
||||
* FromBase58 constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "From Base58";
|
||||
this.module = "Default";
|
||||
this.description = "Base58 (similar to Base64) is a notation for encoding arbitrary byte data. It differs from Base64 by removing easily misread characters (i.e. l, I, 0 and O) to improve human readability.<br><br>This operation decodes data from an ASCII string (with an alphabet of your choosing, presets included) back into its raw form.<br><br>e.g. <code>StV1DL6CwTryKyV</code> becomes <code>hello world</code><br><br>Base58 is commonly used in cryptocurrencies (Bitcoin, Ripple, etc).";
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Alphabet",
|
||||
"type": "editableOption",
|
||||
"value": ALPHABET_OPTIONS
|
||||
},
|
||||
{
|
||||
"name": "Remove non-alphabet chars",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
let alphabet = args[0] || ALPHABET_OPTIONS[0].value;
|
||||
const removeNonAlphaChars = args[1] === undefined ? true : args[1],
|
||||
result = [0];
|
||||
|
||||
alphabet = Utils.expandAlphRange(alphabet).join("");
|
||||
|
||||
if (alphabet.length !== 58 ||
|
||||
[].unique.call(alphabet).length !== 58) {
|
||||
throw new OperationError("Alphabet must be of length 58");
|
||||
}
|
||||
|
||||
if (input.length === 0) return [];
|
||||
|
||||
[].forEach.call(input, function(c, charIndex) {
|
||||
const index = alphabet.indexOf(c);
|
||||
|
||||
if (index === -1) {
|
||||
if (removeNonAlphaChars) {
|
||||
return;
|
||||
} else {
|
||||
throw new OperationError(`Char '${c}' at position ${charIndex} not in alphabet`);
|
||||
}
|
||||
}
|
||||
|
||||
let carry = result[0] * 58 + index;
|
||||
result[0] = carry & 0xFF;
|
||||
carry = carry >> 8;
|
||||
|
||||
for (let i = 1; i < result.length; i++) {
|
||||
carry += result[i] * 58;
|
||||
result[i] = carry & 0xFF;
|
||||
carry = carry >> 8;
|
||||
}
|
||||
|
||||
while (carry > 0) {
|
||||
result.push(carry & 0xFF);
|
||||
carry = carry >> 8;
|
||||
}
|
||||
});
|
||||
|
||||
return result.reverse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default FromBase58;
|
@ -7,6 +7,7 @@
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import {DELIM_OPTIONS} from "../lib/Delim";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* From Charcode operation
|
||||
@ -42,6 +43,8 @@ class FromCharcode extends Operation {
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*
|
||||
* @throws {OperationError} if base out of range
|
||||
*/
|
||||
run(input, args) {
|
||||
const delim = Utils.charRep(args[0] || "Space"),
|
||||
@ -50,7 +53,7 @@ class FromCharcode extends Operation {
|
||||
i = 0;
|
||||
|
||||
if (base < 2 || base > 36) {
|
||||
throw "Error: Base argument must be between 2 and 36";
|
||||
throw new OperationError("Error: Base argument must be between 2 and 36");
|
||||
}
|
||||
|
||||
if (input.length === 0) {
|
||||
|
335
src/core/operations/FromHTMLEntity.mjs
Normal file
335
src/core/operations/FromHTMLEntity.mjs
Normal file
@ -0,0 +1,335 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
|
||||
/**
|
||||
* From HTML Entity operation
|
||||
*/
|
||||
class FromHTMLEntity extends Operation {
|
||||
|
||||
/**
|
||||
* FromHTMLEntity constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "From HTML Entity";
|
||||
this.module = "Default";
|
||||
this.description = "Converts HTML entities back to characters<br><br>e.g. <code>&<span>amp;</span></code> becomes <code>&</code>";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const regex = /&(#?x?[a-zA-Z0-9]{1,8});/g;
|
||||
let output = "",
|
||||
m,
|
||||
i = 0;
|
||||
|
||||
while ((m = regex.exec(input))) {
|
||||
// Add up to match
|
||||
for (; i < m.index;)
|
||||
output += input[i++];
|
||||
|
||||
// Add match
|
||||
const bite = entityToByte[m[1]];
|
||||
if (bite) {
|
||||
output += Utils.chr(bite);
|
||||
} else if (!bite && m[1][0] === "#" && m[1].length > 1 && /^#\d{1,6}$/.test(m[1])) {
|
||||
// Numeric entity (e.g. )
|
||||
const num = m[1].slice(1, m[1].length);
|
||||
output += Utils.chr(parseInt(num, 10));
|
||||
} else if (!bite && m[1][0] === "#" && m[1].length > 3 && /^#x[\dA-F]{2,8}$/i.test(m[1])) {
|
||||
// Hex entity (e.g. :)
|
||||
const hex = m[1].slice(2, m[1].length);
|
||||
output += Utils.chr(parseInt(hex, 16));
|
||||
} else {
|
||||
// Not a valid entity, print as normal
|
||||
for (; i < regex.lastIndex;)
|
||||
output += input[i++];
|
||||
}
|
||||
|
||||
i = regex.lastIndex;
|
||||
}
|
||||
// Add all after final match
|
||||
for (; i < input.length;)
|
||||
output += input[i++];
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lookup table to translate HTML entity codes to their byte values.
|
||||
*/
|
||||
const entityToByte = {
|
||||
"quot": 34,
|
||||
"amp": 38,
|
||||
"apos": 39,
|
||||
"lt": 60,
|
||||
"gt": 62,
|
||||
"nbsp": 160,
|
||||
"iexcl": 161,
|
||||
"cent": 162,
|
||||
"pound": 163,
|
||||
"curren": 164,
|
||||
"yen": 165,
|
||||
"brvbar": 166,
|
||||
"sect": 167,
|
||||
"uml": 168,
|
||||
"copy": 169,
|
||||
"ordf": 170,
|
||||
"laquo": 171,
|
||||
"not": 172,
|
||||
"shy": 173,
|
||||
"reg": 174,
|
||||
"macr": 175,
|
||||
"deg": 176,
|
||||
"plusmn": 177,
|
||||
"sup2": 178,
|
||||
"sup3": 179,
|
||||
"acute": 180,
|
||||
"micro": 181,
|
||||
"para": 182,
|
||||
"middot": 183,
|
||||
"cedil": 184,
|
||||
"sup1": 185,
|
||||
"ordm": 186,
|
||||
"raquo": 187,
|
||||
"frac14": 188,
|
||||
"frac12": 189,
|
||||
"frac34": 190,
|
||||
"iquest": 191,
|
||||
"Agrave": 192,
|
||||
"Aacute": 193,
|
||||
"Acirc": 194,
|
||||
"Atilde": 195,
|
||||
"Auml": 196,
|
||||
"Aring": 197,
|
||||
"AElig": 198,
|
||||
"Ccedil": 199,
|
||||
"Egrave": 200,
|
||||
"Eacute": 201,
|
||||
"Ecirc": 202,
|
||||
"Euml": 203,
|
||||
"Igrave": 204,
|
||||
"Iacute": 205,
|
||||
"Icirc": 206,
|
||||
"Iuml": 207,
|
||||
"ETH": 208,
|
||||
"Ntilde": 209,
|
||||
"Ograve": 210,
|
||||
"Oacute": 211,
|
||||
"Ocirc": 212,
|
||||
"Otilde": 213,
|
||||
"Ouml": 214,
|
||||
"times": 215,
|
||||
"Oslash": 216,
|
||||
"Ugrave": 217,
|
||||
"Uacute": 218,
|
||||
"Ucirc": 219,
|
||||
"Uuml": 220,
|
||||
"Yacute": 221,
|
||||
"THORN": 222,
|
||||
"szlig": 223,
|
||||
"agrave": 224,
|
||||
"aacute": 225,
|
||||
"acirc": 226,
|
||||
"atilde": 227,
|
||||
"auml": 228,
|
||||
"aring": 229,
|
||||
"aelig": 230,
|
||||
"ccedil": 231,
|
||||
"egrave": 232,
|
||||
"eacute": 233,
|
||||
"ecirc": 234,
|
||||
"euml": 235,
|
||||
"igrave": 236,
|
||||
"iacute": 237,
|
||||
"icirc": 238,
|
||||
"iuml": 239,
|
||||
"eth": 240,
|
||||
"ntilde": 241,
|
||||
"ograve": 242,
|
||||
"oacute": 243,
|
||||
"ocirc": 244,
|
||||
"otilde": 245,
|
||||
"ouml": 246,
|
||||
"divide": 247,
|
||||
"oslash": 248,
|
||||
"ugrave": 249,
|
||||
"uacute": 250,
|
||||
"ucirc": 251,
|
||||
"uuml": 252,
|
||||
"yacute": 253,
|
||||
"thorn": 254,
|
||||
"yuml": 255,
|
||||
"OElig": 338,
|
||||
"oelig": 339,
|
||||
"Scaron": 352,
|
||||
"scaron": 353,
|
||||
"Yuml": 376,
|
||||
"fnof": 402,
|
||||
"circ": 710,
|
||||
"tilde": 732,
|
||||
"Alpha": 913,
|
||||
"Beta": 914,
|
||||
"Gamma": 915,
|
||||
"Delta": 916,
|
||||
"Epsilon": 917,
|
||||
"Zeta": 918,
|
||||
"Eta": 919,
|
||||
"Theta": 920,
|
||||
"Iota": 921,
|
||||
"Kappa": 922,
|
||||
"Lambda": 923,
|
||||
"Mu": 924,
|
||||
"Nu": 925,
|
||||
"Xi": 926,
|
||||
"Omicron": 927,
|
||||
"Pi": 928,
|
||||
"Rho": 929,
|
||||
"Sigma": 931,
|
||||
"Tau": 932,
|
||||
"Upsilon": 933,
|
||||
"Phi": 934,
|
||||
"Chi": 935,
|
||||
"Psi": 936,
|
||||
"Omega": 937,
|
||||
"alpha": 945,
|
||||
"beta": 946,
|
||||
"gamma": 947,
|
||||
"delta": 948,
|
||||
"epsilon": 949,
|
||||
"zeta": 950,
|
||||
"eta": 951,
|
||||
"theta": 952,
|
||||
"iota": 953,
|
||||
"kappa": 954,
|
||||
"lambda": 955,
|
||||
"mu": 956,
|
||||
"nu": 957,
|
||||
"xi": 958,
|
||||
"omicron": 959,
|
||||
"pi": 960,
|
||||
"rho": 961,
|
||||
"sigmaf": 962,
|
||||
"sigma": 963,
|
||||
"tau": 964,
|
||||
"upsilon": 965,
|
||||
"phi": 966,
|
||||
"chi": 967,
|
||||
"psi": 968,
|
||||
"omega": 969,
|
||||
"thetasym": 977,
|
||||
"upsih": 978,
|
||||
"piv": 982,
|
||||
"ensp": 8194,
|
||||
"emsp": 8195,
|
||||
"thinsp": 8201,
|
||||
"zwnj": 8204,
|
||||
"zwj": 8205,
|
||||
"lrm": 8206,
|
||||
"rlm": 8207,
|
||||
"ndash": 8211,
|
||||
"mdash": 8212,
|
||||
"lsquo": 8216,
|
||||
"rsquo": 8217,
|
||||
"sbquo": 8218,
|
||||
"ldquo": 8220,
|
||||
"rdquo": 8221,
|
||||
"bdquo": 8222,
|
||||
"dagger": 8224,
|
||||
"Dagger": 8225,
|
||||
"bull": 8226,
|
||||
"hellip": 8230,
|
||||
"permil": 8240,
|
||||
"prime": 8242,
|
||||
"Prime": 8243,
|
||||
"lsaquo": 8249,
|
||||
"rsaquo": 8250,
|
||||
"oline": 8254,
|
||||
"frasl": 8260,
|
||||
"euro": 8364,
|
||||
"image": 8465,
|
||||
"weierp": 8472,
|
||||
"real": 8476,
|
||||
"trade": 8482,
|
||||
"alefsym": 8501,
|
||||
"larr": 8592,
|
||||
"uarr": 8593,
|
||||
"rarr": 8594,
|
||||
"darr": 8595,
|
||||
"harr": 8596,
|
||||
"crarr": 8629,
|
||||
"lArr": 8656,
|
||||
"uArr": 8657,
|
||||
"rArr": 8658,
|
||||
"dArr": 8659,
|
||||
"hArr": 8660,
|
||||
"forall": 8704,
|
||||
"part": 8706,
|
||||
"exist": 8707,
|
||||
"empty": 8709,
|
||||
"nabla": 8711,
|
||||
"isin": 8712,
|
||||
"notin": 8713,
|
||||
"ni": 8715,
|
||||
"prod": 8719,
|
||||
"sum": 8721,
|
||||
"minus": 8722,
|
||||
"lowast": 8727,
|
||||
"radic": 8730,
|
||||
"prop": 8733,
|
||||
"infin": 8734,
|
||||
"ang": 8736,
|
||||
"and": 8743,
|
||||
"or": 8744,
|
||||
"cap": 8745,
|
||||
"cup": 8746,
|
||||
"int": 8747,
|
||||
"there4": 8756,
|
||||
"sim": 8764,
|
||||
"cong": 8773,
|
||||
"asymp": 8776,
|
||||
"ne": 8800,
|
||||
"equiv": 8801,
|
||||
"le": 8804,
|
||||
"ge": 8805,
|
||||
"sub": 8834,
|
||||
"sup": 8835,
|
||||
"nsub": 8836,
|
||||
"sube": 8838,
|
||||
"supe": 8839,
|
||||
"oplus": 8853,
|
||||
"otimes": 8855,
|
||||
"perp": 8869,
|
||||
"sdot": 8901,
|
||||
"vellip": 8942,
|
||||
"lceil": 8968,
|
||||
"rceil": 8969,
|
||||
"lfloor": 8970,
|
||||
"rfloor": 8971,
|
||||
"lang": 9001,
|
||||
"rang": 9002,
|
||||
"loz": 9674,
|
||||
"spades": 9824,
|
||||
"clubs": 9827,
|
||||
"hearts": 9829,
|
||||
"diams": 9830,
|
||||
};
|
||||
|
||||
export default FromHTMLEntity;
|
61
src/core/operations/FromQuotedPrintable.mjs
Normal file
61
src/core/operations/FromQuotedPrintable.mjs
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Some parts taken from mimelib (http://github.com/andris9/mimelib)
|
||||
* @author Andris Reinman
|
||||
* @license MIT
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
/**
|
||||
* From Quoted Printable operation
|
||||
*/
|
||||
class FromQuotedPrintable extends Operation {
|
||||
|
||||
/**
|
||||
* FromQuotedPrintable constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "From Quoted Printable";
|
||||
this.module = "Default";
|
||||
this.description = "Converts QP-encoded text back to standard text.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
const str = input.replace(/=(?:\r?\n|$)/g, "");
|
||||
|
||||
const encodedBytesCount = (str.match(/=[\da-fA-F]{2}/g) || []).length,
|
||||
bufferLength = str.length - encodedBytesCount * 2,
|
||||
buffer = new Array(bufferLength);
|
||||
let chr, hex,
|
||||
bufferPos = 0;
|
||||
|
||||
for (let i = 0, len = str.length; i < len; i++) {
|
||||
chr = str.charAt(i);
|
||||
if (chr === "=" && (hex = str.substr(i + 1, 2)) && /[\da-fA-F]{2}/.test(hex)) {
|
||||
buffer[bufferPos++] = parseInt(hex, 16);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
buffer[bufferPos++] = chr.charCodeAt(0);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default FromQuotedPrintable;
|
@ -7,6 +7,7 @@
|
||||
import Operation from "../Operation";
|
||||
import moment from "moment-timezone";
|
||||
import {UNITS} from "../lib/DateTime";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* From UNIX Timestamp operation
|
||||
@ -37,6 +38,8 @@ class FromUNIXTimestamp extends Operation {
|
||||
* @param {number} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @throws {OperationError} if invalid unit
|
||||
*/
|
||||
run(input, args) {
|
||||
const units = args[0];
|
||||
@ -57,7 +60,7 @@ class FromUNIXTimestamp extends Operation {
|
||||
d = moment(input / 1000000);
|
||||
return d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss.SSS") + " UTC";
|
||||
} else {
|
||||
throw "Unrecognised unit";
|
||||
throw new OperationError("Unrecognised unit");
|
||||
}
|
||||
}
|
||||
|
||||
|
116
src/core/operations/GeneratePGPKeyPair.mjs
Normal file
116
src/core/operations/GeneratePGPKeyPair.mjs
Normal file
@ -0,0 +1,116 @@
|
||||
/**
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @author Matt C [matt@artemisbot.uk]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import kbpgp from "kbpgp";
|
||||
import { getSubkeySize, ASP } from "../lib/PGP";
|
||||
import promisifyDefault from "es6-promisify";
|
||||
const promisify = promisifyDefault.promisify;
|
||||
|
||||
/**
|
||||
* Generate PGP Key Pair operation
|
||||
*/
|
||||
class GeneratePGPKeyPair extends Operation {
|
||||
|
||||
/**
|
||||
* GeneratePGPKeyPair constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Generate PGP Key Pair";
|
||||
this.module = "PGP";
|
||||
this.description = "Generates a new public/private PGP key pair. Supports RSA and Eliptic Curve (EC) keys.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Key type",
|
||||
"type": "option",
|
||||
"value": ["RSA-1024", "RSA-2048", "RSA-4096", "ECC-256", "ECC-384"]
|
||||
},
|
||||
{
|
||||
"name": "Password (optional)",
|
||||
"type": "string",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "Name (optional)",
|
||||
"type": "string",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "Email (optional)",
|
||||
"type": "string",
|
||||
"value": ""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [keyType, keySize] = args[0].split("-"),
|
||||
password = args[1],
|
||||
name = args[2],
|
||||
email = args[3];
|
||||
let userIdentifier = "";
|
||||
|
||||
if (name) userIdentifier += name;
|
||||
if (email) userIdentifier += ` <${email}>`;
|
||||
|
||||
let flags = kbpgp.const.openpgp.certify_keys;
|
||||
flags |= kbpgp.const.openpgp.sign_data;
|
||||
flags |= kbpgp.const.openpgp.auth;
|
||||
flags |= kbpgp.const.openpgp.encrypt_comm;
|
||||
flags |= kbpgp.const.openpgp.encrypt_storage;
|
||||
|
||||
const keyGenerationOptions = {
|
||||
userid: userIdentifier,
|
||||
ecc: keyType === "ecc",
|
||||
primary: {
|
||||
"nbits": keySize,
|
||||
"flags": flags,
|
||||
"expire_in": 0
|
||||
},
|
||||
subkeys: [{
|
||||
"nbits": getSubkeySize(keySize),
|
||||
"flags": kbpgp.const.openpgp.sign_data,
|
||||
"expire_in": 86400 * 365 * 8
|
||||
}, {
|
||||
"nbits": getSubkeySize(keySize),
|
||||
"flags": kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage,
|
||||
"expire_in": 86400 * 365 * 2
|
||||
}],
|
||||
asp: ASP
|
||||
};
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const unsignedKey = await promisify(kbpgp.KeyManager.generate)(keyGenerationOptions);
|
||||
await promisify(unsignedKey.sign.bind(unsignedKey))({});
|
||||
|
||||
const signedKey = unsignedKey,
|
||||
privateKeyExportOptions = {};
|
||||
|
||||
if (password) privateKeyExportOptions.passphrase = password;
|
||||
const privateKey = await promisify(signedKey.export_pgp_private.bind(signedKey))(privateKeyExportOptions);
|
||||
const publicKey = await promisify(signedKey.export_pgp_public.bind(signedKey))({});
|
||||
resolve(privateKey + "\n" + publicKey.trim());
|
||||
} catch (err) {
|
||||
reject(`Error whilst generating key pair: ${err}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default GeneratePGPKeyPair;
|
@ -7,6 +7,7 @@
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import {fromHex} from "../lib/Hex";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Hamming Distance operation
|
||||
@ -55,11 +56,11 @@ class HammingDistance extends Operation {
|
||||
samples = input.split(delim);
|
||||
|
||||
if (samples.length !== 2) {
|
||||
return "Error: You can only calculae the edit distance between 2 strings. Please ensure exactly two inputs are provided, separated by the specified delimiter.";
|
||||
throw new OperationError("Error: You can only calculae the edit distance between 2 strings. Please ensure exactly two inputs are provided, separated by the specified delimiter.");
|
||||
}
|
||||
|
||||
if (samples[0].length !== samples[1].length) {
|
||||
return "Error: Both inputs must be of the same length.";
|
||||
throw new OperationError("Error: Both inputs must be of the same length.");
|
||||
}
|
||||
|
||||
if (inputType === "Hex") {
|
||||
|
217
src/core/operations/MicrosoftScriptDecoder.mjs
Normal file
217
src/core/operations/MicrosoftScriptDecoder.mjs
Normal file
@ -0,0 +1,217 @@
|
||||
/**
|
||||
* @author bmwhitn [brian.m.whitney@outlook.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
/**
|
||||
* Microsoft Script Decoder operation
|
||||
*/
|
||||
class MicrosoftScriptDecoder extends Operation {
|
||||
|
||||
/**
|
||||
* MicrosoftScriptDecoder constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Microsoft Script Decoder";
|
||||
this.module = "Default";
|
||||
this.description = "Decodes Microsoft Encoded Script files that have been encoded with Microsoft's custom encoding. These are often VBS (Visual Basic Script) files that are encoded and renamed with a '.vbe' extention or JS (JScript) files renamed with a '.jse' extention.<br><br><b>Sample</b><br><br>Encoded:<br><code>#@~^RQAAAA==-mD~sX|:/TP{~J:+dYbxL~@!F@*@!+@*@!&@*eEI@#@&@#@&.jm.raY 214Wv:zms/obI0xEAAA==^#~@</code><br><br>Decoded:<br><code>var my_msg = "Testing <1><2><3>!";\n\nVScript.Echo(my_msg);</code>";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const matcher = /#@~\^.{6}==(.+).{6}==\^#~@/;
|
||||
const encodedData = matcher.exec(input);
|
||||
if (encodedData){
|
||||
return MicrosoftScriptDecoder._decode(encodedData[1]);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes Microsoft Encoded Script files that can be read and executed by cscript.exe/wscript.exe.
|
||||
* This is a conversion of a Python script that was originally created by Didier Stevens
|
||||
* (https://DidierStevens.com).
|
||||
*
|
||||
* @private
|
||||
* @param {string} data
|
||||
* @returns {string}
|
||||
*/
|
||||
static _decode(data) {
|
||||
const result = [];
|
||||
let index = -1;
|
||||
data = data.replace(/@&/g, String.fromCharCode(10))
|
||||
.replace(/@#/g, String.fromCharCode(13))
|
||||
.replace(/@\*/g, ">")
|
||||
.replace(/@!/g, "<")
|
||||
.replace(/@\$/g, "@");
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const byte = data.charCodeAt(i);
|
||||
let char = data.charAt(i);
|
||||
if (byte < 128) {
|
||||
index++;
|
||||
}
|
||||
|
||||
if ((byte === 9 || byte > 31 && byte < 128) &&
|
||||
byte !== 60 &&
|
||||
byte !== 62 &&
|
||||
byte !== 64) {
|
||||
char = D_DECODE[byte].charAt(D_COMBINATION[index % 64]);
|
||||
}
|
||||
result.push(char);
|
||||
}
|
||||
return result.join("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const D_DECODE = [
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"\x57\x6E\x7B",
|
||||
"\x4A\x4C\x41",
|
||||
"\x0B\x0B\x0B",
|
||||
"\x0C\x0C\x0C",
|
||||
"\x4A\x4C\x41",
|
||||
"\x0E\x0E\x0E",
|
||||
"\x0F\x0F\x0F",
|
||||
"\x10\x10\x10",
|
||||
"\x11\x11\x11",
|
||||
"\x12\x12\x12",
|
||||
"\x13\x13\x13",
|
||||
"\x14\x14\x14",
|
||||
"\x15\x15\x15",
|
||||
"\x16\x16\x16",
|
||||
"\x17\x17\x17",
|
||||
"\x18\x18\x18",
|
||||
"\x19\x19\x19",
|
||||
"\x1A\x1A\x1A",
|
||||
"\x1B\x1B\x1B",
|
||||
"\x1C\x1C\x1C",
|
||||
"\x1D\x1D\x1D",
|
||||
"\x1E\x1E\x1E",
|
||||
"\x1F\x1F\x1F",
|
||||
"\x2E\x2D\x32",
|
||||
"\x47\x75\x30",
|
||||
"\x7A\x52\x21",
|
||||
"\x56\x60\x29",
|
||||
"\x42\x71\x5B",
|
||||
"\x6A\x5E\x38",
|
||||
"\x2F\x49\x33",
|
||||
"\x26\x5C\x3D",
|
||||
"\x49\x62\x58",
|
||||
"\x41\x7D\x3A",
|
||||
"\x34\x29\x35",
|
||||
"\x32\x36\x65",
|
||||
"\x5B\x20\x39",
|
||||
"\x76\x7C\x5C",
|
||||
"\x72\x7A\x56",
|
||||
"\x43\x7F\x73",
|
||||
"\x38\x6B\x66",
|
||||
"\x39\x63\x4E",
|
||||
"\x70\x33\x45",
|
||||
"\x45\x2B\x6B",
|
||||
"\x68\x68\x62",
|
||||
"\x71\x51\x59",
|
||||
"\x4F\x66\x78",
|
||||
"\x09\x76\x5E",
|
||||
"\x62\x31\x7D",
|
||||
"\x44\x64\x4A",
|
||||
"\x23\x54\x6D",
|
||||
"\x75\x43\x71",
|
||||
"\x4A\x4C\x41",
|
||||
"\x7E\x3A\x60",
|
||||
"\x4A\x4C\x41",
|
||||
"\x5E\x7E\x53",
|
||||
"\x40\x4C\x40",
|
||||
"\x77\x45\x42",
|
||||
"\x4A\x2C\x27",
|
||||
"\x61\x2A\x48",
|
||||
"\x5D\x74\x72",
|
||||
"\x22\x27\x75",
|
||||
"\x4B\x37\x31",
|
||||
"\x6F\x44\x37",
|
||||
"\x4E\x79\x4D",
|
||||
"\x3B\x59\x52",
|
||||
"\x4C\x2F\x22",
|
||||
"\x50\x6F\x54",
|
||||
"\x67\x26\x6A",
|
||||
"\x2A\x72\x47",
|
||||
"\x7D\x6A\x64",
|
||||
"\x74\x39\x2D",
|
||||
"\x54\x7B\x20",
|
||||
"\x2B\x3F\x7F",
|
||||
"\x2D\x38\x2E",
|
||||
"\x2C\x77\x4C",
|
||||
"\x30\x67\x5D",
|
||||
"\x6E\x53\x7E",
|
||||
"\x6B\x47\x6C",
|
||||
"\x66\x34\x6F",
|
||||
"\x35\x78\x79",
|
||||
"\x25\x5D\x74",
|
||||
"\x21\x30\x43",
|
||||
"\x64\x23\x26",
|
||||
"\x4D\x5A\x76",
|
||||
"\x52\x5B\x25",
|
||||
"\x63\x6C\x24",
|
||||
"\x3F\x48\x2B",
|
||||
"\x7B\x55\x28",
|
||||
"\x78\x70\x23",
|
||||
"\x29\x69\x41",
|
||||
"\x28\x2E\x34",
|
||||
"\x73\x4C\x09",
|
||||
"\x59\x21\x2A",
|
||||
"\x33\x24\x44",
|
||||
"\x7F\x4E\x3F",
|
||||
"\x6D\x50\x77",
|
||||
"\x55\x09\x3B",
|
||||
"\x53\x56\x55",
|
||||
"\x7C\x73\x69",
|
||||
"\x3A\x35\x61",
|
||||
"\x5F\x61\x63",
|
||||
"\x65\x4B\x50",
|
||||
"\x46\x58\x67",
|
||||
"\x58\x3B\x51",
|
||||
"\x31\x57\x49",
|
||||
"\x69\x22\x4F",
|
||||
"\x6C\x6D\x46",
|
||||
"\x5A\x4D\x68",
|
||||
"\x48\x25\x7C",
|
||||
"\x27\x28\x36",
|
||||
"\x5C\x46\x70",
|
||||
"\x3D\x4A\x6E",
|
||||
"\x24\x32\x7A",
|
||||
"\x79\x41\x2F",
|
||||
"\x37\x3D\x5F",
|
||||
"\x60\x5F\x4B",
|
||||
"\x51\x4F\x5A",
|
||||
"\x20\x42\x2C",
|
||||
"\x36\x65\x57"
|
||||
];
|
||||
|
||||
const D_COMBINATION = [
|
||||
0, 1, 2, 0, 1, 2, 1, 2, 2, 1, 2, 1, 0, 2, 1, 2, 0, 2, 1, 2, 0, 0, 1, 2, 2, 1, 0, 2, 1, 2, 2, 1,
|
||||
0, 0, 2, 1, 2, 1, 2, 0, 2, 0, 0, 1, 2, 0, 2, 1, 0, 2, 1, 2, 0, 0, 1, 2, 2, 0, 0, 1, 2, 0, 2, 1
|
||||
];
|
||||
|
||||
export default MicrosoftScriptDecoder;
|
@ -6,6 +6,7 @@
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Offset checker operation
|
||||
@ -48,7 +49,7 @@ class OffsetChecker extends Operation {
|
||||
chr;
|
||||
|
||||
if (!samples || samples.length < 2) {
|
||||
return "Not enough samples, perhaps you need to modify the sample delimiter or add more data?";
|
||||
throw new OperationError("Not enough samples, perhaps you need to modify the sample delimiter or add more data?");
|
||||
}
|
||||
|
||||
// Initialise output strings
|
||||
|
86
src/core/operations/PGPDecrypt.mjs
Normal file
86
src/core/operations/PGPDecrypt.mjs
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import kbpgp from "kbpgp";
|
||||
import { ASP, importPrivateKey } from "../lib/PGP";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import promisifyDefault from "es6-promisify";
|
||||
const promisify = promisifyDefault.promisify;
|
||||
|
||||
/**
|
||||
* PGP Decrypt operation
|
||||
*/
|
||||
class PGPDecrypt extends Operation {
|
||||
|
||||
/**
|
||||
* PGPDecrypt constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "PGP Decrypt";
|
||||
this.module = "PGP";
|
||||
this.description = [
|
||||
"Input: the ASCII-armoured PGP message you want to decrypt.",
|
||||
"<br><br>",
|
||||
"Arguments: the ASCII-armoured PGP private key of the recipient, ",
|
||||
"(and the private key password if necessary).",
|
||||
"<br><br>",
|
||||
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
|
||||
"<br><br>",
|
||||
"This function uses the Keybase implementation of PGP.",
|
||||
].join("\n");
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Private key of recipient",
|
||||
"type": "text",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "Private key passphrase",
|
||||
"type": "string",
|
||||
"value": ""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @throws {OperationError} if invalid private key
|
||||
*/
|
||||
async run(input, args) {
|
||||
const encryptedMessage = input,
|
||||
[privateKey, passphrase] = args,
|
||||
keyring = new kbpgp.keyring.KeyRing();
|
||||
let plaintextMessage;
|
||||
|
||||
if (!privateKey) throw new OperationError("Enter the private key of the recipient.");
|
||||
|
||||
const key = await importPrivateKey(privateKey, passphrase);
|
||||
keyring.add_key_manager(key);
|
||||
|
||||
try {
|
||||
plaintextMessage = await promisify(kbpgp.unbox)({
|
||||
armored: encryptedMessage,
|
||||
keyfetch: keyring,
|
||||
asp: ASP
|
||||
});
|
||||
} catch (err) {
|
||||
throw new OperationError(`Couldn't decrypt message with provided private key: ${err}`);
|
||||
}
|
||||
|
||||
return plaintextMessage.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default PGPDecrypt;
|
122
src/core/operations/PGPDecryptAndVerify.mjs
Normal file
122
src/core/operations/PGPDecryptAndVerify.mjs
Normal file
@ -0,0 +1,122 @@
|
||||
/**
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import kbpgp from "kbpgp";
|
||||
import { ASP, importPrivateKey, importPublicKey } from "../lib/PGP";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import promisifyDefault from "es6-promisify";
|
||||
const promisify = promisifyDefault.promisify;
|
||||
|
||||
/**
|
||||
* PGP Decrypt and Verify operation
|
||||
*/
|
||||
class PGPDecryptAndVerify extends Operation {
|
||||
|
||||
/**
|
||||
* PGPDecryptAndVerify constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "PGP Decrypt and Verify";
|
||||
this.module = "PGP";
|
||||
this.description = [
|
||||
"Input: the ASCII-armoured encrypted PGP message you want to verify.",
|
||||
"<br><br>",
|
||||
"Arguments: the ASCII-armoured PGP public key of the signer, ",
|
||||
"the ASCII-armoured private key of the recipient (and the private key password if necessary).",
|
||||
"<br><br>",
|
||||
"This operation uses PGP to decrypt and verify an encrypted digital signature.",
|
||||
"<br><br>",
|
||||
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
|
||||
"<br><br>",
|
||||
"This function uses the Keybase implementation of PGP.",
|
||||
].join("\n");
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Public key of signer",
|
||||
"type": "text",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "Private key of recipient",
|
||||
"type": "text",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "Private key password",
|
||||
"type": "string",
|
||||
"value": ""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const signedMessage = input,
|
||||
[publicKey, privateKey, passphrase] = args,
|
||||
keyring = new kbpgp.keyring.KeyRing();
|
||||
let unboxedLiterals;
|
||||
|
||||
if (!publicKey) throw new OperationError("Enter the public key of the signer.");
|
||||
if (!privateKey) throw new OperationError("Enter the private key of the recipient.");
|
||||
const privKey = await importPrivateKey(privateKey, passphrase);
|
||||
const pubKey = await importPublicKey(publicKey);
|
||||
keyring.add_key_manager(privKey);
|
||||
keyring.add_key_manager(pubKey);
|
||||
|
||||
try {
|
||||
unboxedLiterals = await promisify(kbpgp.unbox)({
|
||||
armored: signedMessage,
|
||||
keyfetch: keyring,
|
||||
asp: ASP
|
||||
});
|
||||
const ds = unboxedLiterals[0].get_data_signer();
|
||||
if (ds) {
|
||||
const km = ds.get_key_manager();
|
||||
if (km) {
|
||||
const signer = km.get_userids_mark_primary()[0].components;
|
||||
let text = "Signed by ";
|
||||
if (signer.email || signer.username || signer.comment) {
|
||||
if (signer.username) {
|
||||
text += `${signer.username} `;
|
||||
}
|
||||
if (signer.comment) {
|
||||
text += `${signer.comment} `;
|
||||
}
|
||||
if (signer.email) {
|
||||
text += `<${signer.email}>`;
|
||||
}
|
||||
text += "\n";
|
||||
}
|
||||
text += [
|
||||
`PGP fingerprint: ${km.get_pgp_fingerprint().toString("hex")}`,
|
||||
`Signed on ${new Date(ds.sig.hashed_subpackets[0].time * 1000).toUTCString()}`,
|
||||
"----------------------------------\n"
|
||||
].join("\n");
|
||||
text += unboxedLiterals.toString();
|
||||
return text.trim();
|
||||
} else {
|
||||
throw new OperationError("Could not identify a key manager.");
|
||||
}
|
||||
} else {
|
||||
throw new OperationError("The data does not appear to be signed.");
|
||||
}
|
||||
} catch (err) {
|
||||
throw new OperationError(`Couldn't verify message: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default PGPDecryptAndVerify;
|
85
src/core/operations/PGPEncrypt.mjs
Normal file
85
src/core/operations/PGPEncrypt.mjs
Normal file
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import kbpgp from "kbpgp";
|
||||
import { ASP } from "../lib/PGP";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import promisifyDefault from "es6-promisify";
|
||||
const promisify = promisifyDefault.promisify;
|
||||
|
||||
/**
|
||||
* PGP Encrypt operation
|
||||
*/
|
||||
class PGPEncrypt extends Operation {
|
||||
|
||||
/**
|
||||
* PGPEncrypt constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "PGP Encrypt";
|
||||
this.module = "PGP";
|
||||
this.description = [
|
||||
"Input: the message you want to encrypt.",
|
||||
"<br><br>",
|
||||
"Arguments: the ASCII-armoured PGP public key of the recipient.",
|
||||
"<br><br>",
|
||||
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
|
||||
"<br><br>",
|
||||
"This function uses the Keybase implementation of PGP.",
|
||||
].join("\n");
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Public key of recipient",
|
||||
"type": "text",
|
||||
"value": ""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @throws {OperationError} if failed private key import or failed encryption
|
||||
*/
|
||||
async run(input, args) {
|
||||
const plaintextMessage = input,
|
||||
plainPubKey = args[0];
|
||||
let key,
|
||||
encryptedMessage;
|
||||
|
||||
if (!plainPubKey) throw new OperationError("Enter the public key of the recipient.");
|
||||
|
||||
try {
|
||||
key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
||||
armored: plainPubKey,
|
||||
});
|
||||
} catch (err) {
|
||||
throw new OperationError(`Could not import public key: ${err}`);
|
||||
}
|
||||
|
||||
try {
|
||||
encryptedMessage = await promisify(kbpgp.box)({
|
||||
"msg": plaintextMessage,
|
||||
"encrypt_for": key,
|
||||
"asp": ASP
|
||||
});
|
||||
} catch (err) {
|
||||
throw new OperationError(`Couldn't encrypt message with provided public key: ${err}`);
|
||||
}
|
||||
|
||||
return encryptedMessage.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default PGPEncrypt;
|
93
src/core/operations/PGPEncryptAndSign.mjs
Normal file
93
src/core/operations/PGPEncryptAndSign.mjs
Normal file
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import kbpgp from "kbpgp";
|
||||
import { ASP, importPrivateKey, importPublicKey } from "../lib/PGP";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import promisifyDefault from "es6-promisify";
|
||||
const promisify = promisifyDefault.promisify;
|
||||
|
||||
/**
|
||||
* PGP Encrypt and Sign operation
|
||||
*/
|
||||
class PGPEncryptAndSign extends Operation {
|
||||
|
||||
/**
|
||||
* PGPEncryptAndSign constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "PGP Encrypt and Sign";
|
||||
this.module = "PGP";
|
||||
this.description = [
|
||||
"Input: the cleartext you want to sign.",
|
||||
"<br><br>",
|
||||
"Arguments: the ASCII-armoured private key of the signer (plus the private key password if necessary)",
|
||||
"and the ASCII-armoured PGP public key of the recipient.",
|
||||
"<br><br>",
|
||||
"This operation uses PGP to produce an encrypted digital signature.",
|
||||
"<br><br>",
|
||||
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
|
||||
"<br><br>",
|
||||
"This function uses the Keybase implementation of PGP.",
|
||||
].join("\n");
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Private key of signer",
|
||||
"type": "text",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "Private key passphrase",
|
||||
"type": "string",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "Public key of recipient",
|
||||
"type": "text",
|
||||
"value": ""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @throws {OperationError} if failure to sign message
|
||||
*/
|
||||
async run(input, args) {
|
||||
const message = input,
|
||||
[privateKey, passphrase, publicKey] = args;
|
||||
let signedMessage;
|
||||
|
||||
if (!privateKey) throw new OperationError("Enter the private key of the signer.");
|
||||
if (!publicKey) throw new OperationError("Enter the public key of the recipient.");
|
||||
const privKey = await importPrivateKey(privateKey, passphrase);
|
||||
const pubKey = await importPublicKey(publicKey);
|
||||
|
||||
try {
|
||||
signedMessage = await promisify(kbpgp.box)({
|
||||
"msg": message,
|
||||
"encrypt_for": pubKey,
|
||||
"sign_with": privKey,
|
||||
"asp": ASP
|
||||
});
|
||||
} catch (err) {
|
||||
throw new OperationError(`Couldn't sign message: ${err}`);
|
||||
}
|
||||
|
||||
return signedMessage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default PGPEncryptAndSign;
|
195
src/core/operations/ParseColourCode.mjs
Normal file
195
src/core/operations/ParseColourCode.mjs
Normal file
@ -0,0 +1,195 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
/**
|
||||
* Parse colour code operation
|
||||
*/
|
||||
class ParseColourCode extends Operation {
|
||||
|
||||
/**
|
||||
* ParseColourCode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Parse colour code";
|
||||
this.module = "Default";
|
||||
this.description = "Converts a colour code in a standard format to other standard formats and displays the colour itself.<br><br><strong>Example inputs</strong><ul><li><code>#d9edf7</code></li><li><code>rgba(217,237,247,1)</code></li><li><code>hsla(200,65%,91%,1)</code></li><li><code>cmyk(0.12, 0.04, 0.00, 0.03)</code></li></ul>";
|
||||
this.inputType = "string";
|
||||
this.outputType = "html";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
run(input, args) {
|
||||
let m = null,
|
||||
r = 0, g = 0, b = 0, a = 1;
|
||||
|
||||
// Read in the input
|
||||
if ((m = input.match(/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/i))) {
|
||||
// Hex - #d9edf7
|
||||
r = parseInt(m[1], 16);
|
||||
g = parseInt(m[2], 16);
|
||||
b = parseInt(m[3], 16);
|
||||
} else if ((m = input.match(/rgba?\((\d{1,3}(?:\.\d+)?),\s?(\d{1,3}(?:\.\d+)?),\s?(\d{1,3}(?:\.\d+)?)(?:,\s?(\d(?:\.\d+)?))?\)/i))) {
|
||||
// RGB or RGBA - rgb(217,237,247) or rgba(217,237,247,1)
|
||||
r = parseFloat(m[1]);
|
||||
g = parseFloat(m[2]);
|
||||
b = parseFloat(m[3]);
|
||||
a = m[4] ? parseFloat(m[4]) : 1;
|
||||
} else if ((m = input.match(/hsla?\((\d{1,3}(?:\.\d+)?),\s?(\d{1,3}(?:\.\d+)?)%,\s?(\d{1,3}(?:\.\d+)?)%(?:,\s?(\d(?:\.\d+)?))?\)/i))) {
|
||||
// HSL or HSLA - hsl(200, 65%, 91%) or hsla(200, 65%, 91%, 1)
|
||||
const h_ = parseFloat(m[1]) / 360,
|
||||
s_ = parseFloat(m[2]) / 100,
|
||||
l_ = parseFloat(m[3]) / 100,
|
||||
rgb_ = ParseColourCode._hslToRgb(h_, s_, l_);
|
||||
|
||||
r = rgb_[0];
|
||||
g = rgb_[1];
|
||||
b = rgb_[2];
|
||||
a = m[4] ? parseFloat(m[4]) : 1;
|
||||
} else if ((m = input.match(/cmyk\((\d(?:\.\d+)?),\s?(\d(?:\.\d+)?),\s?(\d(?:\.\d+)?),\s?(\d(?:\.\d+)?)\)/i))) {
|
||||
// CMYK - cmyk(0.12, 0.04, 0.00, 0.03)
|
||||
const c_ = parseFloat(m[1]),
|
||||
m_ = parseFloat(m[2]),
|
||||
y_ = parseFloat(m[3]),
|
||||
k_ = parseFloat(m[4]);
|
||||
|
||||
r = Math.round(255 * (1 - c_) * (1 - k_));
|
||||
g = Math.round(255 * (1 - m_) * (1 - k_));
|
||||
b = Math.round(255 * (1 - y_) * (1 - k_));
|
||||
}
|
||||
|
||||
const hsl_ = ParseColourCode._rgbToHsl(r, g, b),
|
||||
h = Math.round(hsl_[0] * 360),
|
||||
s = Math.round(hsl_[1] * 100),
|
||||
l = Math.round(hsl_[2] * 100);
|
||||
let k = 1 - Math.max(r/255, g/255, b/255),
|
||||
c = (1 - r/255 - k) / (1 - k),
|
||||
y = (1 - b/255 - k) / (1 - k);
|
||||
|
||||
m = (1 - g/255 - k) / (1 - k);
|
||||
|
||||
c = isNaN(c) ? "0" : c.toFixed(2);
|
||||
m = isNaN(m) ? "0" : m.toFixed(2);
|
||||
y = isNaN(y) ? "0" : y.toFixed(2);
|
||||
k = k.toFixed(2);
|
||||
|
||||
const hex = "#" +
|
||||
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 + "%)",
|
||||
hsla = "hsla(" + h + ", " + s + "%, " + l + "%, " + a + ")",
|
||||
cmyk = "cmyk(" + c + ", " + m + ", " + y + ", " + k + ")";
|
||||
|
||||
// Generate output
|
||||
return `<div id="colorpicker" style="display: inline-block"></div>
|
||||
Hex: ${hex}
|
||||
RGB: ${rgb}
|
||||
RGBA: ${rgba}
|
||||
HSL: ${hsl}
|
||||
HSLA: ${hsla}
|
||||
CMYK: ${cmyk}
|
||||
<script>
|
||||
$('#colorpicker').colorpicker({
|
||||
format: 'rgba',
|
||||
color: '${rgba}',
|
||||
container: true,
|
||||
inline: true,
|
||||
}).on('changeColor', function(e) {
|
||||
var color = e.color.toRGB();
|
||||
document.getElementById('input-text').value = 'rgba(' +
|
||||
color.r + ', ' + color.g + ', ' + color.b + ', ' + color.a + ')';
|
||||
window.app.autoBake();
|
||||
});
|
||||
</script>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an HSL color value to RGB. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_colorSpace.
|
||||
* Assumes h, s, and l are contained in the set [0, 1] and
|
||||
* returns r, g, and b in the set [0, 255].
|
||||
*
|
||||
* @author Mohsen (http://stackoverflow.com/a/9493060)
|
||||
*
|
||||
* @param {number} h - The hue
|
||||
* @param {number} s - The saturation
|
||||
* @param {number} l - The lightness
|
||||
* @return {Array} The RGB representation
|
||||
*/
|
||||
static _hslToRgb(h, s, l) {
|
||||
let r, g, b;
|
||||
|
||||
if (s === 0){
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
const hue2rgb = function hue2rgb(p, q, t) {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1/6) return p + (q - p) * 6 * t;
|
||||
if (t < 1/2) return q;
|
||||
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
||||
return p;
|
||||
};
|
||||
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1/3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1/3);
|
||||
}
|
||||
|
||||
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an RGB color value to HSL. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_colorSpace.
|
||||
* Assumes r, g, and b are contained in the set [0, 255] and
|
||||
* returns h, s, and l in the set [0, 1].
|
||||
*
|
||||
* @author Mohsen (http://stackoverflow.com/a/9493060)
|
||||
*
|
||||
* @param {number} r - The red color value
|
||||
* @param {number} g - The green color value
|
||||
* @param {number} b - The blue color value
|
||||
* @return {Array} The HSL representation
|
||||
*/
|
||||
static _rgbToHsl(r, g, b) {
|
||||
r /= 255; g /= 255; b /= 255;
|
||||
const max = Math.max(r, g, b),
|
||||
min = Math.min(r, g, b),
|
||||
l = (max + min) / 2;
|
||||
let h, s;
|
||||
|
||||
if (max === min) {
|
||||
h = s = 0; // achromatic
|
||||
} else {
|
||||
const d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
|
||||
return [h, s, l];
|
||||
}
|
||||
}
|
||||
|
||||
export default ParseColourCode;
|
@ -7,6 +7,7 @@
|
||||
import Operation from "../Operation";
|
||||
import moment from "moment-timezone";
|
||||
import {DATETIME_FORMATS, FORMAT_EXAMPLES} from "../lib/DateTime";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Parse DateTime operation
|
||||
@ -59,7 +60,7 @@ class ParseDateTime extends Operation {
|
||||
date = moment.tz(input, inputFormat, inputTimezone);
|
||||
if (!date || date.format() === "Invalid date") throw Error;
|
||||
} catch (err) {
|
||||
return "Invalid format.\n\n" + FORMAT_EXAMPLES;
|
||||
throw new OperationError(`Invalid format.\n\n${FORMAT_EXAMPLES}`);
|
||||
}
|
||||
|
||||
output += "Date: " + date.format("dddd Do MMMM YYYY") +
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Parse UNIX file permissions operation
|
||||
@ -169,7 +170,7 @@ class ParseUNIXFilePermissions extends Operation {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "Invalid input format.\nPlease enter the permissions in either octal (e.g. 755) or textual (e.g. drwxr-xr-x) format.";
|
||||
throw new OperationError("Invalid input format.\nPlease enter the permissions in either octal (e.g. 755) or textual (e.g. drwxr-xr-x) format.");
|
||||
}
|
||||
|
||||
output += "Textual representation: " + permsToStr(perms);
|
||||
|
69
src/core/operations/ParseURI.mjs
Normal file
69
src/core/operations/ParseURI.mjs
Normal file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import url from "url";
|
||||
|
||||
/**
|
||||
* Parse URI operation
|
||||
*/
|
||||
class ParseURI extends Operation {
|
||||
|
||||
/**
|
||||
* ParseURI constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Parse URI";
|
||||
this.module = "URL";
|
||||
this.description = "Pretty prints complicated Uniform Resource Identifier (URI) strings for ease of reading. Particularly useful for Uniform Resource Locators (URLs) with a lot of arguments.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const uri = url.parse(input, true);
|
||||
|
||||
let output = "";
|
||||
|
||||
if (uri.protocol) output += "Protocol:\t" + uri.protocol + "\n";
|
||||
if (uri.auth) output += "Auth:\t\t" + uri.auth + "\n";
|
||||
if (uri.hostname) output += "Hostname:\t" + uri.hostname + "\n";
|
||||
if (uri.port) output += "Port:\t\t" + uri.port + "\n";
|
||||
if (uri.pathname) output += "Path name:\t" + uri.pathname + "\n";
|
||||
if (uri.query) {
|
||||
const keys = Object.keys(uri.query);
|
||||
let padding = 0;
|
||||
|
||||
keys.forEach(k => {
|
||||
padding = (k.length > padding) ? k.length : padding;
|
||||
});
|
||||
|
||||
output += "Arguments:\n";
|
||||
for (const key in uri.query) {
|
||||
output += "\t" + key.padEnd(padding, " ");
|
||||
if (uri.query[key].length) {
|
||||
output += " = " + uri.query[key] + "\n";
|
||||
} else {
|
||||
output += "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uri.hash) output += "Hash:\t\t" + uri.hash + "\n";
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ParseURI;
|
@ -7,6 +7,7 @@
|
||||
import Operation from "../Operation";
|
||||
import {INFLATE_BUFFER_TYPE} from "../lib/Zlib";
|
||||
import rawinflate from "zlibjs/bin/rawinflate.min";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
const Zlib = rawinflate.Zlib;
|
||||
|
||||
@ -90,7 +91,7 @@ class RawInflate extends Operation {
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
throw "Error: Unable to inflate data";
|
||||
throw new OperationError("Error: Unable to inflate data");
|
||||
}
|
||||
}
|
||||
// This seems to be the easiest way...
|
||||
|
@ -7,6 +7,7 @@
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import {fromBase64, toBase64} from "../lib/Base64";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Show Base64 offsets operation
|
||||
@ -58,7 +59,7 @@ class ShowBase64Offsets extends Operation {
|
||||
script = "<script type='application/javascript'>$('[data-toggle=\"tooltip\"]').tooltip()</script>";
|
||||
|
||||
if (input.length < 1) {
|
||||
return "Please enter a string.";
|
||||
throw new OperationError("Please enter a string.");
|
||||
}
|
||||
|
||||
// Highlight offset 0
|
||||
|
116
src/core/operations/Strings.mjs
Normal file
116
src/core/operations/Strings.mjs
Normal file
@ -0,0 +1,116 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import XRegExp from "xregexp";
|
||||
import { search } from "../lib/Extract";
|
||||
|
||||
/**
|
||||
* Strings operation
|
||||
*/
|
||||
class Strings extends Operation {
|
||||
|
||||
/**
|
||||
* Strings constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Strings";
|
||||
this.module = "Regex";
|
||||
this.description = "Extracts all strings from the input.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Encoding",
|
||||
"type": "option",
|
||||
"value": ["Single byte", "16-bit littleendian", "16-bit bigendian", "All"]
|
||||
},
|
||||
{
|
||||
"name": "Minimum length",
|
||||
"type": "number",
|
||||
"value": 4
|
||||
},
|
||||
{
|
||||
"name": "Match",
|
||||
"type": "option",
|
||||
"value": [
|
||||
"[ASCII]", "Alphanumeric + punctuation (A)", "All printable chars (A)", "Null-terminated strings (A)",
|
||||
"[Unicode]", "Alphanumeric + punctuation (U)", "All printable chars (U)", "Null-terminated strings (U)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Display total",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [encoding, minLen, matchType, displayTotal] = args,
|
||||
alphanumeric = "A-Z\\d",
|
||||
punctuation = "/\\-:.,_$%'\"()<>= !\\[\\]{}@",
|
||||
printable = "\x20-\x7e",
|
||||
uniAlphanumeric = "\\pL\\pN",
|
||||
uniPunctuation = "\\pP\\pZ",
|
||||
uniPrintable = "\\pL\\pM\\pZ\\pS\\pN\\pP";
|
||||
|
||||
let strings = "";
|
||||
|
||||
switch (matchType) {
|
||||
case "Alphanumeric + punctuation (A)":
|
||||
strings = `[${alphanumeric + punctuation}]`;
|
||||
break;
|
||||
case "All printable chars (A)":
|
||||
case "Null-terminated strings (A)":
|
||||
strings = `[${printable}]`;
|
||||
break;
|
||||
case "Alphanumeric + punctuation (U)":
|
||||
strings = `[${uniAlphanumeric + uniPunctuation}]`;
|
||||
break;
|
||||
case "All printable chars (U)":
|
||||
case "Null-terminated strings (U)":
|
||||
strings = `[${uniPrintable}]`;
|
||||
break;
|
||||
}
|
||||
|
||||
// UTF-16 support is hacked in by allowing null bytes on either side of the matched chars
|
||||
switch (encoding) {
|
||||
case "All":
|
||||
strings = `(\x00?${strings}\x00?)`;
|
||||
break;
|
||||
case "16-bit littleendian":
|
||||
strings = `(${strings}\x00)`;
|
||||
break;
|
||||
case "16-bit bigendian":
|
||||
strings = `(\x00${strings})`;
|
||||
break;
|
||||
case "Single byte":
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
strings = `${strings}{${minLen},}`;
|
||||
|
||||
if (matchType.includes("Null-terminated")) {
|
||||
strings += "\x00";
|
||||
}
|
||||
|
||||
const regex = new XRegExp(strings, "ig");
|
||||
|
||||
return search(input, regex, null, displayTotal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Strings;
|
65
src/core/operations/StripHTMLTags.mjs
Normal file
65
src/core/operations/StripHTMLTags.mjs
Normal file
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
|
||||
/**
|
||||
* Strip HTML tags operation
|
||||
*/
|
||||
class StripHTMLTags extends Operation {
|
||||
|
||||
/**
|
||||
* StripHTMLTags constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Strip HTML tags";
|
||||
this.module = "Default";
|
||||
this.description = "Removes all HTML tags from the input.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Remove indentation",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "Remove excess line breaks",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [removeIndentation, removeLineBreaks] = args;
|
||||
|
||||
input = Utils.stripHtmlTags(input);
|
||||
|
||||
if (removeIndentation) {
|
||||
input = input.replace(/\n[ \f\t]+/g, "\n");
|
||||
}
|
||||
|
||||
if (removeLineBreaks) {
|
||||
input = input
|
||||
.replace(/^\s*\n/, "") // first line
|
||||
.replace(/(\n\s*){2,}/g, "\n"); // all others
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default StripHTMLTags;
|
137
src/core/operations/SwapEndianness.mjs
Normal file
137
src/core/operations/SwapEndianness.mjs
Normal file
@ -0,0 +1,137 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import {toHex, fromHex} from "../lib/Hex";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Swap endianness operation
|
||||
*/
|
||||
class SwapEndianness extends Operation {
|
||||
|
||||
/**
|
||||
* SwapEndianness constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Swap endianness";
|
||||
this.module = "Default";
|
||||
this.description = "Switches the data from big-endian to little-endian or vice-versa. Data can be read in as hexadecimal or raw bytes. It will be returned in the same format as it is entered.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Data format",
|
||||
"type": "option",
|
||||
"value": ["Hex", "Raw"]
|
||||
},
|
||||
{
|
||||
"name": "Word length (bytes)",
|
||||
"type": "number",
|
||||
"value": 4
|
||||
},
|
||||
{
|
||||
"name": "Pad incomplete words",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [dataFormat, wordLength, padIncompleteWords] = args,
|
||||
result = [],
|
||||
words = [];
|
||||
let i = 0,
|
||||
j = 0,
|
||||
data = [];
|
||||
|
||||
if (wordLength <= 0) {
|
||||
throw new OperationError("Word length must be greater than 0");
|
||||
}
|
||||
|
||||
// Convert input to raw data based on specified data format
|
||||
switch (dataFormat) {
|
||||
case "Hex":
|
||||
data = fromHex(input);
|
||||
break;
|
||||
case "Raw":
|
||||
data = Utils.strToByteArray(input);
|
||||
break;
|
||||
default:
|
||||
data = input;
|
||||
}
|
||||
|
||||
// Split up into words
|
||||
for (i = 0; i < data.length; i += wordLength) {
|
||||
const word = data.slice(i, i + wordLength);
|
||||
|
||||
// Pad word if too short
|
||||
if (padIncompleteWords && word.length < wordLength){
|
||||
for (j = word.length; j < wordLength; j++) {
|
||||
word.push(0);
|
||||
}
|
||||
}
|
||||
|
||||
words.push(word);
|
||||
}
|
||||
|
||||
// Swap endianness and flatten
|
||||
for (i = 0; i < words.length; i++) {
|
||||
j = words[i].length;
|
||||
while (j--) {
|
||||
result.push(words[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert data back to specified data format
|
||||
switch (dataFormat) {
|
||||
case "Hex":
|
||||
return toHex(result);
|
||||
case "Raw":
|
||||
return Utils.byteArrayToUtf8(result);
|
||||
default:
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight Swap endianness
|
||||
*
|
||||
* @param {Object[]} pos
|
||||
* @param {number} pos[].start
|
||||
* @param {number} pos[].end
|
||||
* @param {Object[]} args
|
||||
* @returns {Object[]} pos
|
||||
*/
|
||||
highlight(pos, args) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight Swap endianness in reverse
|
||||
*
|
||||
* @param {Object[]} pos
|
||||
* @param {number} pos[].start
|
||||
* @param {number} pos[].end
|
||||
* @param {Object[]} args
|
||||
* @returns {Object[]} pos
|
||||
*/
|
||||
highlightReverse(pos, args) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default SwapEndianness;
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Take bytes operation
|
||||
@ -45,6 +46,8 @@ class TakeBytes extends Operation {
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {ArrayBuffer}
|
||||
*
|
||||
* @throws {OperationError} if invalid value
|
||||
*/
|
||||
run(input, args) {
|
||||
const start = args[0],
|
||||
@ -52,7 +55,7 @@ class TakeBytes extends Operation {
|
||||
applyToEachLine = args[2];
|
||||
|
||||
if (start < 0 || length < 0)
|
||||
throw "Error: Invalid value";
|
||||
throw new OperationError("Error: Invalid value");
|
||||
|
||||
if (!applyToEachLine)
|
||||
return input.slice(start, start+length);
|
||||
|
141
src/core/operations/ToBCD.mjs
Normal file
141
src/core/operations/ToBCD.mjs
Normal file
@ -0,0 +1,141 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import {ENCODING_SCHEME, ENCODING_LOOKUP, FORMAT} from "../lib/BCD";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
/**
|
||||
* To BCD operation
|
||||
*/
|
||||
class ToBCD extends Operation {
|
||||
|
||||
/**
|
||||
* ToBCD constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "To BCD";
|
||||
this.module = "Default";
|
||||
this.description = "Binary-Coded Decimal (BCD) is a class of binary encodings of decimal numbers where each decimal digit is represented by a fixed number of bits, usually four or eight. Special bit patterns are sometimes used for a sign";
|
||||
this.inputType = "BigNumber";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Scheme",
|
||||
"type": "option",
|
||||
"value": ENCODING_SCHEME
|
||||
},
|
||||
{
|
||||
"name": "Packed",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "Signed",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "Output format",
|
||||
"type": "option",
|
||||
"value": FORMAT
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
if (input.isNaN())
|
||||
throw new OperationError("Invalid input");
|
||||
if (!input.integerValue(BigNumber.ROUND_DOWN).isEqualTo(input))
|
||||
throw new OperationError("Fractional values are not supported by BCD");
|
||||
|
||||
const encoding = ENCODING_LOOKUP[args[0]],
|
||||
packed = args[1],
|
||||
signed = args[2],
|
||||
outputFormat = args[3];
|
||||
|
||||
// Split input number up into separate digits
|
||||
const digits = input.toFixed().split("");
|
||||
|
||||
if (digits[0] === "-" || digits[0] === "+") {
|
||||
digits.shift();
|
||||
}
|
||||
|
||||
let nibbles = [];
|
||||
|
||||
digits.forEach(d => {
|
||||
const n = parseInt(d, 10);
|
||||
nibbles.push(encoding[n]);
|
||||
});
|
||||
|
||||
if (signed) {
|
||||
if (packed && digits.length % 2 === 0) {
|
||||
// If there are an even number of digits, we add a leading 0 so
|
||||
// that the sign nibble doesn't sit in its own byte, leading to
|
||||
// ambiguity around whether the number ends with a 0 or not.
|
||||
nibbles.unshift(encoding[0]);
|
||||
}
|
||||
|
||||
nibbles.push(input > 0 ? 12 : 13);
|
||||
// 12 ("C") for + (credit)
|
||||
// 13 ("D") for - (debit)
|
||||
}
|
||||
|
||||
let bytes = [];
|
||||
|
||||
if (packed) {
|
||||
let encoded = 0,
|
||||
little = false;
|
||||
|
||||
nibbles.forEach(n => {
|
||||
encoded ^= little ? n : (n << 4);
|
||||
if (little) {
|
||||
bytes.push(encoded);
|
||||
encoded = 0;
|
||||
}
|
||||
little = !little;
|
||||
});
|
||||
|
||||
if (little) bytes.push(encoded);
|
||||
} else {
|
||||
bytes = nibbles;
|
||||
|
||||
// Add null high nibbles
|
||||
nibbles = nibbles.map(n => {
|
||||
return [0, n];
|
||||
}).reduce((a, b) => {
|
||||
return a.concat(b);
|
||||
});
|
||||
}
|
||||
|
||||
// Output
|
||||
switch (outputFormat) {
|
||||
case "Nibbles":
|
||||
return nibbles.map(n => {
|
||||
return n.toString(2).padStart(4, "0");
|
||||
}).join(" ");
|
||||
case "Bytes":
|
||||
return bytes.map(b => {
|
||||
return b.toString(2).padStart(8, "0");
|
||||
}).join(" ");
|
||||
case "Raw":
|
||||
default:
|
||||
return Utils.byteArrayToChars(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ToBCD;
|
53
src/core/operations/ToBase.mjs
Normal file
53
src/core/operations/ToBase.mjs
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* To Base operation
|
||||
*/
|
||||
class ToBase extends Operation {
|
||||
|
||||
/**
|
||||
* ToBase constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "To Base";
|
||||
this.module = "Default";
|
||||
this.description = "Converts a decimal number to a given numerical base.";
|
||||
this.inputType = "BigNumber";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Radix",
|
||||
"type": "number",
|
||||
"value": 36
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
if (!input) {
|
||||
throw new OperationError("Error: Input must be a number");
|
||||
}
|
||||
const radix = args[0];
|
||||
if (radix < 2 || radix > 36) {
|
||||
throw new OperationError("Error: Radix argument must be between 2 and 36");
|
||||
}
|
||||
return input.toString(radix);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ToBase;
|
85
src/core/operations/ToBase58.mjs
Normal file
85
src/core/operations/ToBase58.mjs
Normal file
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import {ALPHABET_OPTIONS} from "../lib/Base58";
|
||||
|
||||
/**
|
||||
* To Base58 operation
|
||||
*/
|
||||
class ToBase58 extends Operation {
|
||||
|
||||
/**
|
||||
* ToBase58 constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "To Base58";
|
||||
this.module = "Default";
|
||||
this.description = "Base58 (similar to Base64) is a notation for encoding arbitrary byte data. It differs from Base64 by removing easily misread characters (i.e. l, I, 0 and O) to improve human readability.<br><br>This operation encodes data in an ASCII string (with an alphabet of your choosing, presets included).<br><br>e.g. <code>hello world</code> becomes <code>StV1DL6CwTryKyV</code><br><br>Base58 is commonly used in cryptocurrencies (Bitcoin, Ripple, etc).";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Alphabet",
|
||||
"type": "editableOption",
|
||||
"value": ALPHABET_OPTIONS
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
let alphabet = args[0] || ALPHABET_OPTIONS[0].value,
|
||||
result = [0];
|
||||
|
||||
alphabet = Utils.expandAlphRange(alphabet).join("");
|
||||
|
||||
if (alphabet.length !== 58 ||
|
||||
[].unique.call(alphabet).length !== 58) {
|
||||
throw new OperationError("Error: alphabet must be of length 58");
|
||||
}
|
||||
|
||||
if (input.length === 0) return "";
|
||||
|
||||
input.forEach(function(b) {
|
||||
let carry = (result[0] << 8) + b;
|
||||
result[0] = carry % 58;
|
||||
carry = (carry / 58) | 0;
|
||||
|
||||
for (let i = 1; i < result.length; i++) {
|
||||
carry += result[i] << 8;
|
||||
result[i] = carry % 58;
|
||||
carry = (carry / 58) | 0;
|
||||
}
|
||||
|
||||
while (carry > 0) {
|
||||
result.push(carry % 58);
|
||||
carry = (carry / 58) | 0;
|
||||
}
|
||||
});
|
||||
|
||||
result = result.map(function(b) {
|
||||
return alphabet[b];
|
||||
}).reverse().join("");
|
||||
|
||||
while (result.length < input.length) {
|
||||
result = alphabet[0] + result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ToBase58;
|
@ -7,6 +7,7 @@
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import {DELIM_OPTIONS} from "../lib/Delim";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* To Charcode operation
|
||||
@ -42,6 +43,8 @@ class ToCharcode extends Operation {
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @throws {OperationError} if base argument out of range
|
||||
*/
|
||||
run(input, args) {
|
||||
const delim = Utils.charRep(args[0] || "Space"),
|
||||
@ -51,7 +54,7 @@ class ToCharcode extends Operation {
|
||||
ordinal;
|
||||
|
||||
if (base < 2 || base > 36) {
|
||||
throw "Error: Base argument must be between 2 and 36";
|
||||
throw new OperationError("Error: Base argument must be between 2 and 36");
|
||||
}
|
||||
|
||||
const charcode = Utils.strToCharcode(input);
|
||||
|
345
src/core/operations/ToHTMLEntity.mjs
Normal file
345
src/core/operations/ToHTMLEntity.mjs
Normal file
@ -0,0 +1,345 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
|
||||
/**
|
||||
* To HTML Entity operation
|
||||
*/
|
||||
class ToHTMLEntity extends Operation {
|
||||
|
||||
/**
|
||||
* ToHTMLEntity constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "To HTML Entity";
|
||||
this.module = "Default";
|
||||
this.description = "Converts characters to HTML entities<br><br>e.g. <code>&</code> becomes <code>&<span>amp;</span></code>";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Convert all characters",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "Convert to",
|
||||
"type": "option",
|
||||
"value": ["Named entities where possible", "Numeric entities", "Hex entities"]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const convertAll = args[0],
|
||||
numeric = args[1] === "Numeric entities",
|
||||
hexa = args[1] === "Hex entities";
|
||||
|
||||
const charcodes = Utils.strToCharcode(input);
|
||||
let output = "";
|
||||
|
||||
for (let i = 0; i < charcodes.length; i++) {
|
||||
if (convertAll && numeric) {
|
||||
output += "&#" + charcodes[i] + ";";
|
||||
} else if (convertAll && hexa) {
|
||||
output += "&#x" + Utils.hex(charcodes[i]) + ";";
|
||||
} else if (convertAll) {
|
||||
output += byteToEntity[charcodes[i]] || "&#" + charcodes[i] + ";";
|
||||
} else if (numeric) {
|
||||
if (charcodes[i] > 255 || byteToEntity.hasOwnProperty(charcodes[i])) {
|
||||
output += "&#" + charcodes[i] + ";";
|
||||
} else {
|
||||
output += Utils.chr(charcodes[i]);
|
||||
}
|
||||
} else if (hexa) {
|
||||
if (charcodes[i] > 255 || byteToEntity.hasOwnProperty(charcodes[i])) {
|
||||
output += "&#x" + Utils.hex(charcodes[i]) + ";";
|
||||
} else {
|
||||
output += Utils.chr(charcodes[i]);
|
||||
}
|
||||
} else {
|
||||
output += byteToEntity[charcodes[i]] || (
|
||||
charcodes[i] > 255 ?
|
||||
"&#" + charcodes[i] + ";" :
|
||||
Utils.chr(charcodes[i])
|
||||
);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup table to translate byte values to their HTML entity codes.
|
||||
*/
|
||||
const byteToEntity = {
|
||||
34: """,
|
||||
38: "&",
|
||||
39: "'",
|
||||
60: "<",
|
||||
62: ">",
|
||||
160: " ",
|
||||
161: "¡",
|
||||
162: "¢",
|
||||
163: "£",
|
||||
164: "¤",
|
||||
165: "¥",
|
||||
166: "¦",
|
||||
167: "§",
|
||||
168: "¨",
|
||||
169: "©",
|
||||
170: "ª",
|
||||
171: "«",
|
||||
172: "¬",
|
||||
173: "­",
|
||||
174: "®",
|
||||
175: "¯",
|
||||
176: "°",
|
||||
177: "±",
|
||||
178: "²",
|
||||
179: "³",
|
||||
180: "´",
|
||||
181: "µ",
|
||||
182: "¶",
|
||||
183: "·",
|
||||
184: "¸",
|
||||
185: "¹",
|
||||
186: "º",
|
||||
187: "»",
|
||||
188: "¼",
|
||||
189: "½",
|
||||
190: "¾",
|
||||
191: "¿",
|
||||
192: "À",
|
||||
193: "Á",
|
||||
194: "Â",
|
||||
195: "Ã",
|
||||
196: "Ä",
|
||||
197: "Å",
|
||||
198: "Æ",
|
||||
199: "Ç",
|
||||
200: "È",
|
||||
201: "É",
|
||||
202: "Ê",
|
||||
203: "Ë",
|
||||
204: "Ì",
|
||||
205: "Í",
|
||||
206: "Î",
|
||||
207: "Ï",
|
||||
208: "Ð",
|
||||
209: "Ñ",
|
||||
210: "Ò",
|
||||
211: "Ó",
|
||||
212: "Ô",
|
||||
213: "Õ",
|
||||
214: "Ö",
|
||||
215: "×",
|
||||
216: "Ø",
|
||||
217: "Ù",
|
||||
218: "Ú",
|
||||
219: "Û",
|
||||
220: "Ü",
|
||||
221: "Ý",
|
||||
222: "Þ",
|
||||
223: "ß",
|
||||
224: "à",
|
||||
225: "á",
|
||||
226: "â",
|
||||
227: "ã",
|
||||
228: "ä",
|
||||
229: "å",
|
||||
230: "æ",
|
||||
231: "ç",
|
||||
232: "è",
|
||||
233: "é",
|
||||
234: "ê",
|
||||
235: "ë",
|
||||
236: "ì",
|
||||
237: "í",
|
||||
238: "î",
|
||||
239: "ï",
|
||||
240: "ð",
|
||||
241: "ñ",
|
||||
242: "ò",
|
||||
243: "ó",
|
||||
244: "ô",
|
||||
245: "õ",
|
||||
246: "ö",
|
||||
247: "÷",
|
||||
248: "ø",
|
||||
249: "ù",
|
||||
250: "ú",
|
||||
251: "û",
|
||||
252: "ü",
|
||||
253: "ý",
|
||||
254: "þ",
|
||||
255: "ÿ",
|
||||
338: "Œ",
|
||||
339: "œ",
|
||||
352: "Š",
|
||||
353: "š",
|
||||
376: "Ÿ",
|
||||
402: "ƒ",
|
||||
710: "ˆ",
|
||||
732: "˜",
|
||||
913: "Α",
|
||||
914: "Β",
|
||||
915: "Γ",
|
||||
916: "Δ",
|
||||
917: "Ε",
|
||||
918: "Ζ",
|
||||
919: "Η",
|
||||
920: "Θ",
|
||||
921: "Ι",
|
||||
922: "Κ",
|
||||
923: "Λ",
|
||||
924: "Μ",
|
||||
925: "Ν",
|
||||
926: "Ξ",
|
||||
927: "Ο",
|
||||
928: "Π",
|
||||
929: "Ρ",
|
||||
931: "Σ",
|
||||
932: "Τ",
|
||||
933: "Υ",
|
||||
934: "Φ",
|
||||
935: "Χ",
|
||||
936: "Ψ",
|
||||
937: "Ω",
|
||||
945: "α",
|
||||
946: "β",
|
||||
947: "γ",
|
||||
948: "δ",
|
||||
949: "ε",
|
||||
950: "ζ",
|
||||
951: "η",
|
||||
952: "θ",
|
||||
953: "ι",
|
||||
954: "κ",
|
||||
955: "λ",
|
||||
956: "μ",
|
||||
957: "ν",
|
||||
958: "ξ",
|
||||
959: "ο",
|
||||
960: "π",
|
||||
961: "ρ",
|
||||
962: "ς",
|
||||
963: "σ",
|
||||
964: "τ",
|
||||
965: "υ",
|
||||
966: "φ",
|
||||
967: "χ",
|
||||
968: "ψ",
|
||||
969: "ω",
|
||||
977: "ϑ",
|
||||
978: "ϒ",
|
||||
982: "ϖ",
|
||||
8194: " ",
|
||||
8195: " ",
|
||||
8201: " ",
|
||||
8204: "‌",
|
||||
8205: "‍",
|
||||
8206: "‎",
|
||||
8207: "‏",
|
||||
8211: "–",
|
||||
8212: "—",
|
||||
8216: "‘",
|
||||
8217: "’",
|
||||
8218: "‚",
|
||||
8220: "“",
|
||||
8221: "”",
|
||||
8222: "„",
|
||||
8224: "†",
|
||||
8225: "‡",
|
||||
8226: "•",
|
||||
8230: "…",
|
||||
8240: "‰",
|
||||
8242: "′",
|
||||
8243: "″",
|
||||
8249: "‹",
|
||||
8250: "›",
|
||||
8254: "‾",
|
||||
8260: "⁄",
|
||||
8364: "€",
|
||||
8465: "ℑ",
|
||||
8472: "℘",
|
||||
8476: "ℜ",
|
||||
8482: "™",
|
||||
8501: "ℵ",
|
||||
8592: "←",
|
||||
8593: "↑",
|
||||
8594: "→",
|
||||
8595: "↓",
|
||||
8596: "↔",
|
||||
8629: "↵",
|
||||
8656: "⇐",
|
||||
8657: "⇑",
|
||||
8658: "⇒",
|
||||
8659: "⇓",
|
||||
8660: "⇔",
|
||||
8704: "∀",
|
||||
8706: "∂",
|
||||
8707: "∃",
|
||||
8709: "∅",
|
||||
8711: "∇",
|
||||
8712: "∈",
|
||||
8713: "∉",
|
||||
8715: "∋",
|
||||
8719: "∏",
|
||||
8721: "∑",
|
||||
8722: "−",
|
||||
8727: "∗",
|
||||
8730: "√",
|
||||
8733: "∝",
|
||||
8734: "∞",
|
||||
8736: "∠",
|
||||
8743: "∧",
|
||||
8744: "∨",
|
||||
8745: "∩",
|
||||
8746: "∪",
|
||||
8747: "∫",
|
||||
8756: "∴",
|
||||
8764: "∼",
|
||||
8773: "≅",
|
||||
8776: "≈",
|
||||
8800: "≠",
|
||||
8801: "≡",
|
||||
8804: "≤",
|
||||
8805: "≥",
|
||||
8834: "⊂",
|
||||
8835: "⊃",
|
||||
8836: "⊄",
|
||||
8838: "⊆",
|
||||
8839: "⊇",
|
||||
8853: "⊕",
|
||||
8855: "⊗",
|
||||
8869: "⊥",
|
||||
8901: "⋅",
|
||||
8942: "⋮",
|
||||
8968: "⌈",
|
||||
8969: "⌉",
|
||||
8970: "⌊",
|
||||
8971: "⌋",
|
||||
9001: "⟨",
|
||||
9002: "⟩",
|
||||
9674: "◊",
|
||||
9824: "♠",
|
||||
9827: "♣",
|
||||
9829: "♥",
|
||||
9830: "♦",
|
||||
};
|
||||
|
||||
export default ToHTMLEntity;
|
244
src/core/operations/ToQuotedPrintable.mjs
Normal file
244
src/core/operations/ToQuotedPrintable.mjs
Normal file
@ -0,0 +1,244 @@
|
||||
/**
|
||||
* Some parts taken from mimelib (http://github.com/andris9/mimelib)
|
||||
* @author Andris Reinman
|
||||
* @license MIT
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
/**
|
||||
* To Quoted Printable operation
|
||||
*/
|
||||
class ToQuotedPrintable extends Operation {
|
||||
|
||||
/**
|
||||
* ToQuotedPrintable constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "To Quoted Printable";
|
||||
this.module = "Default";
|
||||
this.description = "Quoted-Printable, or QP encoding, is an encoding using printable ASCII characters (alphanumeric and the equals sign '=') to transmit 8-bit data over a 7-bit data path or, generally, over a medium which is not 8-bit clean. It is defined as a MIME content transfer encoding for use in e-mail.<br><br>QP works by using the equals sign '=' as an escape character. It also limits line length to 76, as some software has limits on line length.";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
let mimeEncodedStr = this.mimeEncode(input);
|
||||
|
||||
// fix line breaks
|
||||
mimeEncodedStr = mimeEncodedStr.replace(/\r?\n|\r/g, function() {
|
||||
return "\r\n";
|
||||
}).replace(/[\t ]+$/gm, function(spaces) {
|
||||
return spaces.replace(/ /g, "=20").replace(/\t/g, "=09");
|
||||
});
|
||||
|
||||
return this._addSoftLinebreaks(mimeEncodedStr, "qp");
|
||||
}
|
||||
|
||||
|
||||
/** @license
|
||||
========================================================================
|
||||
mimelib: http://github.com/andris9/mimelib
|
||||
Copyright (c) 2011-2012 Andris Reinman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Encodes mime data.
|
||||
*
|
||||
* @param {byteArray} buffer
|
||||
* @returns {string}
|
||||
*/
|
||||
mimeEncode(buffer) {
|
||||
const ranges = [
|
||||
[0x09],
|
||||
[0x0A],
|
||||
[0x0D],
|
||||
[0x20],
|
||||
[0x21],
|
||||
[0x23, 0x3C],
|
||||
[0x3E],
|
||||
[0x40, 0x5E],
|
||||
[0x60, 0x7E]
|
||||
];
|
||||
let result = "";
|
||||
|
||||
for (let i = 0, len = buffer.length; i < len; i++) {
|
||||
if (this._checkRanges(buffer[i], ranges)) {
|
||||
result += String.fromCharCode(buffer[i]);
|
||||
continue;
|
||||
}
|
||||
result += "=" + (buffer[i] < 0x10 ? "0" : "") + buffer[i].toString(16).toUpperCase();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given number falls within a given set of ranges.
|
||||
*
|
||||
* @private
|
||||
* @param {number} nr
|
||||
* @param {byteArray[]} ranges
|
||||
* @returns {bolean}
|
||||
*/
|
||||
_checkRanges(nr, ranges) {
|
||||
for (let i = ranges.length - 1; i >= 0; i--) {
|
||||
if (!ranges[i].length)
|
||||
continue;
|
||||
if (ranges[i].length === 1 && nr === ranges[i][0])
|
||||
return true;
|
||||
if (ranges[i].length === 2 && nr >= ranges[i][0] && nr <= ranges[i][1])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds soft line breaks to a string.
|
||||
* Lines can't be longer that 76 + <CR><LF> = 78 bytes
|
||||
* http://tools.ietf.org/html/rfc2045#section-6.7
|
||||
*
|
||||
* @private
|
||||
* @param {string} str
|
||||
* @param {string} encoding
|
||||
* @returns {string}
|
||||
*/
|
||||
_addSoftLinebreaks(str, encoding) {
|
||||
const lineLengthMax = 76;
|
||||
|
||||
encoding = (encoding || "base64").toString().toLowerCase().trim();
|
||||
|
||||
if (encoding === "qp") {
|
||||
return this._addQPSoftLinebreaks(str, lineLengthMax);
|
||||
} else {
|
||||
return this._addBase64SoftLinebreaks(str, lineLengthMax);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds soft line breaks to a base64 string.
|
||||
*
|
||||
* @private
|
||||
* @param {string} base64EncodedStr
|
||||
* @param {number} lineLengthMax
|
||||
* @returns {string}
|
||||
*/
|
||||
_addBase64SoftLinebreaks(base64EncodedStr, lineLengthMax) {
|
||||
base64EncodedStr = (base64EncodedStr || "").toString().trim();
|
||||
return base64EncodedStr.replace(new RegExp(".{" + lineLengthMax + "}", "g"), "$&\r\n").trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds soft line breaks to a quoted printable string.
|
||||
*
|
||||
* @private
|
||||
* @param {string} mimeEncodedStr
|
||||
* @param {number} lineLengthMax
|
||||
* @returns {string}
|
||||
*/
|
||||
_addQPSoftLinebreaks(mimeEncodedStr, lineLengthMax) {
|
||||
const len = mimeEncodedStr.length,
|
||||
lineMargin = Math.floor(lineLengthMax / 3);
|
||||
let pos = 0,
|
||||
match, code, line,
|
||||
result = "";
|
||||
|
||||
// insert soft linebreaks where needed
|
||||
while (pos < len) {
|
||||
line = mimeEncodedStr.substr(pos, lineLengthMax);
|
||||
if ((match = line.match(/\r\n/))) {
|
||||
line = line.substr(0, match.index + match[0].length);
|
||||
result += line;
|
||||
pos += line.length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.substr(-1) === "\n") {
|
||||
// nothing to change here
|
||||
result += line;
|
||||
pos += line.length;
|
||||
continue;
|
||||
} else if ((match = line.substr(-lineMargin).match(/\n.*?$/))) {
|
||||
// truncate to nearest line break
|
||||
line = line.substr(0, line.length - (match[0].length - 1));
|
||||
result += line;
|
||||
pos += line.length;
|
||||
continue;
|
||||
} else if (line.length > lineLengthMax - lineMargin && (match = line.substr(-lineMargin).match(/[ \t.,!?][^ \t.,!?]*$/))) {
|
||||
// truncate to nearest space
|
||||
line = line.substr(0, line.length - (match[0].length - 1));
|
||||
} else if (line.substr(-1) === "\r") {
|
||||
line = line.substr(0, line.length - 1);
|
||||
} else {
|
||||
if (line.match(/=[\da-f]{0,2}$/i)) {
|
||||
|
||||
// push incomplete encoding sequences to the next line
|
||||
if ((match = line.match(/=[\da-f]{0,1}$/i))) {
|
||||
line = line.substr(0, line.length - match[0].length);
|
||||
}
|
||||
|
||||
// ensure that utf-8 sequences are not split
|
||||
while (line.length > 3 && line.length < len - pos && !line.match(/^(?:=[\da-f]{2}){1,4}$/i) && (match = line.match(/=[\da-f]{2}$/ig))) {
|
||||
code = parseInt(match[0].substr(1, 2), 16);
|
||||
if (code < 128) {
|
||||
break;
|
||||
}
|
||||
|
||||
line = line.substr(0, line.length - 3);
|
||||
|
||||
if (code >= 0xC0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (pos + line.length < len && line.substr(-1) !== "\n") {
|
||||
if (line.length === 76 && line.match(/=[\da-f]{2}$/i)) {
|
||||
line = line.substr(0, line.length - 3);
|
||||
} else if (line.length === 76) {
|
||||
line = line.substr(0, line.length - 1);
|
||||
}
|
||||
pos += line.length;
|
||||
line += "=\r\n";
|
||||
} else {
|
||||
pos += line.length;
|
||||
}
|
||||
|
||||
result += line;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ToQuotedPrintable;
|
@ -7,6 +7,7 @@
|
||||
import Operation from "../Operation";
|
||||
import moment from "moment-timezone";
|
||||
import {UNITS} from "../lib/DateTime";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* To UNIX Timestamp operation
|
||||
@ -47,6 +48,8 @@ class ToUNIXTimestamp extends Operation {
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @throws {OperationError} if unit unrecognised
|
||||
*/
|
||||
run(input, args) {
|
||||
const [units, treatAsUTC, showDateTime] = args,
|
||||
@ -63,7 +66,7 @@ class ToUNIXTimestamp extends Operation {
|
||||
} else if (units === "Nanoseconds (ns)") {
|
||||
result = d.valueOf() * 1000000;
|
||||
} else {
|
||||
throw "Unrecognised unit";
|
||||
throw new OperationError("Unrecognised unit");
|
||||
}
|
||||
|
||||
return showDateTime ? `${result} (${d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss")} UTC)` : result.toString();
|
||||
|
@ -7,6 +7,7 @@
|
||||
import Operation from "../Operation";
|
||||
import moment from "moment-timezone";
|
||||
import {DATETIME_FORMATS, FORMAT_EXAMPLES} from "../lib/DateTime";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Translate DateTime Format operation
|
||||
@ -67,7 +68,7 @@ class TranslateDateTimeFormat extends Operation {
|
||||
date = moment.tz(input, inputFormat, inputTimezone);
|
||||
if (!date || date.format() === "Invalid date") throw Error;
|
||||
} catch (err) {
|
||||
return "Invalid format.\n\n" + FORMAT_EXAMPLES;
|
||||
throw new OperationError(`Invalid format.\n\n${FORMAT_EXAMPLES}`);
|
||||
}
|
||||
|
||||
return date.tz(outputTimezone).format(outputFormat);
|
||||
|
44
src/core/operations/URLDecode.mjs
Normal file
44
src/core/operations/URLDecode.mjs
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
/**
|
||||
* URL Decode operation
|
||||
*/
|
||||
class URLDecode extends Operation {
|
||||
|
||||
/**
|
||||
* URLDecode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "URL Decode";
|
||||
this.module = "URL";
|
||||
this.description = "Converts URI/URL percent-encoded characters back to their raw values.<br><br>e.g. <code>%3d</code> becomes <code>=</code>";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const data = input.replace(/\+/g, "%20");
|
||||
try {
|
||||
return decodeURIComponent(data);
|
||||
} catch (err) {
|
||||
return unescape(data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default URLDecode;
|
68
src/core/operations/URLEncode.mjs
Normal file
68
src/core/operations/URLEncode.mjs
Normal file
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
/**
|
||||
* URL Encode operation
|
||||
*/
|
||||
class URLEncode extends Operation {
|
||||
|
||||
/**
|
||||
* URLEncode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "URL Encode";
|
||||
this.module = "URL";
|
||||
this.description = "Encodes problematic characters into percent-encoding, a format supported by URIs/URLs.<br><br>e.g. <code>=</code> becomes <code>%3d</code>";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Encode all special chars",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const encodeAll = args[0];
|
||||
return encodeAll ? this.encodeAllChars(input) : encodeURI(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode characters in URL outside of encodeURI() function spec
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
encodeAllChars (str) {
|
||||
// TODO Do this programatically
|
||||
return encodeURIComponent(str)
|
||||
.replace(/!/g, "%21")
|
||||
.replace(/#/g, "%23")
|
||||
.replace(/'/g, "%27")
|
||||
.replace(/\(/g, "%28")
|
||||
.replace(/\)/g, "%29")
|
||||
.replace(/\*/g, "%2A")
|
||||
.replace(/-/g, "%2D")
|
||||
.replace(/\./g, "%2E")
|
||||
.replace(/_/g, "%5F")
|
||||
.replace(/~/g, "%7E");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default URLEncode;
|
75
src/core/operations/UnescapeUnicodeCharacters.mjs
Normal file
75
src/core/operations/UnescapeUnicodeCharacters.mjs
Normal file
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
|
||||
/**
|
||||
* Unescape Unicode Characters operation
|
||||
*/
|
||||
class UnescapeUnicodeCharacters extends Operation {
|
||||
|
||||
/**
|
||||
* UnescapeUnicodeCharacters constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Unescape Unicode Characters";
|
||||
this.module = "Default";
|
||||
this.description = "Converts unicode-escaped character notation back into raw characters.<br><br>Supports the prefixes:<ul><li><code>\\u</code></li><li><code>%u</code></li><li><code>U+</code></li></ul>e.g. <code>\\u03c3\\u03bf\\u03c5</code> becomes <code>σου</code>";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Prefix",
|
||||
"type": "option",
|
||||
"value": ["\\u", "%u", "U+"]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const prefix = prefixToRegex[args[0]],
|
||||
regex = new RegExp(prefix+"([a-f\\d]{4})", "ig");
|
||||
let output = "",
|
||||
m,
|
||||
i = 0;
|
||||
|
||||
while ((m = regex.exec(input))) {
|
||||
// Add up to match
|
||||
output += input.slice(i, m.index);
|
||||
i = m.index;
|
||||
|
||||
// Add match
|
||||
output += Utils.chr(parseInt(m[1], 16));
|
||||
|
||||
i = regex.lastIndex;
|
||||
}
|
||||
|
||||
// Add all after final match
|
||||
output += input.slice(i, input.length);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup table to add prefixes to unicode delimiters so that they can be used in a regex.
|
||||
*/
|
||||
const prefixToRegex = {
|
||||
"\\u": "\\\\u",
|
||||
"%u": "%u",
|
||||
"U+": "U\\+"
|
||||
};
|
||||
|
||||
export default UnescapeUnicodeCharacters;
|
@ -1,333 +0,0 @@
|
||||
import XRegExp from "xregexp";
|
||||
|
||||
|
||||
/**
|
||||
* Identifier extraction operations.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
const Extract = {
|
||||
|
||||
/**
|
||||
* Runs search operations across the input data using regular expressions.
|
||||
*
|
||||
* @private
|
||||
* @param {string} input
|
||||
* @param {RegExp} searchRegex
|
||||
* @param {RegExp} removeRegex - A regular expression defining results to remove from the
|
||||
* final list
|
||||
* @param {boolean} includeTotal - Whether or not to include the total number of results
|
||||
* @returns {string}
|
||||
*/
|
||||
_search: function(input, searchRegex, removeRegex, includeTotal) {
|
||||
let output = "",
|
||||
total = 0,
|
||||
match;
|
||||
|
||||
while ((match = searchRegex.exec(input))) {
|
||||
// Moves pointer when an empty string is matched (prevents infinite loop)
|
||||
if (match.index === searchRegex.lastIndex) {
|
||||
searchRegex.lastIndex++;
|
||||
}
|
||||
|
||||
if (removeRegex && removeRegex.test(match[0]))
|
||||
continue;
|
||||
total++;
|
||||
output += match[0] + "\n";
|
||||
}
|
||||
|
||||
if (includeTotal)
|
||||
output = "Total found: " + total + "\n\n" + output;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
MIN_STRING_LEN: 4,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
STRING_MATCH_TYPE: [
|
||||
"[ASCII]", "Alphanumeric + punctuation (A)", "All printable chars (A)", "Null-terminated strings (A)",
|
||||
"[Unicode]", "Alphanumeric + punctuation (U)", "All printable chars (U)", "Null-terminated strings (U)"
|
||||
],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
ENCODING_LIST: ["Single byte", "16-bit littleendian", "16-bit bigendian", "All"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
DISPLAY_TOTAL: false,
|
||||
|
||||
/**
|
||||
* Strings operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runStrings: function(input, args) {
|
||||
const encoding = args[0],
|
||||
minLen = args[1],
|
||||
matchType = args[2],
|
||||
displayTotal = args[3],
|
||||
alphanumeric = "A-Z\\d",
|
||||
punctuation = "/\\-:.,_$%'\"()<>= !\\[\\]{}@",
|
||||
printable = "\x20-\x7e",
|
||||
uniAlphanumeric = "\\pL\\pN",
|
||||
uniPunctuation = "\\pP\\pZ",
|
||||
uniPrintable = "\\pL\\pM\\pZ\\pS\\pN\\pP";
|
||||
|
||||
let strings = "";
|
||||
|
||||
switch (matchType) {
|
||||
case "Alphanumeric + punctuation (A)":
|
||||
strings = `[${alphanumeric + punctuation}]`;
|
||||
break;
|
||||
case "All printable chars (A)":
|
||||
case "Null-terminated strings (A)":
|
||||
strings = `[${printable}]`;
|
||||
break;
|
||||
case "Alphanumeric + punctuation (U)":
|
||||
strings = `[${uniAlphanumeric + uniPunctuation}]`;
|
||||
break;
|
||||
case "All printable chars (U)":
|
||||
case "Null-terminated strings (U)":
|
||||
strings = `[${uniPrintable}]`;
|
||||
break;
|
||||
}
|
||||
|
||||
// UTF-16 support is hacked in by allowing null bytes on either side of the matched chars
|
||||
switch (encoding) {
|
||||
case "All":
|
||||
strings = `(\x00?${strings}\x00?)`;
|
||||
break;
|
||||
case "16-bit littleendian":
|
||||
strings = `(${strings}\x00)`;
|
||||
break;
|
||||
case "16-bit bigendian":
|
||||
strings = `(\x00${strings})`;
|
||||
break;
|
||||
case "Single byte":
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
strings = `${strings}{${minLen},}`;
|
||||
|
||||
if (matchType.includes("Null-terminated")) {
|
||||
strings += "\x00";
|
||||
}
|
||||
|
||||
const regex = new XRegExp(strings, "ig");
|
||||
|
||||
return Extract._search(input, regex, null, displayTotal);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
INCLUDE_IPV4: true,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
INCLUDE_IPV6: false,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
REMOVE_LOCAL: false,
|
||||
|
||||
/**
|
||||
* Extract IP addresses operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runIp: function(input, args) {
|
||||
let includeIpv4 = args[0],
|
||||
includeIpv6 = args[1],
|
||||
removeLocal = args[2],
|
||||
displayTotal = args[3],
|
||||
ipv4 = "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?",
|
||||
ipv6 = "((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})((([\\dA-F]{1,4}((?!\\3)::|:\\b|(?![\\dA-F])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})",
|
||||
ips = "";
|
||||
|
||||
if (includeIpv4 && includeIpv6) {
|
||||
ips = ipv4 + "|" + ipv6;
|
||||
} else if (includeIpv4) {
|
||||
ips = ipv4;
|
||||
} else if (includeIpv6) {
|
||||
ips = ipv6;
|
||||
}
|
||||
|
||||
if (ips) {
|
||||
const regex = new RegExp(ips, "ig");
|
||||
|
||||
if (removeLocal) {
|
||||
let ten = "10\\..+",
|
||||
oneninetwo = "192\\.168\\..+",
|
||||
oneseventwo = "172\\.(?:1[6-9]|2\\d|3[01])\\..+",
|
||||
onetwoseven = "127\\..+",
|
||||
removeRegex = new RegExp("^(?:" + ten + "|" + oneninetwo +
|
||||
"|" + oneseventwo + "|" + onetwoseven + ")");
|
||||
|
||||
return Extract._search(input, regex, removeRegex, displayTotal);
|
||||
} else {
|
||||
return Extract._search(input, regex, null, displayTotal);
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Extract email addresses operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runEmail: function(input, args) {
|
||||
let displayTotal = args[0],
|
||||
regex = /\b\w[-.\w]*@[-\w]+(?:\.[-\w]+)*\.[A-Z]{2,4}\b/ig;
|
||||
|
||||
return Extract._search(input, regex, null, displayTotal);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Extract MAC addresses operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runMac: function(input, args) {
|
||||
let displayTotal = args[0],
|
||||
regex = /[A-F\d]{2}(?:[:-][A-F\d]{2}){5}/ig;
|
||||
|
||||
return Extract._search(input, regex, null, displayTotal);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Extract URLs operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runUrls: function(input, args) {
|
||||
let displayTotal = args[0],
|
||||
protocol = "[A-Z]+://",
|
||||
hostname = "[-\\w]+(?:\\.\\w[-\\w]*)+",
|
||||
port = ":\\d+",
|
||||
path = "/[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]*";
|
||||
|
||||
path += "(?:[.!,?]+[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]+)*";
|
||||
const regex = new RegExp(protocol + hostname + "(?:" + port +
|
||||
")?(?:" + path + ")?", "ig");
|
||||
return Extract._search(input, regex, null, displayTotal);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Extract domains operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runDomains: function(input, args) {
|
||||
const displayTotal = args[0],
|
||||
regex = /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b/ig;
|
||||
|
||||
return Extract._search(input, regex, null, displayTotal);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
INCLUDE_WIN_PATH: true,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
INCLUDE_UNIX_PATH: true,
|
||||
|
||||
/**
|
||||
* Extract file paths operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runFilePaths: function(input, args) {
|
||||
let includeWinPath = args[0],
|
||||
includeUnixPath = args[1],
|
||||
displayTotal = args[2],
|
||||
winDrive = "[A-Z]:\\\\",
|
||||
winName = "[A-Z\\d][A-Z\\d\\- '_\\(\\)~]{0,61}",
|
||||
winExt = "[A-Z\\d]{1,6}",
|
||||
winPath = winDrive + "(?:" + winName + "\\\\?)*" + winName +
|
||||
"(?:\\." + winExt + ")?",
|
||||
unixPath = "(?:/[A-Z\\d.][A-Z\\d\\-.]{0,61})+",
|
||||
filePaths = "";
|
||||
|
||||
if (includeWinPath && includeUnixPath) {
|
||||
filePaths = winPath + "|" + unixPath;
|
||||
} else if (includeWinPath) {
|
||||
filePaths = winPath;
|
||||
} else if (includeUnixPath) {
|
||||
filePaths = unixPath;
|
||||
}
|
||||
|
||||
if (filePaths) {
|
||||
const regex = new RegExp(filePaths, "ig");
|
||||
return Extract._search(input, regex, null, displayTotal);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Extract dates operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runDates: function(input, args) {
|
||||
let displayTotal = args[0],
|
||||
date1 = "(?:19|20)\\d\\d[- /.](?:0[1-9]|1[012])[- /.](?:0[1-9]|[12][0-9]|3[01])", // yyyy-mm-dd
|
||||
date2 = "(?:0[1-9]|[12][0-9]|3[01])[- /.](?:0[1-9]|1[012])[- /.](?:19|20)\\d\\d", // dd/mm/yyyy
|
||||
date3 = "(?:0[1-9]|1[012])[- /.](?:0[1-9]|[12][0-9]|3[01])[- /.](?:19|20)\\d\\d", // mm/dd/yyyy
|
||||
regex = new RegExp(date1 + "|" + date2 + "|" + date3, "ig");
|
||||
|
||||
return Extract._search(input, regex, null, displayTotal);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default Extract;
|
@ -1,364 +0,0 @@
|
||||
import * as kbpgp from "kbpgp";
|
||||
import {promisify} from "es6-promisify";
|
||||
|
||||
|
||||
/**
|
||||
* PGP operations.
|
||||
*
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @author Matt C [matt@artemisbot.uk]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
const PGP = {
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
KEY_TYPES: ["RSA-1024", "RSA-2048", "RSA-4096", "ECC-256", "ECC-384"],
|
||||
|
||||
|
||||
/**
|
||||
* Get size of subkey
|
||||
*
|
||||
* @private
|
||||
* @param {number} keySize
|
||||
* @returns {number}
|
||||
*/
|
||||
_getSubkeySize(keySize) {
|
||||
return {
|
||||
1024: 1024,
|
||||
2048: 1024,
|
||||
4096: 2048,
|
||||
256: 256,
|
||||
384: 256,
|
||||
}[keySize];
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Progress callback
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_ASP: new kbpgp.ASP({
|
||||
"progress_hook": info => {
|
||||
let msg = "";
|
||||
|
||||
switch (info.what) {
|
||||
case "guess":
|
||||
msg = "Guessing a prime";
|
||||
break;
|
||||
case "fermat":
|
||||
msg = "Factoring prime using Fermat's factorization method";
|
||||
break;
|
||||
case "mr":
|
||||
msg = "Performing Miller-Rabin primality test";
|
||||
break;
|
||||
case "passed_mr":
|
||||
msg = "Passed Miller-Rabin primality test";
|
||||
break;
|
||||
case "failed_mr":
|
||||
msg = "Failed Miller-Rabin primality test";
|
||||
break;
|
||||
case "found":
|
||||
msg = "Prime found";
|
||||
break;
|
||||
default:
|
||||
msg = `Stage: ${info.what}`;
|
||||
}
|
||||
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage(msg);
|
||||
}
|
||||
}),
|
||||
|
||||
|
||||
/**
|
||||
* Import private key and unlock if necessary
|
||||
*
|
||||
* @private
|
||||
* @param {string} privateKey
|
||||
* @param {string} [passphrase]
|
||||
* @returns {Object}
|
||||
*/
|
||||
async _importPrivateKey(privateKey, passphrase) {
|
||||
try {
|
||||
const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
||||
armored: privateKey,
|
||||
opts: {
|
||||
"no_check_keys": true
|
||||
}
|
||||
});
|
||||
if (key.is_pgp_locked()) {
|
||||
if (passphrase) {
|
||||
await promisify(key.unlock_pgp.bind(key))({
|
||||
passphrase
|
||||
});
|
||||
} else {
|
||||
throw "Did not provide passphrase with locked private key.";
|
||||
}
|
||||
}
|
||||
return key;
|
||||
} catch (err) {
|
||||
throw `Could not import private key: ${err}`;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Import public key
|
||||
*
|
||||
* @private
|
||||
* @param {string} publicKey
|
||||
* @returns {Object}
|
||||
*/
|
||||
async _importPublicKey (publicKey) {
|
||||
try {
|
||||
const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
||||
armored: publicKey,
|
||||
opts: {
|
||||
"no_check_keys": true
|
||||
}
|
||||
});
|
||||
return key;
|
||||
} catch (err) {
|
||||
throw `Could not import public key: ${err}`;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Generate PGP Key Pair operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runGenerateKeyPair(input, args) {
|
||||
let [keyType, keySize] = args[0].split("-"),
|
||||
password = args[1],
|
||||
name = args[2],
|
||||
email = args[3],
|
||||
userIdentifier = "";
|
||||
|
||||
if (name) userIdentifier += name;
|
||||
if (email) userIdentifier += ` <${email}>`;
|
||||
|
||||
let flags = kbpgp.const.openpgp.certify_keys;
|
||||
flags |= kbpgp.const.openpgp.sign_data;
|
||||
flags |= kbpgp.const.openpgp.auth;
|
||||
flags |= kbpgp.const.openpgp.encrypt_comm;
|
||||
flags |= kbpgp.const.openpgp.encrypt_storage;
|
||||
|
||||
let keyGenerationOptions = {
|
||||
userid: userIdentifier,
|
||||
ecc: keyType === "ecc",
|
||||
primary: {
|
||||
"nbits": keySize,
|
||||
"flags": flags,
|
||||
"expire_in": 0
|
||||
},
|
||||
subkeys: [{
|
||||
"nbits": PGP._getSubkeySize(keySize),
|
||||
"flags": kbpgp.const.openpgp.sign_data,
|
||||
"expire_in": 86400 * 365 * 8
|
||||
}, {
|
||||
"nbits": PGP._getSubkeySize(keySize),
|
||||
"flags": kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage,
|
||||
"expire_in": 86400 * 365 * 2
|
||||
}],
|
||||
asp: PGP._ASP
|
||||
};
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const unsignedKey = await promisify(kbpgp.KeyManager.generate)(keyGenerationOptions);
|
||||
await promisify(unsignedKey.sign.bind(unsignedKey))({});
|
||||
let signedKey = unsignedKey;
|
||||
let privateKeyExportOptions = {};
|
||||
if (password) privateKeyExportOptions.passphrase = password;
|
||||
const privateKey = await promisify(signedKey.export_pgp_private.bind(signedKey))(privateKeyExportOptions);
|
||||
const publicKey = await promisify(signedKey.export_pgp_public.bind(signedKey))({});
|
||||
resolve(privateKey + "\n" + publicKey.trim());
|
||||
} catch (err) {
|
||||
reject(`Error whilst generating key pair: ${err}`);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* PGP Encrypt operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async runEncrypt(input, args) {
|
||||
let plaintextMessage = input,
|
||||
plainPubKey = args[0],
|
||||
key,
|
||||
encryptedMessage;
|
||||
|
||||
if (!plainPubKey) return "Enter the public key of the recipient.";
|
||||
|
||||
try {
|
||||
key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
||||
armored: plainPubKey,
|
||||
});
|
||||
} catch (err) {
|
||||
throw `Could not import public key: ${err}`;
|
||||
}
|
||||
|
||||
try {
|
||||
encryptedMessage = await promisify(kbpgp.box)({
|
||||
"msg": plaintextMessage,
|
||||
"encrypt_for": key,
|
||||
"asp": PGP._ASP
|
||||
});
|
||||
} catch (err) {
|
||||
throw `Couldn't encrypt message with provided public key: ${err}`;
|
||||
}
|
||||
|
||||
return encryptedMessage.toString();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* PGP Decrypt operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async runDecrypt(input, args) {
|
||||
let encryptedMessage = input,
|
||||
privateKey = args[0],
|
||||
passphrase = args[1],
|
||||
keyring = new kbpgp.keyring.KeyRing(),
|
||||
plaintextMessage;
|
||||
|
||||
if (!privateKey) return "Enter the private key of the recipient.";
|
||||
|
||||
const key = await PGP._importPrivateKey(privateKey, passphrase);
|
||||
keyring.add_key_manager(key);
|
||||
|
||||
try {
|
||||
plaintextMessage = await promisify(kbpgp.unbox)({
|
||||
armored: encryptedMessage,
|
||||
keyfetch: keyring,
|
||||
asp: PGP._ASP
|
||||
});
|
||||
} catch (err) {
|
||||
throw `Couldn't decrypt message with provided private key: ${err}`;
|
||||
}
|
||||
|
||||
return plaintextMessage.toString();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* PGP Sign Message operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async runSign(input, args) {
|
||||
let message = input,
|
||||
privateKey = args[0],
|
||||
passphrase = args[1],
|
||||
publicKey = args[2],
|
||||
signedMessage;
|
||||
|
||||
if (!privateKey) return "Enter the private key of the signer.";
|
||||
if (!publicKey) return "Enter the public key of the recipient.";
|
||||
const privKey = await PGP._importPrivateKey(privateKey, passphrase);
|
||||
const pubKey = await PGP._importPublicKey(publicKey);
|
||||
|
||||
try {
|
||||
signedMessage = await promisify(kbpgp.box)({
|
||||
"msg": message,
|
||||
"encrypt_for": pubKey,
|
||||
"sign_with": privKey,
|
||||
"asp": PGP._ASP
|
||||
});
|
||||
} catch (err) {
|
||||
throw `Couldn't sign message: ${err}`;
|
||||
}
|
||||
|
||||
return signedMessage;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* PGP Verify Message operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async runVerify(input, args) {
|
||||
let signedMessage = input,
|
||||
publicKey = args[0],
|
||||
privateKey = args[1],
|
||||
passphrase = args[2],
|
||||
keyring = new kbpgp.keyring.KeyRing(),
|
||||
unboxedLiterals;
|
||||
|
||||
if (!publicKey) return "Enter the public key of the signer.";
|
||||
if (!privateKey) return "Enter the private key of the recipient.";
|
||||
const privKey = await PGP._importPrivateKey(privateKey, passphrase);
|
||||
const pubKey = await PGP._importPublicKey(publicKey);
|
||||
keyring.add_key_manager(privKey);
|
||||
keyring.add_key_manager(pubKey);
|
||||
|
||||
try {
|
||||
unboxedLiterals = await promisify(kbpgp.unbox)({
|
||||
armored: signedMessage,
|
||||
keyfetch: keyring,
|
||||
asp: PGP._ASP
|
||||
});
|
||||
const ds = unboxedLiterals[0].get_data_signer();
|
||||
if (ds) {
|
||||
const km = ds.get_key_manager();
|
||||
if (km) {
|
||||
const signer = km.get_userids_mark_primary()[0].components;
|
||||
let text = "Signed by ";
|
||||
if (signer.email || signer.username || signer.comment) {
|
||||
if (signer.username) {
|
||||
text += `${signer.username} `;
|
||||
}
|
||||
if (signer.comment) {
|
||||
text += `${signer.comment} `;
|
||||
}
|
||||
if (signer.email) {
|
||||
text += `<${signer.email}>`;
|
||||
}
|
||||
text += "\n";
|
||||
}
|
||||
text += [
|
||||
`PGP fingerprint: ${km.get_pgp_fingerprint().toString("hex")}`,
|
||||
`Signed on ${new Date(ds.sig.hashed_subpackets[0].time * 1000).toUTCString()}`,
|
||||
"----------------------------------\n"
|
||||
].join("\n");
|
||||
text += unboxedLiterals.toString();
|
||||
return text.trim();
|
||||
} else {
|
||||
return "Could not identify a key manager.";
|
||||
}
|
||||
} else {
|
||||
return "The data does not appear to be signed.";
|
||||
}
|
||||
} catch (err) {
|
||||
return `Couldn't verify message: ${err}`;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default PGP;
|
@ -1,118 +0,0 @@
|
||||
/* globals unescape */
|
||||
import url from "url";
|
||||
|
||||
|
||||
/**
|
||||
* URL operations.
|
||||
* Namespace is appended with an underscore to prevent overwriting the global URL object.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
const URL_ = {
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
ENCODE_ALL: false,
|
||||
|
||||
/**
|
||||
* URL Encode operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runTo: function(input, args) {
|
||||
const encodeAll = args[0];
|
||||
return encodeAll ? URL_._encodeAllChars(input) : encodeURI(input);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* URL Decode operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runFrom: function(input, args) {
|
||||
const data = input.replace(/\+/g, "%20");
|
||||
try {
|
||||
return decodeURIComponent(data);
|
||||
} catch (err) {
|
||||
return unescape(data);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Parse URI operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runParse: function(input, args) {
|
||||
const uri = url.parse(input, true);
|
||||
|
||||
let output = "";
|
||||
|
||||
if (uri.protocol) output += "Protocol:\t" + uri.protocol + "\n";
|
||||
if (uri.auth) output += "Auth:\t\t" + uri.auth + "\n";
|
||||
if (uri.hostname) output += "Hostname:\t" + uri.hostname + "\n";
|
||||
if (uri.port) output += "Port:\t\t" + uri.port + "\n";
|
||||
if (uri.pathname) output += "Path name:\t" + uri.pathname + "\n";
|
||||
if (uri.query) {
|
||||
let keys = Object.keys(uri.query),
|
||||
padding = 0;
|
||||
|
||||
keys.forEach(k => {
|
||||
padding = (k.length > padding) ? k.length : padding;
|
||||
});
|
||||
|
||||
output += "Arguments:\n";
|
||||
for (let key in uri.query) {
|
||||
output += "\t" + key.padEnd(padding, " ");
|
||||
if (uri.query[key].length) {
|
||||
output += " = " + uri.query[key] + "\n";
|
||||
} else {
|
||||
output += "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uri.hash) output += "Hash:\t\t" + uri.hash + "\n";
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* URL encodes additional special characters beyond the standard set.
|
||||
*
|
||||
* @private
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
_encodeAllChars: function(str) {
|
||||
//TODO Do this programatically
|
||||
return encodeURIComponent(str)
|
||||
.replace(/!/g, "%21")
|
||||
.replace(/#/g, "%23")
|
||||
.replace(/'/g, "%27")
|
||||
.replace(/\(/g, "%28")
|
||||
.replace(/\)/g, "%29")
|
||||
.replace(/\*/g, "%2A")
|
||||
.replace(/-/g, "%2D")
|
||||
.replace(/\./g, "%2E")
|
||||
.replace(/_/g, "%5F")
|
||||
.replace(/~/g, "%7E");
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default URL_;
|
@ -3316,7 +3316,7 @@ If input "type" is set 5 it will adjust the mnemonic array to decode Centaur ins
|
||||
If input "type" is set 6 it will adjust the mnemonic array to decode instruction for the X86/486 CPU which conflict with the vector unit instructions with UMOV.
|
||||
-------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
function CompatibilityMode( type )
|
||||
export function CompatibilityMode( type )
|
||||
{
|
||||
//Reset the changeable sections of the Mnemonics array, and operand encoding array.
|
||||
|
||||
@ -3515,7 +3515,7 @@ The function "GetPosition()" Gives back the current base address in it's proper
|
||||
If the hex input is invalid returns false.
|
||||
-------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
function LoadBinCode( HexStr )
|
||||
export function LoadBinCode( HexStr )
|
||||
{
|
||||
//Clear BinCode, and Reset Code Position in Bin Code array.
|
||||
|
||||
@ -3605,7 +3605,7 @@ segment, and offset address. Note that the Code Segment is used in 16 bit code.
|
||||
if set 36, or higher. Effects instruction location in memory when decoding a program.
|
||||
-------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
function SetBasePosition( Address )
|
||||
export function SetBasePosition( Address )
|
||||
{
|
||||
//Split the Segment:offset.
|
||||
|
||||
@ -5652,7 +5652,7 @@ function Reset()
|
||||
do an linear disassemble.
|
||||
-------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
function LDisassemble()
|
||||
export function LDisassemble()
|
||||
{
|
||||
var Instruction = ""; //Stores the Decoded instruction.
|
||||
var Out = ""; //The Disassemble output
|
||||
@ -5709,13 +5709,13 @@ function LDisassemble()
|
||||
* The following code has been added to expose public methods for use in CyberChef
|
||||
*/
|
||||
|
||||
export default {
|
||||
LoadBinCode: LoadBinCode,
|
||||
LDisassemble: LDisassemble,
|
||||
SetBasePosition: SetBasePosition,
|
||||
CompatibilityMode: CompatibilityMode,
|
||||
|
||||
setBitMode: val => { BitMode = val; },
|
||||
setShowInstructionHex: val => { ShowInstructionHex = val; },
|
||||
setShowInstructionPos: val => { ShowInstructionPos = val; },
|
||||
export function setBitMode (val) {
|
||||
BitMode = val;
|
||||
};
|
||||
export function setShowInstructionHex (val) {
|
||||
ShowInstructionHex = val;
|
||||
};
|
||||
export function setShowInstructionPos (val) {
|
||||
ShowInstructionPos = val;
|
||||
};
|
||||
|
186
src/core/vendor/canvascomponents.js
vendored
186
src/core/vendor/canvascomponents.js
vendored
@ -1,186 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Various components for drawing diagrams on an HTML5 canvas.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @constant
|
||||
* @namespace
|
||||
*/
|
||||
const CanvasComponents = {
|
||||
|
||||
drawLine: function(ctx, startX, startY, endX, endY) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(startX, startY);
|
||||
ctx.lineTo(endX, endY);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
},
|
||||
|
||||
drawBarChart: function(canvas, scores, xAxisLabel, yAxisLabel, numXLabels, numYLabels, fontSize) {
|
||||
fontSize = fontSize || 15;
|
||||
if (!numXLabels || numXLabels > Math.round(canvas.width / 50)) {
|
||||
numXLabels = Math.round(canvas.width / 50);
|
||||
}
|
||||
if (!numYLabels || numYLabels > Math.round(canvas.width / 50)) {
|
||||
numYLabels = Math.round(canvas.height / 50);
|
||||
}
|
||||
|
||||
// Graph properties
|
||||
var ctx = canvas.getContext("2d"),
|
||||
leftPadding = canvas.width * 0.08,
|
||||
rightPadding = canvas.width * 0.03,
|
||||
topPadding = canvas.height * 0.08,
|
||||
bottomPadding = canvas.height * 0.15,
|
||||
graphHeight = canvas.height - topPadding - bottomPadding,
|
||||
graphWidth = canvas.width - leftPadding - rightPadding,
|
||||
base = topPadding + graphHeight,
|
||||
ceil = topPadding;
|
||||
|
||||
ctx.font = fontSize + "px Arial";
|
||||
|
||||
// Draw axis
|
||||
ctx.lineWidth = "1.0";
|
||||
ctx.strokeStyle = "#444";
|
||||
CanvasComponents.drawLine(ctx, leftPadding, base, graphWidth + leftPadding, base); // x
|
||||
CanvasComponents.drawLine(ctx, leftPadding, base, leftPadding, ceil); // y
|
||||
|
||||
// Bar properties
|
||||
var barPadding = graphWidth * 0.003,
|
||||
barWidth = (graphWidth - (barPadding * scores.length)) / scores.length,
|
||||
currX = leftPadding + barPadding,
|
||||
max = Math.max.apply(Math, scores);
|
||||
|
||||
// Draw bars
|
||||
ctx.fillStyle = "green";
|
||||
for (var i = 0; i < scores.length; i++) {
|
||||
var h = scores[i] / max * graphHeight;
|
||||
ctx.fillRect(currX, base - h, barWidth, h);
|
||||
currX += barWidth + barPadding;
|
||||
}
|
||||
|
||||
// Mark x axis
|
||||
ctx.fillStyle = "black";
|
||||
ctx.textAlign = "center";
|
||||
currX = leftPadding + barPadding;
|
||||
if (numXLabels >= scores.length) {
|
||||
// Mark every score
|
||||
for (i = 0; i <= scores.length; i++) {
|
||||
ctx.fillText(i, currX, base + (bottomPadding * 0.3));
|
||||
currX += barWidth + barPadding;
|
||||
}
|
||||
} else {
|
||||
// Mark some scores
|
||||
for (i = 0; i <= numXLabels; i++) {
|
||||
var val = Math.ceil((scores.length / numXLabels) * i);
|
||||
currX = (graphWidth / numXLabels) * i + leftPadding;
|
||||
ctx.fillText(val, currX, base + (bottomPadding * 0.3));
|
||||
}
|
||||
}
|
||||
|
||||
// Mark y axis
|
||||
ctx.textAlign = "right";
|
||||
var currY;
|
||||
if (numYLabels >= max) {
|
||||
// Mark every increment
|
||||
for (i = 0; i <= max; i++) {
|
||||
currY = base - (i / max * graphHeight) + fontSize / 3;
|
||||
ctx.fillText(i, leftPadding * 0.8, currY);
|
||||
}
|
||||
} else {
|
||||
// Mark some increments
|
||||
for (i = 0; i <= numYLabels; i++) {
|
||||
val = Math.ceil((max / numYLabels) * i);
|
||||
currY = base - (val / max * graphHeight) + fontSize / 3;
|
||||
ctx.fillText(val, leftPadding * 0.8, currY);
|
||||
}
|
||||
}
|
||||
|
||||
// Label x axis
|
||||
if (xAxisLabel) {
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(xAxisLabel, graphWidth / 2 + leftPadding, base + bottomPadding * 0.8);
|
||||
}
|
||||
|
||||
// Label y axis
|
||||
if (yAxisLabel) {
|
||||
ctx.save();
|
||||
var x = leftPadding * 0.3,
|
||||
y = graphHeight / 2 + topPadding;
|
||||
ctx.translate(x, y);
|
||||
ctx.rotate(-Math.PI / 2);
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(yAxisLabel, 0, 0);
|
||||
ctx.restore();
|
||||
}
|
||||
},
|
||||
|
||||
drawScaleBar: function(canvas, score, max, markings) {
|
||||
// Bar properties
|
||||
var ctx = canvas.getContext("2d"),
|
||||
leftPadding = canvas.width * 0.01,
|
||||
rightPadding = canvas.width * 0.01,
|
||||
topPadding = canvas.height * 0.1,
|
||||
bottomPadding = canvas.height * 0.3,
|
||||
barHeight = canvas.height - topPadding - bottomPadding,
|
||||
barWidth = canvas.width - leftPadding - rightPadding;
|
||||
|
||||
// Scale properties
|
||||
var proportion = score / max;
|
||||
|
||||
// Draw bar outline
|
||||
ctx.strokeRect(leftPadding, topPadding, barWidth, barHeight);
|
||||
|
||||
// Shade in up to proportion
|
||||
var grad = ctx.createLinearGradient(leftPadding, 0, barWidth + leftPadding, 0);
|
||||
grad.addColorStop(0, "green");
|
||||
grad.addColorStop(0.5, "gold");
|
||||
grad.addColorStop(1, "red");
|
||||
ctx.fillStyle = grad;
|
||||
ctx.fillRect(leftPadding, topPadding, barWidth * proportion, barHeight);
|
||||
|
||||
// Add markings
|
||||
var x0, y0, x1, y1;
|
||||
ctx.fillStyle = "black";
|
||||
ctx.textAlign = "center";
|
||||
ctx.font = "13px Arial";
|
||||
for (var i = 0; i < markings.length; i++) {
|
||||
// Draw min line down
|
||||
x0 = barWidth / max * markings[i].min + leftPadding;
|
||||
y0 = topPadding + barHeight + (bottomPadding * 0.1);
|
||||
x1 = x0;
|
||||
y1 = topPadding + barHeight + (bottomPadding * 0.3);
|
||||
CanvasComponents.drawLine(ctx, x0, y0, x1, y1);
|
||||
|
||||
// Draw max line down
|
||||
x0 = barWidth / max * markings[i].max + leftPadding;
|
||||
x1 = x0;
|
||||
CanvasComponents.drawLine(ctx, x0, y0, x1, y1);
|
||||
|
||||
// Join min and max lines
|
||||
x0 = barWidth / max * markings[i].min + leftPadding;
|
||||
y0 = topPadding + barHeight + (bottomPadding * 0.3);
|
||||
x1 = barWidth / max * markings[i].max + leftPadding;
|
||||
y1 = y0;
|
||||
CanvasComponents.drawLine(ctx, x0, y0, x1, y1);
|
||||
|
||||
// Add label
|
||||
if (markings[i].max >= max * 0.9) {
|
||||
ctx.textAlign = "right";
|
||||
x0 = x1;
|
||||
} else if (markings[i].max <= max * 0.1) {
|
||||
ctx.textAlign = "left";
|
||||
} else {
|
||||
x0 = x0 + (x1 - x0) / 2;
|
||||
}
|
||||
y0 = topPadding + barHeight + (bottomPadding * 0.8);
|
||||
ctx.fillText(markings[i].label, x0, y0);
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default CanvasComponents;
|
@ -13,7 +13,7 @@ import "bootstrap";
|
||||
import "bootstrap-switch";
|
||||
import "bootstrap-colorpicker";
|
||||
import moment from "moment-timezone";
|
||||
import CanvasComponents from "../core/vendor/canvascomponents.js";
|
||||
import * as CanvasComponents from "../core/lib/CanvasComponents";
|
||||
|
||||
// CyberChef
|
||||
import App from "./App";
|
||||
|
@ -1,6 +1,5 @@
|
||||
const webpack = require("webpack");
|
||||
const ExtractTextPlugin = require("extract-text-webpack-plugin");
|
||||
const WebpackSyncShellPlugin = require("webpack-synchronizable-shell-plugin");
|
||||
|
||||
/**
|
||||
* Webpack configuration details for use with Grunt.
|
||||
@ -43,19 +42,7 @@ module.exports = {
|
||||
raw: true,
|
||||
entryOnly: true
|
||||
}),
|
||||
new ExtractTextPlugin("styles.css"),
|
||||
new WebpackSyncShellPlugin({
|
||||
onBuildStart: {
|
||||
scripts: [
|
||||
"echo \n--- Generating config files. ---",
|
||||
"node --experimental-modules src/core/config/scripts/generateOpsIndex.mjs",
|
||||
"node --experimental-modules src/core/config/scripts/generateConfig.mjs",
|
||||
"echo --- Config scripts finished. ---\n"
|
||||
],
|
||||
blocking: true,
|
||||
parallel: false
|
||||
}
|
||||
})
|
||||
new ExtractTextPlugin("styles.css")
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
|
Loading…
Reference in New Issue
Block a user