mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-20 14:56:19 -04:00
Merge branch 'esm' of https://github.com/gchq/CyberChef into ip-convert
This commit is contained in:
commit
ea36687205
75 changed files with 6520 additions and 3131 deletions
|
@ -22,11 +22,11 @@ module.exports = function (grunt) {
|
||||||
// Tasks
|
// Tasks
|
||||||
grunt.registerTask("dev",
|
grunt.registerTask("dev",
|
||||||
"A persistent task which creates a development build whenever source files are modified.",
|
"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",
|
grunt.registerTask("node",
|
||||||
"Compiles CyberChef into a single NodeJS module.",
|
"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",
|
grunt.registerTask("test",
|
||||||
"A task which runs all the tests in test/tests.",
|
"A task which runs all the tests in test/tests.",
|
||||||
|
@ -38,7 +38,7 @@ module.exports = function (grunt) {
|
||||||
|
|
||||||
grunt.registerTask("prod",
|
grunt.registerTask("prod",
|
||||||
"Creates a production-ready build. Use the --msg flag to add a compile message.",
|
"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",
|
grunt.registerTask("default",
|
||||||
"Lints the code base",
|
"Lints the code base",
|
||||||
|
@ -46,7 +46,7 @@ module.exports = function (grunt) {
|
||||||
|
|
||||||
grunt.registerTask("inline",
|
grunt.registerTask("inline",
|
||||||
"Compiles a production build of CyberChef into a single, portable web page.",
|
"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);
|
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": "^4.6.0",
|
||||||
"webpack-dev-server": "^3.1.3",
|
"webpack-dev-server": "^3.1.3",
|
||||||
"webpack-node-externals": "^1.7.2",
|
"webpack-node-externals": "^1.7.2",
|
||||||
"webpack-synchronizable-shell-plugin": "0.0.7",
|
|
||||||
"worker-loader": "^1.1.1"
|
"worker-loader": "^1.1.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -60,6 +60,12 @@ class Chef {
|
||||||
recipe.setBreakpoint(progress + 1, true);
|
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
|
// If stepping with flow control, we have to start from the beginning
|
||||||
// but still want to skip all previous breakpoints
|
// but still want to skip all previous breakpoints
|
||||||
if (progress > 0 && containsFc) {
|
if (progress > 0 && containsFc) {
|
||||||
|
|
|
@ -81,9 +81,10 @@ class Operation {
|
||||||
* this behaviour.
|
* this behaviour.
|
||||||
*
|
*
|
||||||
* @param {*} data - The result of the run() function
|
* @param {*} data - The result of the run() function
|
||||||
|
* @param {Object[]} args - The operation's arguments
|
||||||
* @returns {*} - A human-readable version of the data
|
* @returns {*} - A human-readable version of the data
|
||||||
*/
|
*/
|
||||||
present(data) {
|
present(data, args) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -177,7 +177,10 @@ class Recipe {
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Return expected errors as output
|
// 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");
|
dish.set(err.message, "string");
|
||||||
return i;
|
return i;
|
||||||
} else {
|
} else {
|
||||||
|
@ -209,7 +212,10 @@ class Recipe {
|
||||||
async present(dish) {
|
async present(dish) {
|
||||||
if (!this.lastRunOp) return;
|
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);
|
dish.set(output, this.lastRunOp.presentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,6 +273,18 @@ class Recipe {
|
||||||
return highlights;
|
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;
|
export default Recipe;
|
||||||
|
|
|
@ -15,6 +15,8 @@ class OperationError extends Error {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
|
|
||||||
|
this.type = "OperationError";
|
||||||
|
|
||||||
if (Error.captureStackTrace) {
|
if (Error.captureStackTrace) {
|
||||||
Error.captureStackTrace(this, OperationError);
|
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 Operation from "../Operation";
|
||||||
import Utils from "../Utils";
|
import Utils from "../Utils";
|
||||||
import forge from "node-forge/dist/forge.min.js";
|
import forge from "node-forge/dist/forge.min.js";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AES Decrypt operation
|
* AES Decrypt operation
|
||||||
|
@ -65,6 +66,8 @@ class AESDecrypt extends Operation {
|
||||||
* @param {string} input
|
* @param {string} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
*
|
||||||
|
* @throws {OperationError} if cannot decrypt input or invalid key length
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
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);
|
gcmTag = Utils.convertToByteString(args[5].string, args[5].option);
|
||||||
|
|
||||||
if ([16, 24, 32].indexOf(key.length) < 0) {
|
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:
|
The following algorithms will be used based on the size of the key:
|
||||||
16 bytes = AES-128
|
16 bytes = AES-128
|
||||||
24 bytes = AES-192
|
24 bytes = AES-192
|
||||||
32 bytes = AES-256`;
|
32 bytes = AES-256`);
|
||||||
}
|
}
|
||||||
|
|
||||||
input = Utils.convertToByteString(input, inputType);
|
input = Utils.convertToByteString(input, inputType);
|
||||||
|
@ -96,7 +99,7 @@ The following algorithms will be used based on the size of the key:
|
||||||
if (result) {
|
if (result) {
|
||||||
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
|
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
|
||||||
} else {
|
} 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 Operation from "../Operation";
|
||||||
import Utils from "../Utils";
|
import Utils from "../Utils";
|
||||||
import forge from "node-forge/dist/forge.min.js";
|
import forge from "node-forge/dist/forge.min.js";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AES Encrypt operation
|
* AES Encrypt operation
|
||||||
|
@ -59,6 +60,8 @@ class AESEncrypt extends Operation {
|
||||||
* @param {string} input
|
* @param {string} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
*
|
||||||
|
* @throws {OperationError} if invalid key length
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
||||||
|
@ -68,12 +71,12 @@ class AESEncrypt extends Operation {
|
||||||
outputType = args[4];
|
outputType = args[4];
|
||||||
|
|
||||||
if ([16, 24, 32].indexOf(key.length) < 0) {
|
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:
|
The following algorithms will be used based on the size of the key:
|
||||||
16 bytes = AES-128
|
16 bytes = AES-128
|
||||||
24 bytes = AES-192
|
24 bytes = AES-192
|
||||||
32 bytes = AES-256`;
|
32 bytes = AES-256`);
|
||||||
}
|
}
|
||||||
|
|
||||||
input = Utils.convertToByteString(input, inputType);
|
input = Utils.convertToByteString(input, inputType);
|
||||||
|
|
|
@ -42,6 +42,8 @@ class AffineCipherDecode extends Operation {
|
||||||
* @param {string} input
|
* @param {string} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
*
|
||||||
|
* @throws {OperationError} if a or b values are invalid
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const alphabet = "abcdefghijklmnopqrstuvwxyz",
|
const alphabet = "abcdefghijklmnopqrstuvwxyz",
|
||||||
|
|
|
@ -37,6 +37,8 @@ class BifidCipherDecode extends Operation {
|
||||||
* @param {string} input
|
* @param {string} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
*
|
||||||
|
* @throws {OperationError} if invalid key
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const keywordStr = args[0].toUpperCase().replace("J", "I"),
|
const keywordStr = args[0].toUpperCase().replace("J", "I"),
|
||||||
|
|
|
@ -37,6 +37,8 @@ class BifidCipherEncode extends Operation {
|
||||||
* @param {string} input
|
* @param {string} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
*
|
||||||
|
* @throws {OperationError} if key is invalid
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const keywordStr = args[0].toUpperCase().replace("J", "I"),
|
const keywordStr = args[0].toUpperCase().replace("J", "I"),
|
||||||
|
|
|
@ -41,7 +41,7 @@ class CartesianProduct extends Operation {
|
||||||
* Validate input length
|
* Validate input length
|
||||||
*
|
*
|
||||||
* @param {Object[]} sets
|
* @param {Object[]} sets
|
||||||
* @throws {Error} if fewer than 2 sets
|
* @throws {OperationError} if fewer than 2 sets
|
||||||
*/
|
*/
|
||||||
validateSampleNumbers(sets) {
|
validateSampleNumbers(sets) {
|
||||||
if (!sets || sets.length < 2) {
|
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 Operation from "../Operation";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drop bytes operation
|
* Drop bytes operation
|
||||||
|
@ -45,6 +46,8 @@ class DropBytes extends Operation {
|
||||||
* @param {ArrayBuffer} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {ArrayBuffer}
|
* @returns {ArrayBuffer}
|
||||||
|
*
|
||||||
|
* @throws {OperationError} if invalid input
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const start = args[0],
|
const start = args[0],
|
||||||
|
@ -52,7 +55,7 @@ class DropBytes extends Operation {
|
||||||
applyToEachLine = args[2];
|
applyToEachLine = args[2];
|
||||||
|
|
||||||
if (start < 0 || length < 0)
|
if (start < 0 || length < 0)
|
||||||
throw "Error: Invalid value";
|
throw new OperationError("Error: Invalid value");
|
||||||
|
|
||||||
if (!applyToEachLine) {
|
if (!applyToEachLine) {
|
||||||
const left = input.slice(0, start),
|
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 Operation from "../Operation";
|
||||||
import Utils from "../Utils";
|
import Utils from "../Utils";
|
||||||
import {INPUT_DELIM_OPTIONS} from "../lib/Delim";
|
import {INPUT_DELIM_OPTIONS} from "../lib/Delim";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter operation
|
* Filter operation
|
||||||
|
@ -56,7 +57,7 @@ class Filter extends Operation {
|
||||||
try {
|
try {
|
||||||
regex = new RegExp(args[1]);
|
regex = new RegExp(args[1]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return "Invalid regex. Details: " + err.message;
|
throw new OperationError(`Invalid regex. Details: ${err.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const regexFilter = function(value) {
|
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 Operation from "../Operation";
|
||||||
import Utils from "../Utils";
|
import Utils from "../Utils";
|
||||||
import {DELIM_OPTIONS} from "../lib/Delim";
|
import {DELIM_OPTIONS} from "../lib/Delim";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* From Charcode operation
|
* From Charcode operation
|
||||||
|
@ -42,6 +43,8 @@ class FromCharcode extends Operation {
|
||||||
* @param {string} input
|
* @param {string} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
|
*
|
||||||
|
* @throws {OperationError} if base out of range
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const delim = Utils.charRep(args[0] || "Space"),
|
const delim = Utils.charRep(args[0] || "Space"),
|
||||||
|
@ -50,7 +53,7 @@ class FromCharcode extends Operation {
|
||||||
i = 0;
|
i = 0;
|
||||||
|
|
||||||
if (base < 2 || base > 36) {
|
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) {
|
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 Operation from "../Operation";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import {UNITS} from "../lib/DateTime";
|
import {UNITS} from "../lib/DateTime";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* From UNIX Timestamp operation
|
* From UNIX Timestamp operation
|
||||||
|
@ -37,6 +38,8 @@ class FromUNIXTimestamp extends Operation {
|
||||||
* @param {number} input
|
* @param {number} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
*
|
||||||
|
* @throws {OperationError} if invalid unit
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const units = args[0];
|
const units = args[0];
|
||||||
|
@ -57,7 +60,7 @@ class FromUNIXTimestamp extends Operation {
|
||||||
d = moment(input / 1000000);
|
d = moment(input / 1000000);
|
||||||
return d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss.SSS") + " UTC";
|
return d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss.SSS") + " UTC";
|
||||||
} else {
|
} 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 Operation from "../Operation";
|
||||||
import Utils from "../Utils";
|
import Utils from "../Utils";
|
||||||
import {fromHex} from "../lib/Hex";
|
import {fromHex} from "../lib/Hex";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hamming Distance operation
|
* Hamming Distance operation
|
||||||
|
@ -55,11 +56,11 @@ class HammingDistance extends Operation {
|
||||||
samples = input.split(delim);
|
samples = input.split(delim);
|
||||||
|
|
||||||
if (samples.length !== 2) {
|
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) {
|
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") {
|
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 Operation from "../Operation";
|
||||||
import Utils from "../Utils";
|
import Utils from "../Utils";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Offset checker operation
|
* Offset checker operation
|
||||||
|
@ -48,7 +49,7 @@ class OffsetChecker extends Operation {
|
||||||
chr;
|
chr;
|
||||||
|
|
||||||
if (!samples || samples.length < 2) {
|
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
|
// 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 Operation from "../Operation";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import {DATETIME_FORMATS, FORMAT_EXAMPLES} from "../lib/DateTime";
|
import {DATETIME_FORMATS, FORMAT_EXAMPLES} from "../lib/DateTime";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse DateTime operation
|
* Parse DateTime operation
|
||||||
|
@ -59,7 +60,7 @@ class ParseDateTime extends Operation {
|
||||||
date = moment.tz(input, inputFormat, inputTimezone);
|
date = moment.tz(input, inputFormat, inputTimezone);
|
||||||
if (!date || date.format() === "Invalid date") throw Error;
|
if (!date || date.format() === "Invalid date") throw Error;
|
||||||
} catch (err) {
|
} 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") +
|
output += "Date: " + date.format("dddd Do MMMM YYYY") +
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Operation from "../Operation";
|
import Operation from "../Operation";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse UNIX file permissions operation
|
* Parse UNIX file permissions operation
|
||||||
|
@ -169,7 +170,7 @@ class ParseUNIXFilePermissions extends Operation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
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 Operation from "../Operation";
|
||||||
import {INFLATE_BUFFER_TYPE} from "../lib/Zlib";
|
import {INFLATE_BUFFER_TYPE} from "../lib/Zlib";
|
||||||
import rawinflate from "zlibjs/bin/rawinflate.min";
|
import rawinflate from "zlibjs/bin/rawinflate.min";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
const Zlib = rawinflate.Zlib;
|
const Zlib = rawinflate.Zlib;
|
||||||
|
|
||||||
|
@ -90,7 +91,7 @@ class RawInflate extends Operation {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
throw "Error: Unable to inflate data";
|
throw new OperationError("Error: Unable to inflate data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// This seems to be the easiest way...
|
// This seems to be the easiest way...
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
import Operation from "../Operation";
|
import Operation from "../Operation";
|
||||||
import Utils from "../Utils";
|
import Utils from "../Utils";
|
||||||
import {fromBase64, toBase64} from "../lib/Base64";
|
import {fromBase64, toBase64} from "../lib/Base64";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show Base64 offsets operation
|
* Show Base64 offsets operation
|
||||||
|
@ -58,7 +59,7 @@ class ShowBase64Offsets extends Operation {
|
||||||
script = "<script type='application/javascript'>$('[data-toggle=\"tooltip\"]').tooltip()</script>";
|
script = "<script type='application/javascript'>$('[data-toggle=\"tooltip\"]').tooltip()</script>";
|
||||||
|
|
||||||
if (input.length < 1) {
|
if (input.length < 1) {
|
||||||
return "Please enter a string.";
|
throw new OperationError("Please enter a string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Highlight offset 0
|
// 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 Operation from "../Operation";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take bytes operation
|
* Take bytes operation
|
||||||
|
@ -45,6 +46,8 @@ class TakeBytes extends Operation {
|
||||||
* @param {ArrayBuffer} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {ArrayBuffer}
|
* @returns {ArrayBuffer}
|
||||||
|
*
|
||||||
|
* @throws {OperationError} if invalid value
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const start = args[0],
|
const start = args[0],
|
||||||
|
@ -52,7 +55,7 @@ class TakeBytes extends Operation {
|
||||||
applyToEachLine = args[2];
|
applyToEachLine = args[2];
|
||||||
|
|
||||||
if (start < 0 || length < 0)
|
if (start < 0 || length < 0)
|
||||||
throw "Error: Invalid value";
|
throw new OperationError("Error: Invalid value");
|
||||||
|
|
||||||
if (!applyToEachLine)
|
if (!applyToEachLine)
|
||||||
return input.slice(start, start+length);
|
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 Operation from "../Operation";
|
||||||
import Utils from "../Utils";
|
import Utils from "../Utils";
|
||||||
import {DELIM_OPTIONS} from "../lib/Delim";
|
import {DELIM_OPTIONS} from "../lib/Delim";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To Charcode operation
|
* To Charcode operation
|
||||||
|
@ -42,6 +43,8 @@ class ToCharcode extends Operation {
|
||||||
* @param {string} input
|
* @param {string} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
*
|
||||||
|
* @throws {OperationError} if base argument out of range
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const delim = Utils.charRep(args[0] || "Space"),
|
const delim = Utils.charRep(args[0] || "Space"),
|
||||||
|
@ -51,7 +54,7 @@ class ToCharcode extends Operation {
|
||||||
ordinal;
|
ordinal;
|
||||||
|
|
||||||
if (base < 2 || base > 36) {
|
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);
|
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 Operation from "../Operation";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import {UNITS} from "../lib/DateTime";
|
import {UNITS} from "../lib/DateTime";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To UNIX Timestamp operation
|
* To UNIX Timestamp operation
|
||||||
|
@ -47,6 +48,8 @@ class ToUNIXTimestamp extends Operation {
|
||||||
* @param {string} input
|
* @param {string} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
*
|
||||||
|
* @throws {OperationError} if unit unrecognised
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const [units, treatAsUTC, showDateTime] = args,
|
const [units, treatAsUTC, showDateTime] = args,
|
||||||
|
@ -63,7 +66,7 @@ class ToUNIXTimestamp extends Operation {
|
||||||
} else if (units === "Nanoseconds (ns)") {
|
} else if (units === "Nanoseconds (ns)") {
|
||||||
result = d.valueOf() * 1000000;
|
result = d.valueOf() * 1000000;
|
||||||
} else {
|
} 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();
|
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 Operation from "../Operation";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import {DATETIME_FORMATS, FORMAT_EXAMPLES} from "../lib/DateTime";
|
import {DATETIME_FORMATS, FORMAT_EXAMPLES} from "../lib/DateTime";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate DateTime Format operation
|
* Translate DateTime Format operation
|
||||||
|
@ -67,7 +68,7 @@ class TranslateDateTimeFormat extends Operation {
|
||||||
date = moment.tz(input, inputFormat, inputTimezone);
|
date = moment.tz(input, inputFormat, inputTimezone);
|
||||||
if (!date || date.format() === "Invalid date") throw Error;
|
if (!date || date.format() === "Invalid date") throw Error;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return "Invalid format.\n\n" + FORMAT_EXAMPLES;
|
throw new OperationError(`Invalid format.\n\n${FORMAT_EXAMPLES}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return date.tz(outputTimezone).format(outputFormat);
|
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.
|
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.
|
//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.
|
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.
|
//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.
|
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.
|
//Split the Segment:offset.
|
||||||
|
|
||||||
|
@ -5652,7 +5652,7 @@ function Reset()
|
||||||
do an linear disassemble.
|
do an linear disassemble.
|
||||||
-------------------------------------------------------------------------------------------------------------------------*/
|
-------------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
function LDisassemble()
|
export function LDisassemble()
|
||||||
{
|
{
|
||||||
var Instruction = ""; //Stores the Decoded instruction.
|
var Instruction = ""; //Stores the Decoded instruction.
|
||||||
var Out = ""; //The Disassemble output
|
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
|
* The following code has been added to expose public methods for use in CyberChef
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
export function setBitMode (val) {
|
||||||
LoadBinCode: LoadBinCode,
|
BitMode = val;
|
||||||
LDisassemble: LDisassemble,
|
|
||||||
SetBasePosition: SetBasePosition,
|
|
||||||
CompatibilityMode: CompatibilityMode,
|
|
||||||
|
|
||||||
setBitMode: val => { BitMode = val; },
|
|
||||||
setShowInstructionHex: val => { ShowInstructionHex = val; },
|
|
||||||
setShowInstructionPos: val => { ShowInstructionPos = 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-switch";
|
||||||
import "bootstrap-colorpicker";
|
import "bootstrap-colorpicker";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import CanvasComponents from "../core/vendor/canvascomponents.js";
|
import * as CanvasComponents from "../core/lib/CanvasComponents";
|
||||||
|
|
||||||
// CyberChef
|
// CyberChef
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
const webpack = require("webpack");
|
const webpack = require("webpack");
|
||||||
const ExtractTextPlugin = require("extract-text-webpack-plugin");
|
const ExtractTextPlugin = require("extract-text-webpack-plugin");
|
||||||
const WebpackSyncShellPlugin = require("webpack-synchronizable-shell-plugin");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Webpack configuration details for use with Grunt.
|
* Webpack configuration details for use with Grunt.
|
||||||
|
@ -43,19 +42,7 @@ module.exports = {
|
||||||
raw: true,
|
raw: true,
|
||||||
entryOnly: true
|
entryOnly: true
|
||||||
}),
|
}),
|
||||||
new ExtractTextPlugin("styles.css"),
|
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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue