Converted the core to ES modules

This commit is contained in:
n1474335 2018-03-26 23:14:23 +01:00
parent c1bb93eec1
commit 9b4fc3d3aa
154 changed files with 1901 additions and 2223 deletions

View file

@ -5,7 +5,7 @@
"chrome": 40,
"firefox": 35,
"edge": 14,
"node": "6.5",
"node": "6.5"
},
"modules": false,
"useBuiltIns": true

View file

@ -1,2 +1 @@
src/core/lib/**
src/core/config/MetaConfig.js
src/core/vendor/**

View file

@ -89,7 +89,6 @@
"globals": {
"$": false,
"jQuery": false,
"moment": false,
"log": false,
"COMPILE_TIME": false,

3
.gitignore vendored
View file

@ -5,5 +5,4 @@ build
docs/*
!docs/*.conf.json
!docs/*.ico
.vscode
src/core/config/MetaConfig.js
.vscode

View file

@ -1,6 +1,6 @@
language: node_js
node_js:
- "8.4"
- node
install: npm install
before_script:
- npm install -g grunt

View file

@ -4,7 +4,8 @@ const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const NodeExternals = require("webpack-node-externals");
const Inliner = require("web-resource-inliner");
const fs = require("fs");
const glob = require("glob");
const path = require("path");
/**
* Grunt configuration for building the app in various formats.
@ -21,15 +22,15 @@ module.exports = function (grunt) {
// Tasks
grunt.registerTask("dev",
"A persistent task which creates a development build whenever source files are modified.",
["clean:dev", "concurrent:dev"]);
["clean:dev", "clean:config", "webpack-dev-server:start"]);
grunt.registerTask("node",
"Compiles CyberChef into a single NodeJS module.",
["clean:node", "webpack:metaConf", "webpack:node", "chmod:build"]);
["clean:node", "clean:config", "webpack:node", "chmod:build"]);
grunt.registerTask("test",
"A task which runs all the tests in test/tests.",
["clean:test", "webpack:metaConf", "webpack:tests", "execute:test"]);
["exec:tests"]);
grunt.registerTask("docs",
"Compiles documentation in the /docs directory.",
@ -37,7 +38,7 @@ module.exports = function (grunt) {
grunt.registerTask("prod",
"Creates a production-ready build. Use the --msg flag to add a compile message.",
["eslint", "clean:prod", "webpack:metaConf", "webpack:web", "inline", "chmod"]);
["eslint", "clean:prod", "clean:config", "webpack:web", "inline", "chmod"]);
grunt.registerTask("default",
"Lints the code base",
@ -62,9 +63,7 @@ module.exports = function (grunt) {
grunt.loadNpmTasks("grunt-contrib-copy");
grunt.loadNpmTasks("grunt-chmod");
grunt.loadNpmTasks("grunt-exec");
grunt.loadNpmTasks("grunt-execute");
grunt.loadNpmTasks("grunt-accessibility");
grunt.loadNpmTasks("grunt-concurrent");
// Project configuration
@ -118,12 +117,12 @@ module.exports = function (grunt) {
* Generates an entry list for all the modules.
*/
function listEntryModules() {
const path = "./src/core/config/modules/";
let entryModules = {};
fs.readdirSync(path).forEach(file => {
if (file !== "Default.js" && file !== "OpModules.js")
entryModules[file.split(".js")[0]] = path + file;
glob.sync("./src/core/config/modules/*.mjs").forEach(file => {
const basename = path.basename(file);
if (basename !== "Default.mjs" && basename !== "OpModules.mjs")
entryModules[basename.split(".mjs")[0]] = path.resolve(file);
});
return entryModules;
@ -132,9 +131,9 @@ module.exports = function (grunt) {
grunt.initConfig({
clean: {
dev: ["build/dev/*"],
prod: ["build/prod/*", "src/core/config/MetaConfig.js"],
test: ["build/test/*", "src/core/config/MetaConfig.js"],
node: ["build/node/*", "src/core/config/MetaConfig.js"],
prod: ["build/prod/*"],
node: ["build/node/*"],
config: ["src/core/config/OperationConfig.json", "src/core/config/modules/*"],
docs: ["docs/*", "!docs/*.conf.json", "!docs/*.ico", "!docs/*.png"],
inlineScripts: ["build/prod/scripts.js"],
},
@ -143,10 +142,10 @@ module.exports = function (grunt) {
configFile: "./.eslintrc.json"
},
configs: ["Gruntfile.js"],
core: ["src/core/**/*.js", "!src/core/lib/**/*", "!src/core/config/MetaConfig.js"],
web: ["src/web/**/*.js"],
node: ["src/node/**/*.js"],
tests: ["test/**/*.js"],
core: ["src/core/**/*.{js,mjs}", "!src/core/vendor/**/*"],
web: ["src/web/**/*.{js,mjs}"],
node: ["src/node/**/*.{js,mjs}"],
tests: ["test/**/*.{js,mjs}"],
},
jsdoc: {
options: {
@ -159,17 +158,11 @@ module.exports = function (grunt) {
all: {
src: [
"src/**/*.js",
"!src/core/lib/**/*",
"!src/core/config/MetaConfig.js"
"src/**/*.mjs",
"!src/core/vendor/**/*"
],
}
},
concurrent: {
options: {
logConcurrentOutput: true
},
dev: ["webpack:metaConfDev", "webpack-dev-server:start"]
},
accessibility: {
options: {
accessibilityLevel: "WCAG2A",
@ -184,39 +177,6 @@ module.exports = function (grunt) {
},
webpack: {
options: webpackConfig,
metaConf: {
mode: "production",
target: "node",
entry: [
"babel-polyfill",
"./src/core/config/OperationConfig.js"
],
output: {
filename: "MetaConfig.js",
path: __dirname + "/src/core/config/",
library: "MetaConfig",
libraryTarget: "commonjs2",
libraryExport: "default"
},
externals: [NodeExternals()],
},
metaConfDev: {
mode: "development",
target: "node",
entry: [
"babel-polyfill",
"./src/core/config/OperationConfig.js"
],
output: {
filename: "MetaConfig.js",
path: __dirname + "/src/core/config/",
library: "MetaConfig",
libraryTarget: "commonjs2",
libraryExport: "default"
},
externals: [NodeExternals()],
watch: true
},
web: {
mode: "production",
target: "web",
@ -229,7 +189,7 @@ module.exports = function (grunt) {
},
resolve: {
alias: {
"./config/modules/OpModules.js": "./config/modules/Default.js"
"./config/modules/OpModules": "./config/modules/Default"
}
},
plugins: [
@ -279,7 +239,7 @@ module.exports = function (grunt) {
tests: {
mode: "development",
target: "node",
entry: "./test/index.js",
entry: "./test/index.mjs",
externals: [NodeExternals()],
output: {
filename: "index.js",
@ -292,7 +252,7 @@ module.exports = function (grunt) {
node: {
mode: "production",
target: "node",
entry: "./src/node/index.js",
entry: "./src/node/index.mjs",
externals: [NodeExternals()],
output: {
filename: "CyberChef.js",
@ -330,7 +290,7 @@ module.exports = function (grunt) {
}, moduleEntryPoints),
resolve: {
alias: {
"./config/modules/OpModules.js": "./config/modules/Default.js"
"./config/modules/OpModules": "./config/modules/Default"
}
},
plugins: [
@ -401,10 +361,10 @@ module.exports = function (grunt) {
},
sitemap: {
command: "node build/prod/sitemap.js > build/prod/sitemap.xml"
},
tests: {
command: "node --experimental-modules test/index.mjs"
}
},
execute: {
test: "build/test/index.js"
},
});
};

63
package-lock.json generated
View file

@ -5300,26 +5300,6 @@
"shelljs": "0.5.3"
}
},
"grunt-concurrent": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/grunt-concurrent/-/grunt-concurrent-2.3.1.tgz",
"integrity": "sha1-Hj2zjM71o9oRleYdYx/n4yE0TSM=",
"dev": true,
"requires": {
"arrify": "1.0.1",
"async": "1.5.2",
"indent-string": "2.1.0",
"pad-stream": "1.2.0"
},
"dependencies": {
"async": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
"dev": true
}
}
},
"grunt-contrib-clean": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-1.1.0.tgz",
@ -5460,12 +5440,6 @@
"integrity": "sha512-cgAlreXf3muSYS5LzW0Cc4xHK03BjFOYk0MqCQ/MZ3k1Xz2GU7D+IAJg4UKicxpO+XdONJdx/NJ6kpy2wI+uHg==",
"dev": true
},
"grunt-execute": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.2.2.tgz",
"integrity": "sha1-TpRf5XlZzA3neZCDtrQq7ZYWNQo=",
"dev": true
},
"grunt-jsdoc": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/grunt-jsdoc/-/grunt-jsdoc-2.2.1.tgz",
@ -8419,19 +8393,6 @@
"integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==",
"dev": true
},
"pad-stream": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pad-stream/-/pad-stream-1.2.0.tgz",
"integrity": "sha1-Yx3Mn3mBC3BZZeid7eps/w/B38k=",
"dev": true,
"requires": {
"meow": "3.7.0",
"pumpify": "1.3.5",
"repeating": "2.0.1",
"split2": "1.1.1",
"through2": "2.0.3"
}
},
"pako": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
@ -11046,15 +11007,6 @@
"resolved": "https://registry.npmjs.org/split.js/-/split.js-1.3.5.tgz",
"integrity": "sha1-YuLOZtLPkcx3SqXwdJ/yUTgDn1A="
},
"split2": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/split2/-/split2-1.1.1.tgz",
"integrity": "sha1-Fi2bGIZfAqsvKtlYVSLbm1TEgfk=",
"dev": true,
"requires": {
"through2": "2.0.3"
}
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@ -12198,15 +12150,6 @@
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==",
"dev": true
},
"val-loader": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/val-loader/-/val-loader-1.1.0.tgz",
"integrity": "sha512-8m62XF42FcfrBBl02rtDY9hQhDcDczrEcr60/aSMxlzJiXAcbAimRPvsDoDa5QcGAusOgOmVTpFtK5EbfZdDwA==",
"dev": true,
"requires": {
"loader-utils": "1.1.0"
}
},
"valid-data-url": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.4.tgz",
@ -12709,6 +12652,12 @@
"integrity": "sha1-Iyxi7GCSsQBjWj0p2DwXRxKN+b0=",
"dev": true
},
"webpack-shell-plugin": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/webpack-shell-plugin/-/webpack-shell-plugin-0.5.0.tgz",
"integrity": "sha1-Kbih2A3erg3bEOcpZn9yhlPCx0I=",
"dev": true
},
"webpack-sources": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.0.1.tgz",

View file

@ -41,12 +41,10 @@
"grunt": ">=1.0.2",
"grunt-accessibility": "~6.0.0",
"grunt-chmod": "~1.1.1",
"grunt-concurrent": "^2.3.1",
"grunt-contrib-clean": "~1.1.0",
"grunt-contrib-copy": "~1.0.0",
"grunt-eslint": "^20.1.0",
"grunt-exec": "~3.0.0",
"grunt-execute": "^0.2.2",
"grunt-jsdoc": "^2.2.1",
"grunt-webpack": "^3.0.2",
"html-webpack-plugin": "^3.0.4",
@ -61,11 +59,11 @@
"sitemap": "^1.13.0",
"style-loader": "^0.20.2",
"url-loader": "^0.6.2",
"val-loader": "^1.1.0",
"web-resource-inliner": "^4.2.1",
"webpack": "^4.0.1",
"webpack-dev-server": "^3.1.0",
"webpack-node-externals": "^1.6.0",
"webpack-shell-plugin": "^0.5.0",
"worker-loader": "^1.1.1"
},
"dependencies": {

View file

@ -1,165 +0,0 @@
import Dish from "./Dish.js";
import Recipe from "./Recipe.js";
/**
* The main controller for CyberChef.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @class
*/
const Chef = function() {
this.dish = new Dish();
};
/**
* Runs the recipe over the input.
*
* @param {string|ArrayBuffer} input - The input data as a string or ArrayBuffer
* @param {Object[]} recipeConfig - The recipe configuration object
* @param {Object} options - The options object storing various user choices
* @param {boolean} options.attempHighlight - Whether or not to attempt highlighting
* @param {number} progress - The position in the recipe to start from
* @param {number} [step] - Whether to only execute one operation in the recipe
*
* @returns {Object} response
* @returns {string} response.result - The output of the recipe
* @returns {string} response.type - The data type of the result
* @returns {number} response.progress - The position that we have got to in the recipe
* @returns {number} response.duration - The number of ms it took to execute the recipe
* @returns {number} response.error - The error object thrown by a failed operation (false if no error)
*/
Chef.prototype.bake = async function(input, recipeConfig, options, progress, step) {
log.debug("Chef baking");
const startTime = new Date().getTime(),
recipe = new Recipe(recipeConfig),
containsFc = recipe.containsFlowControl(),
notUTF8 = options && options.hasOwnProperty("treatAsUtf8") && !options.treatAsUtf8;
let error = false;
if (containsFc && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
// Clean up progress
if (progress >= recipeConfig.length) {
progress = 0;
}
if (step) {
// Unset breakpoint on this step
recipe.setBreakpoint(progress, false);
// Set breakpoint on next step
recipe.setBreakpoint(progress + 1, true);
}
// If stepping with flow control, we have to start from the beginning
// but still want to skip all previous breakpoints
if (progress > 0 && containsFc) {
recipe.removeBreaksUpTo(progress);
progress = 0;
}
// If starting from scratch, load data
if (progress === 0) {
const type = input instanceof ArrayBuffer ? Dish.ARRAY_BUFFER : Dish.STRING;
this.dish.set(input, type);
}
try {
progress = await recipe.execute(this.dish, progress);
} catch (err) {
log.error(err);
error = {
displayStr: err.displayStr,
};
progress = err.progress;
}
// Depending on the size of the output, we may send it back as a string or an ArrayBuffer.
// This can prevent unnecessary casting as an ArrayBuffer can be easily downloaded as a file.
// The threshold is specified in KiB.
const threshold = (options.ioDisplayThreshold || 1024) * 1024;
const returnType = this.dish.size() > threshold ? Dish.ARRAY_BUFFER : Dish.STRING;
return {
result: this.dish.type === Dish.HTML ?
this.dish.get(Dish.HTML, notUTF8) :
this.dish.get(returnType, notUTF8),
type: Dish.enumLookup(this.dish.type),
progress: progress,
duration: new Date().getTime() - startTime,
error: error
};
};
/**
* When a browser tab is unfocused and the browser has to run lots of dynamic content in other tabs,
* it swaps out the memory for that tab. If the CyberChef tab has been unfocused for more than a
* minute, we run a silent bake which will force the browser to load and cache all the relevant
* JavaScript code needed to do a real bake.
*
* This will stop baking taking a long time when the CyberChef browser tab has been unfocused for a
* long time and the browser has swapped out all its memory.
*
* The output will not be modified (hence "silent" bake).
*
* This will only actually execute the recipe if auto-bake is enabled, otherwise it will just load
* the recipe, ingredients and dish.
*
* @param {Object[]} recipeConfig - The recipe configuration object
* @returns {number} The time it took to run the silent bake in milliseconds.
*/
Chef.prototype.silentBake = function(recipeConfig) {
log.debug("Running silent bake");
let startTime = new Date().getTime(),
recipe = new Recipe(recipeConfig),
dish = new Dish("", Dish.STRING);
try {
recipe.execute(dish);
} catch (err) {
// Suppress all errors
}
return new Date().getTime() - startTime;
};
/**
* Calculates highlight offsets if possible.
*
* @param {Object[]} recipeConfig
* @param {string} direction
* @param {Object} pos - The position object for the highlight.
* @param {number} pos.start - The start offset.
* @param {number} pos.end - The end offset.
* @returns {Object}
*/
Chef.prototype.calculateHighlights = function(recipeConfig, direction, pos) {
const recipe = new Recipe(recipeConfig);
const highlights = recipe.generateHighlightList();
if (!highlights) return false;
for (let i = 0; i < highlights.length; i++) {
// Remove multiple highlights before processing again
pos = [pos[0]];
const func = direction === "forward" ? highlights[i].f : highlights[i].b;
if (typeof func == "function") {
pos = func(pos, highlights[i].args);
}
}
return {
pos: pos,
direction: direction
};
};
export default Chef;

172
src/core/Chef.mjs Executable file
View file

@ -0,0 +1,172 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Dish from "./Dish";
import Recipe from "./Recipe";
import log from "loglevel";
/**
* The main controller for CyberChef.
*/
class Chef {
/**
* Chef constructor
*/
constructor() {
this.dish = new Dish();
}
/**
* Runs the recipe over the input.
*
* @param {string|ArrayBuffer} input - The input data as a string or ArrayBuffer
* @param {Object[]} recipeConfig - The recipe configuration object
* @param {Object} options - The options object storing various user choices
* @param {boolean} options.attempHighlight - Whether or not to attempt highlighting
* @param {number} progress - The position in the recipe to start from
* @param {number} [step] - Whether to only execute one operation in the recipe
*
* @returns {Object} response
* @returns {string} response.result - The output of the recipe
* @returns {string} response.type - The data type of the result
* @returns {number} response.progress - The position that we have got to in the recipe
* @returns {number} response.duration - The number of ms it took to execute the recipe
* @returns {number} response.error - The error object thrown by a failed operation (false if no error)
*/
async bake(input, recipeConfig, options, progress, step) {
log.debug("Chef baking");
const startTime = new Date().getTime(),
recipe = new Recipe(recipeConfig),
containsFc = recipe.containsFlowControl(),
notUTF8 = options && options.hasOwnProperty("treatAsUtf8") && !options.treatAsUtf8;
let error = false;
if (containsFc && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
// Clean up progress
if (progress >= recipeConfig.length) {
progress = 0;
}
if (step) {
// Unset breakpoint on this step
recipe.setBreakpoint(progress, false);
// Set breakpoint on next step
recipe.setBreakpoint(progress + 1, true);
}
// If stepping with flow control, we have to start from the beginning
// but still want to skip all previous breakpoints
if (progress > 0 && containsFc) {
recipe.removeBreaksUpTo(progress);
progress = 0;
}
// If starting from scratch, load data
if (progress === 0) {
const type = input instanceof ArrayBuffer ? Dish.ARRAY_BUFFER : Dish.STRING;
this.dish.set(input, type);
}
try {
progress = await recipe.execute(this.dish, progress);
} catch (err) {
log.error(err);
error = {
displayStr: err.displayStr,
};
progress = err.progress;
}
// Depending on the size of the output, we may send it back as a string or an ArrayBuffer.
// This can prevent unnecessary casting as an ArrayBuffer can be easily downloaded as a file.
// The threshold is specified in KiB.
const threshold = (options.ioDisplayThreshold || 1024) * 1024;
const returnType = this.dish.size > threshold ? Dish.ARRAY_BUFFER : Dish.STRING;
return {
result: this.dish.type === Dish.HTML ?
this.dish.get(Dish.HTML, notUTF8) :
this.dish.get(returnType, notUTF8),
type: Dish.enumLookup(this.dish.type),
progress: progress,
duration: new Date().getTime() - startTime,
error: error
};
}
/**
* When a browser tab is unfocused and the browser has to run lots of dynamic content in other tabs,
* it swaps out the memory for that tab. If the CyberChef tab has been unfocused for more than a
* minute, we run a silent bake which will force the browser to load and cache all the relevant
* JavaScript code needed to do a real bake.
*
* This will stop baking taking a long time when the CyberChef browser tab has been unfocused for a
* long time and the browser has swapped out all its memory.
*
* The output will not be modified (hence "silent" bake).
*
* This will only actually execute the recipe if auto-bake is enabled, otherwise it will just load
* the recipe, ingredients and dish.
*
* @param {Object[]} recipeConfig - The recipe configuration object
* @returns {number} The time it took to run the silent bake in milliseconds.
*/
silentBake(recipeConfig) {
log.debug("Running silent bake");
let startTime = new Date().getTime(),
recipe = new Recipe(recipeConfig),
dish = new Dish("", Dish.STRING);
try {
recipe.execute(dish);
} catch (err) {
// Suppress all errors
}
return new Date().getTime() - startTime;
}
/**
* Calculates highlight offsets if possible.
*
* @param {Object[]} recipeConfig
* @param {string} direction
* @param {Object} pos - The position object for the highlight.
* @param {number} pos.start - The start offset.
* @param {number} pos.end - The end offset.
* @returns {Object}
*/
calculateHighlights(recipeConfig, direction, pos) {
const recipe = new Recipe(recipeConfig);
const highlights = recipe.generateHighlightList();
if (!highlights) return false;
for (let i = 0; i < highlights.length; i++) {
// Remove multiple highlights before processing again
pos = [pos[0]];
const func = direction === "forward" ? highlights[i].f : highlights[i].b;
if (typeof func == "function") {
pos = func(pos, highlights[i].args);
}
}
return {
pos: pos,
direction: direction
};
}
}
export default Chef;

View file

@ -7,9 +7,9 @@
*/
import "babel-polyfill";
import Chef from "./Chef.js";
import OperationConfig from "./config/MetaConfig.js";
import OpModules from "./config/modules/Default.js";
import Chef from "./Chef";
import OperationConfig from "./config/OperationConfig.json";
import OpModules from "./config/modules/Default";
// Add ">" to the start of all log messages in the Chef Worker
import loglevelMessagePrefix from "loglevel-message-prefix";

View file

@ -1,274 +0,0 @@
import Utils from "./Utils.js";
import BigNumber from "bignumber.js";
/**
* The data being operated on by each operation.
*
* @author n1474335 [n1474335@gmail.com]
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @class
* @param {byteArray|string|number|ArrayBuffer|BigNumber} value - The value of the input data.
* @param {number} type - The data type of value, see Dish enums.
*/
const Dish = function(value, type) {
this.value = value || typeof value === "string" ? value : null;
this.type = type || Dish.BYTE_ARRAY;
};
/**
* Dish data type enum for byte arrays.
* @readonly
* @enum
*/
Dish.BYTE_ARRAY = 0;
/**
* Dish data type enum for strings.
* @readonly
* @enum
*/
Dish.STRING = 1;
/**
* Dish data type enum for numbers.
* @readonly
* @enum
*/
Dish.NUMBER = 2;
/**
* Dish data type enum for HTML.
* @readonly
* @enum
*/
Dish.HTML = 3;
/**
* Dish data type enum for ArrayBuffers.
* @readonly
* @enum
*/
Dish.ARRAY_BUFFER = 4;
/**
* Dish data type enum for BigNumbers.
* @readonly
* @enum
*/
Dish.BIG_NUMBER = 5;
/**
* Returns the data type enum for the given type string.
*
* @static
* @param {string} typeStr - The name of the data type.
* @returns {number} The data type enum value.
*/
Dish.typeEnum = function(typeStr) {
switch (typeStr.toLowerCase()) {
case "bytearray":
case "byte array":
return Dish.BYTE_ARRAY;
case "string":
return Dish.STRING;
case "number":
return Dish.NUMBER;
case "html":
return Dish.HTML;
case "arraybuffer":
case "array buffer":
return Dish.ARRAY_BUFFER;
case "bignumber":
case "big number":
return Dish.BIG_NUMBER;
default:
throw "Invalid data type string. No matching enum.";
}
};
/**
* Returns the data type string for the given type enum.
*
* @static
* @param {number} typeEnum - The enum value of the data type.
* @returns {string} The data type as a string.
*/
Dish.enumLookup = function(typeEnum) {
switch (typeEnum) {
case Dish.BYTE_ARRAY:
return "byteArray";
case Dish.STRING:
return "string";
case Dish.NUMBER:
return "number";
case Dish.HTML:
return "html";
case Dish.ARRAY_BUFFER:
return "ArrayBuffer";
case Dish.BIG_NUMBER:
return "BigNumber";
default:
throw "Invalid data type enum. No matching type.";
}
};
/**
* Sets the data value and type and then validates them.
*
* @param {byteArray|string|number|ArrayBuffer|BigNumber} value - The value of the input data.
* @param {number} type - The data type of value, see Dish enums.
*/
Dish.prototype.set = function(value, type) {
log.debug("Dish type: " + Dish.enumLookup(type));
this.value = value;
this.type = type;
if (!this.valid()) {
const sample = Utils.truncate(JSON.stringify(this.value), 13);
throw "Data is not a valid " + Dish.enumLookup(type) + ": " + sample;
}
};
/**
* Returns the value of the data in the type format specified.
*
* @param {number} type - The data type of value, see Dish enums.
* @param {boolean} [notUTF8] - Do not treat strings as UTF8.
* @returns {byteArray|string|number|ArrayBuffer|BigNumber} The value of the output data.
*/
Dish.prototype.get = function(type, notUTF8) {
if (this.type !== type) {
this.translate(type, notUTF8);
}
return this.value;
};
/**
* Translates the data to the given type format.
*
* @param {number} toType - The data type of value, see Dish enums.
* @param {boolean} [notUTF8] - Do not treat strings as UTF8.
*/
Dish.prototype.translate = function(toType, notUTF8) {
log.debug(`Translating Dish from ${Dish.enumLookup(this.type)} to ${Dish.enumLookup(toType)}`);
const byteArrayToStr = notUTF8 ? Utils.byteArrayToChars : Utils.byteArrayToUtf8;
// Convert data to intermediate byteArray type
switch (this.type) {
case Dish.STRING:
this.value = this.value ? Utils.strToByteArray(this.value) : [];
break;
case Dish.NUMBER:
this.value = typeof this.value == "number" ? Utils.strToByteArray(this.value.toString()) : [];
break;
case Dish.HTML:
this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : [];
break;
case Dish.ARRAY_BUFFER:
// Array.from() would be nicer here, but it's slightly slower
this.value = Array.prototype.slice.call(new Uint8Array(this.value));
break;
case Dish.BIG_NUMBER:
this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toFixed()) : [];
break;
default:
break;
}
this.type = Dish.BYTE_ARRAY;
// Convert from byteArray to toType
switch (toType) {
case Dish.STRING:
case Dish.HTML:
this.value = this.value ? byteArrayToStr(this.value) : "";
this.type = Dish.STRING;
break;
case Dish.NUMBER:
this.value = this.value ? parseFloat(byteArrayToStr(this.value)) : 0;
this.type = Dish.NUMBER;
break;
case Dish.ARRAY_BUFFER:
this.value = new Uint8Array(this.value).buffer;
this.type = Dish.ARRAY_BUFFER;
break;
case Dish.BIG_NUMBER:
try {
this.value = new BigNumber(byteArrayToStr(this.value));
} catch (err) {
this.value = new BigNumber(NaN);
}
this.type = Dish.BIG_NUMBER;
break;
default:
break;
}
};
/**
* Validates that the value is the type that has been specified.
* May have to disable parts of BYTE_ARRAY validation if it effects performance.
*
* @returns {boolean} Whether the data is valid or not.
*/
Dish.prototype.valid = function() {
switch (this.type) {
case Dish.BYTE_ARRAY:
if (!(this.value instanceof Array)) {
return false;
}
// Check that every value is a number between 0 - 255
for (let i = 0; i < this.value.length; i++) {
if (typeof this.value[i] !== "number" ||
this.value[i] < 0 ||
this.value[i] > 255) {
return false;
}
}
return true;
case Dish.STRING:
case Dish.HTML:
return typeof this.value === "string";
case Dish.NUMBER:
return typeof this.value === "number";
case Dish.ARRAY_BUFFER:
return this.value instanceof ArrayBuffer;
case Dish.BIG_NUMBER:
return this.value instanceof BigNumber;
default:
return false;
}
};
/**
* Determines how much space the Dish takes up.
* Numbers in JavaScript are 64-bit floating point, however for the purposes of the Dish,
* we measure how many bytes are taken up when the number is written as a string.
*
* @returns {number}
*/
Dish.prototype.size = function() {
switch (this.type) {
case Dish.BYTE_ARRAY:
case Dish.STRING:
case Dish.HTML:
return this.value.length;
case Dish.NUMBER:
case Dish.BIG_NUMBER:
return this.value.toString().length;
case Dish.ARRAY_BUFFER:
return this.value.byteLength;
default:
return -1;
}
};
export default Dish;

293
src/core/Dish.mjs Executable file
View file

@ -0,0 +1,293 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Utils from "./Utils";
import BigNumber from "bignumber.js";
import log from "loglevel";
/**
* The data being operated on by each operation.
*/
class Dish {
/**
* Dish constructor
*
* @param {byteArray|string|number|ArrayBuffer|BigNumber} [value=null]
* - The value of the input data.
* @param {number} [type=Dish.BYTE_ARRAY]
* - The data type of value, see Dish enums.
*/
constructor(value=null, type=Dish.BYTE_ARRAY) {
this.value = value;
this.type = type;
}
/**
* Returns the data type enum for the given type string.
*
* @param {string} typeStr - The name of the data type.
* @returns {number} The data type enum value.
*/
static typeEnum(typeStr) {
switch (typeStr.toLowerCase()) {
case "bytearray":
case "byte array":
return Dish.BYTE_ARRAY;
case "string":
return Dish.STRING;
case "number":
return Dish.NUMBER;
case "html":
return Dish.HTML;
case "arraybuffer":
case "array buffer":
return Dish.ARRAY_BUFFER;
case "bignumber":
case "big number":
return Dish.BIG_NUMBER;
default:
throw "Invalid data type string. No matching enum.";
}
}
/**
* Returns the data type string for the given type enum.
*
* @param {number} typeEnum - The enum value of the data type.
* @returns {string} The data type as a string.
*/
static enumLookup(typeEnum) {
switch (typeEnum) {
case Dish.BYTE_ARRAY:
return "byteArray";
case Dish.STRING:
return "string";
case Dish.NUMBER:
return "number";
case Dish.HTML:
return "html";
case Dish.ARRAY_BUFFER:
return "ArrayBuffer";
case Dish.BIG_NUMBER:
return "BigNumber";
default:
throw "Invalid data type enum. No matching type.";
}
}
/**
* Sets the data value and type and then validates them.
*
* @param {byteArray|string|number|ArrayBuffer|BigNumber} value
* - The value of the input data.
* @param {number} type
* - The data type of value, see Dish enums.
*/
set(value, type) {
if (typeof type === "string") {
type = Dish.typeEnum(type);
}
log.debug("Dish type: " + Dish.enumLookup(type));
this.value = value;
this.type = type;
if (!this.valid()) {
const sample = Utils.truncate(JSON.stringify(this.value), 13);
throw "Data is not a valid " + Dish.enumLookup(type) + ": " + sample;
}
}
/**
* Returns the value of the data in the type format specified.
*
* @param {number} type - The data type of value, see Dish enums.
* @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
* @returns {byteArray|string|number|ArrayBuffer|BigNumber}
* The value of the output data.
*/
get(type, notUTF8=false) {
if (typeof type === "string") {
type = Dish.typeEnum(type);
}
if (this.type !== type) {
this.translate(type, notUTF8);
}
return this.value;
}
/**
* Translates the data to the given type format.
*
* @param {number} toType - The data type of value, see Dish enums.
* @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
*/
translate(toType, notUTF8=false) {
log.debug(`Translating Dish from ${Dish.enumLookup(this.type)} to ${Dish.enumLookup(toType)}`);
const byteArrayToStr = notUTF8 ? Utils.byteArrayToChars : Utils.byteArrayToUtf8;
// Convert data to intermediate byteArray type
switch (this.type) {
case Dish.STRING:
this.value = this.value ? Utils.strToByteArray(this.value) : [];
break;
case Dish.NUMBER:
this.value = typeof this.value == "number" ? Utils.strToByteArray(this.value.toString()) : [];
break;
case Dish.HTML:
this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : [];
break;
case Dish.ARRAY_BUFFER:
// Array.from() would be nicer here, but it's slightly slower
this.value = Array.prototype.slice.call(new Uint8Array(this.value));
break;
case Dish.BIG_NUMBER:
this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toFixed()) : [];
break;
default:
break;
}
this.type = Dish.BYTE_ARRAY;
// Convert from byteArray to toType
switch (toType) {
case Dish.STRING:
case Dish.HTML:
this.value = this.value ? byteArrayToStr(this.value) : "";
this.type = Dish.STRING;
break;
case Dish.NUMBER:
this.value = this.value ? parseFloat(byteArrayToStr(this.value)) : 0;
this.type = Dish.NUMBER;
break;
case Dish.ARRAY_BUFFER:
this.value = new Uint8Array(this.value).buffer;
this.type = Dish.ARRAY_BUFFER;
break;
case Dish.BIG_NUMBER:
try {
this.value = new BigNumber(byteArrayToStr(this.value));
} catch (err) {
this.value = new BigNumber(NaN);
}
this.type = Dish.BIG_NUMBER;
break;
default:
break;
}
}
/**
* Validates that the value is the type that has been specified.
* May have to disable parts of BYTE_ARRAY validation if it effects performance.
*
* @returns {boolean} Whether the data is valid or not.
*/
valid() {
switch (this.type) {
case Dish.BYTE_ARRAY:
if (!(this.value instanceof Array)) {
return false;
}
// Check that every value is a number between 0 - 255
for (let i = 0; i < this.value.length; i++) {
if (typeof this.value[i] !== "number" ||
this.value[i] < 0 ||
this.value[i] > 255) {
return false;
}
}
return true;
case Dish.STRING:
case Dish.HTML:
return typeof this.value === "string";
case Dish.NUMBER:
return typeof this.value === "number";
case Dish.ARRAY_BUFFER:
return this.value instanceof ArrayBuffer;
case Dish.BIG_NUMBER:
return this.value instanceof BigNumber;
default:
return false;
}
}
/**
* Determines how much space the Dish takes up.
* Numbers in JavaScript are 64-bit floating point, however for the purposes of the Dish,
* we measure how many bytes are taken up when the number is written as a string.
*
* @returns {number}
*/
get size() {
switch (this.type) {
case Dish.BYTE_ARRAY:
case Dish.STRING:
case Dish.HTML:
return this.value.length;
case Dish.NUMBER:
case Dish.BIG_NUMBER:
return this.value.toString().length;
case Dish.ARRAY_BUFFER:
return this.value.byteLength;
default:
return -1;
}
}
}
/**
* Dish data type enum for byte arrays.
* @readonly
* @enum
*/
Dish.BYTE_ARRAY = 0;
/**
* Dish data type enum for strings.
* @readonly
* @enum
*/
Dish.STRING = 1;
/**
* Dish data type enum for numbers.
* @readonly
* @enum
*/
Dish.NUMBER = 2;
/**
* Dish data type enum for HTML.
* @readonly
* @enum
*/
Dish.HTML = 3;
/**
* Dish data type enum for ArrayBuffers.
* @readonly
* @enum
*/
Dish.ARRAY_BUFFER = 4;
/**
* Dish data type enum for BigNumbers.
* @readonly
* @enum
*/
Dish.BIG_NUMBER = 5;
export default Dish;

View file

@ -1,5 +1,5 @@
import Recipe from "./Recipe.js";
import Dish from "./Dish.js";
import Recipe from "./Recipe";
import Dish from "./Dish";
/**
@ -27,7 +27,7 @@ const FlowControl = {
inputType = opList[state.progress].inputType,
outputType = opList[state.progress].outputType,
input = state.dish.get(inputType),
ings = opList[state.progress].getIngValues(),
ings = opList[state.progress].ingValues,
splitDelim = ings[0],
mergeDelim = ings[1],
ignoreErrors = ings[2],
@ -41,7 +41,7 @@ const FlowControl = {
// Create subOpList for each tranche to operate on
// (all remaining operations unless we encounter a Merge)
for (i = state.progress + 1; i < opList.length; i++) {
if (opList[i].name === "Merge" && !opList[i].isDisabled()) {
if (opList[i].name === "Merge" && !opList[i].disabled) {
break;
} else {
subOpList.push(opList[i]);
@ -57,7 +57,7 @@ const FlowControl = {
recipe.addOperations(subOpList);
// Take a deep(ish) copy of the ingredient values
const ingValues = subOpList.map(op => JSON.parse(JSON.stringify(op.getIngValues())));
const ingValues = subOpList.map(op => JSON.parse(JSON.stringify(op.ingValues)));
// Run recipe over each tranche
for (i = 0; i < inputs.length; i++) {
@ -65,7 +65,7 @@ const FlowControl = {
// Baseline ing values for each tranche so that registers are reset
subOpList.forEach((op, i) => {
op.setIngValues(JSON.parse(JSON.stringify(ingValues[i])));
op.ingValues = JSON.parse(JSON.stringify(ingValues[i]));
});
const dish = new Dish(inputs[i], inputType);
@ -112,7 +112,7 @@ const FlowControl = {
* @returns {Object} The updated state of the recipe.
*/
runRegister: function(state) {
const ings = state.opList[state.progress].getIngValues(),
const ings = state.opList[state.progress].ingValues,
extractorStr = ings[0],
i = ings[1],
m = ings[2];
@ -150,9 +150,9 @@ const FlowControl = {
// Step through all subsequent ops and replace registers in args with extracted content
for (let i = state.progress + 1; i < state.opList.length; i++) {
if (state.opList[i].isDisabled()) continue;
if (state.opList[i].disabled) continue;
let args = state.opList[i].getIngValues();
let args = state.opList[i].ingValues;
args = args.map(arg => {
if (typeof arg !== "string" && typeof arg !== "object") return arg;
@ -181,7 +181,7 @@ const FlowControl = {
* @returns {Object} The updated state of the recipe.
*/
runJump: function(state) {
const ings = state.opList[state.progress].getIngValues(),
const ings = state.opList[state.progress].ingValues,
label = ings[0],
maxJumps = ings[1],
jmpIndex = FlowControl._getLabelIndex(label, state);
@ -209,7 +209,7 @@ const FlowControl = {
* @returns {Object} The updated state of the recipe.
*/
runCondJump: function(state) {
const ings = state.opList[state.progress].getIngValues(),
const ings = state.opList[state.progress].ingValues,
dish = state.dish,
regexStr = ings[0],
invert = ings[1],
@ -276,7 +276,7 @@ const FlowControl = {
for (let o = 0; o < state.opList.length; o++) {
let operation = state.opList[o];
if (operation.name === "Label"){
let ings = operation.getIngValues();
let ings = operation.ingValues;
if (name === ings[0]) {
return o;
}

View file

@ -1,92 +0,0 @@
import Utils from "./Utils.js";
/**
* The arguments to operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @class
* @param {Object} ingredientConfig
*/
const Ingredient = function(ingredientConfig) {
this.name = "";
this.type = "";
this.value = null;
if (ingredientConfig) {
this._parseConfig(ingredientConfig);
}
};
/**
* Reads and parses the given config.
*
* @private
* @param {Object} ingredientConfig
*/
Ingredient.prototype._parseConfig = function(ingredientConfig) {
this.name = ingredientConfig.name;
this.type = ingredientConfig.type;
};
/**
* Returns the value of the Ingredient as it should be displayed in a recipe config.
*
* @returns {*}
*/
Ingredient.prototype.getConfig = function() {
return this.value;
};
/**
* Sets the value of the Ingredient.
*
* @param {*} value
*/
Ingredient.prototype.setValue = function(value) {
this.value = Ingredient.prepare(value, this.type);
};
/**
* Most values will be strings when they are entered. This function converts them to the correct
* type.
*
* @static
* @param {*} data
* @param {string} type - The name of the data type.
*/
Ingredient.prepare = function(data, type) {
let number;
switch (type) {
case "binaryString":
case "binaryShortString":
case "editableOption":
return Utils.parseEscapedChars(data);
case "byteArray":
if (typeof data == "string") {
data = data.replace(/\s+/g, "");
return Utils.fromHex(data);
} else {
return data;
}
case "number":
number = parseFloat(data);
if (isNaN(number)) {
const sample = Utils.truncate(data.toString(), 10);
throw "Invalid ingredient value. Not a number: " + sample;
}
return number;
default:
return data;
}
};
export default Ingredient;

109
src/core/Ingredient.mjs Executable file
View file

@ -0,0 +1,109 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Utils from "./Utils";
/**
* The arguments to operations.
*/
class Ingredient {
/**
* Ingredient constructor
*
* @param {Object} ingredientConfig
*/
constructor(ingredientConfig) {
this.name = "";
this.type = "";
this._value = null;
if (ingredientConfig) {
this._parseConfig(ingredientConfig);
}
}
/**
* Reads and parses the given config.
*
* @private
* @param {Object} ingredientConfig
*/
_parseConfig(ingredientConfig) {
this.name = ingredientConfig.name;
this.type = ingredientConfig.type;
this.defaultValue = ingredientConfig.value;
}
/**
* Returns the value of the Ingredient as it should be displayed in a recipe config.
*
* @returns {*}
*/
get config() {
return this._value;
}
/**
* Sets the value of the Ingredient.
*
* @param {*} value
*/
set value(value) {
this._value = Ingredient.prepare(value, this.type);
}
/**
* Gets the value of the Ingredient.
*
* @returns {*}
*/
get value() {
return this._value;
}
/**
* Most values will be strings when they are entered. This function converts them to the correct
* type.
*
* @param {*} data
* @param {string} type - The name of the data type.
*/
static prepare(data, type) {
let number;
switch (type) {
case "binaryString":
case "binaryShortString":
case "editableOption":
return Utils.parseEscapedChars(data);
case "byteArray":
if (typeof data == "string") {
data = data.replace(/\s+/g, "");
return Utils.fromHex(data);
} else {
return data;
}
case "number":
number = parseFloat(data);
if (isNaN(number)) {
const sample = Utils.truncate(data.toString(), 10);
throw "Invalid ingredient value. Not a number: " + sample;
}
return number;
default:
return data;
}
}
}
export default Ingredient;

View file

@ -1,174 +0,0 @@
import Dish from "./Dish.js";
import Ingredient from "./Ingredient.js";
import OperationConfig from "./config/MetaConfig.js";
import OpModules from "./config/modules/OpModules.js";
/**
* The Operation specified by the user to be run.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @class
* @param {string} operationName
*/
const Operation = function(operationName) {
this.name = operationName;
this.module = "";
this.description = "";
this.inputType = -1;
this.outputType = -1;
this.run = null;
this.highlight = null;
this.highlightReverse = null;
this.breakpoint = false;
this.disabled = false;
this.ingList = [];
if (OperationConfig.hasOwnProperty(this.name)) {
this._parseConfig(OperationConfig[this.name]);
}
};
/**
* Reads and parses the given config.
*
* @private
* @param {Object} operationConfig
*/
Operation.prototype._parseConfig = function(operationConfig) {
this.module = operationConfig.module;
this.description = operationConfig.description;
this.inputType = Dish.typeEnum(operationConfig.inputType);
this.outputType = Dish.typeEnum(operationConfig.outputType);
this.highlight = operationConfig.highlight;
this.highlightReverse = operationConfig.highlightReverse;
this.flowControl = operationConfig.flowControl;
this.run = OpModules[this.module][this.name];
for (let a = 0; a < operationConfig.args.length; a++) {
const ingredientConfig = operationConfig.args[a];
const ingredient = new Ingredient(ingredientConfig);
this.addIngredient(ingredient);
}
if (this.highlight === "func") {
this.highlight = OpModules[this.module][`${this.name}-highlight`];
}
if (this.highlightReverse === "func") {
this.highlightReverse = OpModules[this.module][`${this.name}-highlightReverse`];
}
};
/**
* Returns the value of the Operation as it should be displayed in a recipe config.
*
* @returns {Object}
*/
Operation.prototype.getConfig = function() {
const ingredientConfig = [];
for (let o = 0; o < this.ingList.length; o++) {
ingredientConfig.push(this.ingList[o].getConfig());
}
const operationConfig = {
"op": this.name,
"args": ingredientConfig
};
return operationConfig;
};
/**
* Adds a new Ingredient to this Operation.
*
* @param {Ingredient} ingredient
*/
Operation.prototype.addIngredient = function(ingredient) {
this.ingList.push(ingredient);
};
/**
* Set the Ingredient values for this Operation.
*
* @param {Object[]} ingValues
*/
Operation.prototype.setIngValues = function(ingValues) {
for (let i = 0; i < ingValues.length; i++) {
this.ingList[i].setValue(ingValues[i]);
}
};
/**
* Get the Ingredient values for this Operation.
*
* @returns {Object[]}
*/
Operation.prototype.getIngValues = function() {
const ingValues = [];
for (let i = 0; i < this.ingList.length; i++) {
ingValues.push(this.ingList[i].value);
}
return ingValues;
};
/**
* Set whether this Operation has a breakpoint.
*
* @param {boolean} value
*/
Operation.prototype.setBreakpoint = function(value) {
this.breakpoint = !!value;
};
/**
* Returns true if this Operation has a breakpoint set.
*
* @returns {boolean}
*/
Operation.prototype.isBreakpoint = function() {
return this.breakpoint;
};
/**
* Set whether this Operation is disabled.
*
* @param {boolean} value
*/
Operation.prototype.setDisabled = function(value) {
this.disabled = !!value;
};
/**
* Returns true if this Operation is disabled.
*
* @returns {boolean}
*/
Operation.prototype.isDisabled = function() {
return this.disabled;
};
/**
* Returns true if this Operation is a flow control.
*
* @returns {boolean}
*/
Operation.prototype.isFlowControl = function() {
return this.flowControl;
};
export default Operation;

239
src/core/Operation.mjs Executable file
View file

@ -0,0 +1,239 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Dish from "./Dish";
import Ingredient from "./Ingredient";
/**
* The Operation specified by the user to be run.
*/
class Operation {
/**
* Operation constructor
*/
constructor() {
// Private fields
this._inputType = -1;
this._outputType = -1;
this._breakpoint = false;
this._disabled = false;
this._flowControl = false;
this._ingList = [];
// Public fields
this.name = "";
this.module = "";
this.description = "";
}
/**
* Interface for operation runner
*
* @param {*} input
* @param {Object[]} args
* @returns {*}
*/
run(input, args) {
return input;
}
/**
* Interface for forward highlighter
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return false;
}
/**
* Interface for reverse highlighter
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return false;
}
/**
* Sets the input type as a Dish enum.
*
* @param {string} typeStr
*/
set inputType(typeStr) {
this._inputType = Dish.typeEnum(typeStr);
}
/**
* Gets the input type as a readable string.
*
* @returns {string}
*/
get inputType() {
return Dish.enumLookup(this._inputType);
}
/**
* Sets the output type as a Dish enum.
*
* @param {string} typeStr
*/
set outputType(typeStr) {
this._outputType = Dish.typeEnum(typeStr);
}
/**
* Gets the output type as a readable string.
*
* @returns {string}
*/
get outputType() {
return Dish.enumLookup(this._outputType);
}
/**
* Sets the args for the current operation.
*
* @param {Object[]} conf
*/
set args(conf) {
conf.forEach(arg => {
const ingredient = new Ingredient(arg);
this.addIngredient(ingredient);
});
}
/**
* Gets the args for the current operation.
*
* @param {Object[]} conf
*/
get args() {
return this._ingList.map(ing => {
return {
name: ing.name,
type: ing.type,
value: ing.defaultValue
};
});
}
/**
* Returns the value of the Operation as it should be displayed in a recipe config.
*
* @returns {Object}
*/
get config() {
return {
"op": this.name,
"args": this._ingList.map(ing => ing.conf)
};
}
/**
* Adds a new Ingredient to this Operation.
*
* @param {Ingredient} ingredient
*/
addIngredient(ingredient) {
this._ingList.push(ingredient);
}
/**
* Set the Ingredient values for this Operation.
*
* @param {Object[]} ingValues
*/
set ingValues(ingValues) {
ingValues.forEach((val, i) => {
this._ingList[i].value = val;
});
}
/**
* Get the Ingredient values for this Operation.
*
* @returns {Object[]}
*/
get ingValues() {
return this._ingList.map(ing => ing.value);
}
/**
* Set whether this Operation has a breakpoint.
*
* @param {boolean} value
*/
set breakpoint(value) {
this._breakpoint = !!value;
}
/**
* Returns true if this Operation has a breakpoint set.
*
* @returns {boolean}
*/
get breakpoint() {
return this._breakpoint;
}
/**
* Set whether this Operation is disabled.
*
* @param {boolean} value
*/
set disabled(value) {
this._disabled = !!value;
}
/**
* Returns true if this Operation is disabled.
*
* @returns {boolean}
*/
get disabled() {
return this._disabled;
}
/**
* Returns true if this Operation is a flow control.
*
* @returns {boolean}
*/
get flowControl() {
return this._flowControl;
}
}
export default Operation;

View file

@ -1,264 +0,0 @@
import Operation from "./Operation.js";
/**
* The Recipe controls a list of Operations and the Dish they operate on.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @class
* @param {Object} recipeConfig
*/
const Recipe = function(recipeConfig) {
this.opList = [];
if (recipeConfig) {
this._parseConfig(recipeConfig);
}
};
/**
* Reads and parses the given config.
*
* @private
* @param {Object} recipeConfig
*/
Recipe.prototype._parseConfig = function(recipeConfig) {
for (let c = 0; c < recipeConfig.length; c++) {
const operationName = recipeConfig[c].op;
const operation = new Operation(operationName);
operation.setIngValues(recipeConfig[c].args);
operation.setBreakpoint(recipeConfig[c].breakpoint);
operation.setDisabled(recipeConfig[c].disabled);
this.addOperation(operation);
}
};
/**
* Returns the value of the Recipe as it should be displayed in a recipe config.
*
* @returns {*}
*/
Recipe.prototype.getConfig = function() {
const recipeConfig = [];
for (let o = 0; o < this.opList.length; o++) {
recipeConfig.push(this.opList[o].getConfig());
}
return recipeConfig;
};
/**
* Adds a new Operation to this Recipe.
*
* @param {Operation} operation
*/
Recipe.prototype.addOperation = function(operation) {
this.opList.push(operation);
};
/**
* Adds a list of Operations to this Recipe.
*
* @param {Operation[]} operations
*/
Recipe.prototype.addOperations = function(operations) {
this.opList = this.opList.concat(operations);
};
/**
* Set a breakpoint on a specified Operation.
*
* @param {number} position - The index of the Operation
* @param {boolean} value
*/
Recipe.prototype.setBreakpoint = function(position, value) {
try {
this.opList[position].setBreakpoint(value);
} catch (err) {
// Ignore index error
}
};
/**
* Remove breakpoints on all Operations in the Recipe up to the specified position. Used by Flow
* Control Fork operation.
*
* @param {number} pos
*/
Recipe.prototype.removeBreaksUpTo = function(pos) {
for (let i = 0; i < pos; i++) {
this.opList[i].setBreakpoint(false);
}
};
/**
* Returns true if there is an Flow Control Operation in this Recipe.
*
* @returns {boolean}
*/
Recipe.prototype.containsFlowControl = function() {
for (let i = 0; i < this.opList.length; i++) {
if (this.opList[i].isFlowControl()) return true;
}
return false;
};
/**
* Returns the index of the last Operation index that will be executed, taking into account disabled
* Operations and breakpoints.
*
* @param {number} [startIndex=0] - The index to start searching from
* @returns (number}
*/
Recipe.prototype.lastOpIndex = function(startIndex) {
let i = startIndex + 1 || 0,
op;
for (; i < this.opList.length; i++) {
op = this.opList[i];
if (op.isDisabled()) return i-1;
if (op.isBreakpoint()) return i-1;
}
return i-1;
};
/**
* Executes each operation in the recipe over the given Dish.
*
* @param {Dish} dish
* @param {number} [startFrom=0] - The index of the Operation to start executing from
* @param {number} [forkState={}] - If this is a forked recipe, the state of the recipe up to this point
* @returns {number} - The final progress through the recipe
*/
Recipe.prototype.execute = async function(dish, startFrom = 0, forkState = {}) {
let op, input, output,
numJumps = 0,
numRegisters = forkState.numRegisters || 0;
log.debug(`[*] Executing recipe of ${this.opList.length} operations, starting at ${startFrom}`);
for (let i = startFrom; i < this.opList.length; i++) {
op = this.opList[i];
log.debug(`[${i}] ${op.name} ${JSON.stringify(op.getIngValues())}`);
if (op.isDisabled()) {
log.debug("Operation is disabled, skipping");
continue;
}
if (op.isBreakpoint()) {
log.debug("Pausing at breakpoint");
return i;
}
try {
input = dish.get(op.inputType);
log.debug("Executing operation");
if (op.isFlowControl()) {
// Package up the current state
let state = {
"progress": i,
"dish": dish,
"opList": this.opList,
"numJumps": numJumps,
"numRegisters": numRegisters,
"forkOffset": forkState.forkOffset || 0
};
state = await op.run(state);
i = state.progress;
numJumps = state.numJumps;
numRegisters = state.numRegisters;
} else {
output = await op.run(input, op.getIngValues());
dish.set(output, op.outputType);
}
} catch (err) {
const e = typeof err == "string" ? { message: err } : err;
e.progress = i;
if (e.fileName) {
e.displayStr = op.name + " - " + e.name + " in " +
e.fileName + " on line " + e.lineNumber +
".<br><br>Message: " + (e.displayStr || e.message);
} else {
e.displayStr = op.name + " - " + (e.displayStr || e.message);
}
throw e;
}
}
log.debug("Recipe complete");
return this.opList.length;
};
/**
* Returns the recipe configuration in string format.
*
* @returns {string}
*/
Recipe.prototype.toString = function() {
return JSON.stringify(this.getConfig());
};
/**
* Creates a Recipe from a given configuration string.
*
* @param {string} recipeStr
*/
Recipe.prototype.fromString = function(recipeStr) {
const recipeConfig = JSON.parse(recipeStr);
this._parseConfig(recipeConfig);
};
/**
* Generates a list of all the highlight functions assigned to operations in the recipe, if the
* entire recipe supports highlighting.
*
* @returns {Object[]} highlights
* @returns {function} highlights[].f
* @returns {function} highlights[].b
* @returns {Object[]} highlights[].args
*/
Recipe.prototype.generateHighlightList = function() {
const highlights = [];
for (let i = 0; i < this.opList.length; i++) {
let op = this.opList[i];
if (op.isDisabled()) continue;
// If any breakpoints are set, do not attempt to highlight
if (op.isBreakpoint()) return false;
// If any of the operations do not support highlighting, fail immediately.
if (op.highlight === false || op.highlight === undefined) return false;
highlights.push({
f: op.highlight,
b: op.highlightReverse,
args: op.getIngValues()
});
}
return highlights;
};
export default Recipe;

250
src/core/Recipe.mjs Executable file
View file

@ -0,0 +1,250 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
// import Operation from "./Operation.js";
import OpModules from "./config/modules/OpModules";
import OperationConfig from "./config/OperationConfig.json";
import log from "loglevel";
/**
* The Recipe controls a list of Operations and the Dish they operate on.
*/
class Recipe {
/**
* Recipe constructor
*
* @param {Object} recipeConfig
*/
constructor(recipeConfig) {
this.opList = [];
if (recipeConfig) {
this._parseConfig(recipeConfig);
}
}
/**
* Reads and parses the given config.
*
* @private
* @param {Object} recipeConfig
*/
_parseConfig(recipeConfig) {
for (let c = 0; c < recipeConfig.length; c++) {
const operationName = recipeConfig[c].op;
const opConf = OperationConfig[operationName];
const opObj = OpModules[opConf.module][operationName];
const operation = new opObj();
operation.ingValues = recipeConfig[c].args;
operation.breakpoint = recipeConfig[c].breakpoint;
operation.disabled = recipeConfig[c].disabled;
this.addOperation(operation);
}
}
/**
* Returns the value of the Recipe as it should be displayed in a recipe config.
*
* @returns {Object[]}
*/
get config() {
return this.opList.map(op => op.config);
}
/**
* Adds a new Operation to this Recipe.
*
* @param {Operation} operation
*/
addOperation(operation) {
this.opList.push(operation);
}
/**
* Adds a list of Operations to this Recipe.
*
* @param {Operation[]} operations
*/
addOperations(operations) {
this.opList = this.opList.concat(operations);
}
/**
* Set a breakpoint on a specified Operation.
*
* @param {number} position - The index of the Operation
* @param {boolean} value
*/
setBreakpoint(position, value) {
try {
this.opList[position].breakpoint = value;
} catch (err) {
// Ignore index error
}
}
/**
* Remove breakpoints on all Operations in the Recipe up to the specified position. Used by Flow
* Control Fork operation.
*
* @param {number} pos
*/
removeBreaksUpTo(pos) {
for (let i = 0; i < pos; i++) {
this.opList[i].breakpoint = false;
}
}
/**
* Returns true if there is an Flow Control Operation in this Recipe.
*
* @returns {boolean}
*/
containsFlowControl() {
return this.opList.reduce((acc, curr) => {
return acc || curr.flowControl;
}, false);
}
/**
* Executes each operation in the recipe over the given Dish.
*
* @param {Dish} dish
* @param {number} [startFrom=0]
* - The index of the Operation to start executing from
* @param {number} [forkState={}]
* - If this is a forked recipe, the state of the recipe up to this point
* @returns {number}
* - The final progress through the recipe
*/
async execute(dish, startFrom=0, forkState={}) {
let op, input, output,
numJumps = 0,
numRegisters = forkState.numRegisters || 0;
log.debug(`[*] Executing recipe of ${this.opList.length} operations, starting at ${startFrom}`);
for (let i = startFrom; i < this.opList.length; i++) {
op = this.opList[i];
log.debug(`[${i}] ${op.name} ${JSON.stringify(op.ingValues)}`);
if (op.disabled) {
log.debug("Operation is disabled, skipping");
continue;
}
if (op.breakpoint) {
log.debug("Pausing at breakpoint");
return i;
}
try {
input = dish.get(op.inputType);
log.debug("Executing operation");
if (op.flowControl) {
// Package up the current state
let state = {
"progress": i,
"dish": dish,
"opList": this.opList,
"numJumps": numJumps,
"numRegisters": numRegisters,
"forkOffset": forkState.forkOffset || 0
};
state = await op.run(state);
i = state.progress;
numJumps = state.numJumps;
numRegisters = state.numRegisters;
} else {
output = await op.run(input, op.ingValues);
dish.set(output, op.outputType);
}
} catch (err) {
const e = typeof err == "string" ? { message: err } : err;
e.progress = i;
if (e.fileName) {
e.displayStr = op.name + " - " + e.name + " in " +
e.fileName + " on line " + e.lineNumber +
".<br><br>Message: " + (e.displayStr || e.message);
} else {
e.displayStr = op.name + " - " + (e.displayStr || e.message);
}
throw e;
}
}
log.debug("Recipe complete");
return this.opList.length;
}
/**
* Returns the recipe configuration in string format.
*
* @returns {string}
*/
toString() {
return JSON.stringify(this.config);
}
/**
* Creates a Recipe from a given configuration string.
*
* @param {string} recipeStr
*/
fromString(recipeStr) {
const recipeConfig = JSON.parse(recipeStr);
this._parseConfig(recipeConfig);
}
/**
* Generates a list of all the highlight functions assigned to operations in the recipe, if the
* entire recipe supports highlighting.
*
* @returns {Object[]} highlights
* @returns {function} highlights[].f
* @returns {function} highlights[].b
* @returns {Object[]} highlights[].args
*/
generateHighlightList() {
const highlights = [];
for (let i = 0; i < this.opList.length; i++) {
let op = this.opList[i];
if (op.disabled) continue;
// If any breakpoints are set, do not attempt to highlight
if (op.breakpoint) return false;
// If any of the operations do not support highlighting, fail immediately.
if (op.highlight === false || op.highlight === undefined) return false;
highlights.push({
f: op.highlight,
b: op.highlightReverse,
args: op.ingValues
});
}
return highlights;
}
}
export default Recipe;

View file

@ -1,17 +1,17 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import utf8 from "utf8";
import moment from "moment-timezone";
/**
* Utility functions for use in operations, the core framework and the stage.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Utils = {
class Utils {
/**
* Translates an ordinal into a character.
@ -23,7 +23,7 @@ const Utils = {
* // returns 'a'
* Utils.chr(97);
*/
chr: function(o) {
static chr(o) {
// Detect astral symbols
// Thanks to @mathiasbynens for this solution
// https://mathiasbynens.be/notes/javascript-unicode
@ -35,7 +35,7 @@ const Utils = {
}
return String.fromCharCode(o);
},
}
/**
@ -48,7 +48,7 @@ const Utils = {
* // returns 97
* Utils.ord('a');
*/
ord: function(c) {
static ord(c) {
// Detect astral symbols
// Thanks to @mathiasbynens for this solution
// https://mathiasbynens.be/notes/javascript-unicode
@ -62,7 +62,7 @@ const Utils = {
}
return c.charCodeAt(0);
},
}
/**
@ -88,8 +88,7 @@ const Utils = {
* // returns ["t", "e", "s", "t", 1, 1, 1, 1]
* Utils.padBytesRight("test", 8, 1);
*/
padBytesRight: function(arr, numBytes, padByte) {
padByte = padByte || 0;
static padBytesRight(arr, numBytes, padByte=0) {
const paddedBytes = new Array(numBytes);
paddedBytes.fill(padByte);
@ -98,7 +97,7 @@ const Utils = {
});
return paddedBytes;
},
}
/**
@ -116,13 +115,12 @@ const Utils = {
* // returns "A long s-"
* Utils.truncate("A long string", 9, "-");
*/
truncate: function(str, max, suffix) {
suffix = suffix || "...";
static truncate(str, max, suffix="...") {
if (str.length > max) {
str = str.slice(0, max - suffix.length) + suffix;
}
return str;
},
}
/**
@ -139,11 +137,10 @@ const Utils = {
* // returns "6e"
* Utils.hex(110);
*/
hex: function(c, length) {
static hex(c, length=2) {
c = typeof c == "string" ? Utils.ord(c) : c;
length = length || 2;
return c.toString(16).padStart(length, "0");
},
}
/**
@ -160,11 +157,10 @@ const Utils = {
* // returns "01101110"
* Utils.bin(110);
*/
bin: function(c, length) {
static bin(c, length=8) {
c = typeof c == "string" ? Utils.ord(c) : c;
length = length || 8;
return c.toString(2).padStart(length, "0");
},
}
/**
@ -174,7 +170,7 @@ const Utils = {
* @param {boolean} [preserveWs=false] - Whether or not to print whitespace.
* @returns {string}
*/
printable: function(str, preserveWs) {
static printable(str, preserveWs=false) {
if (ENVIRONMENT_IS_WEB() && window.app && !window.app.options.treatAsUtf8) {
str = Utils.byteArrayToChars(Utils.strToByteArray(str));
}
@ -185,7 +181,7 @@ const Utils = {
str = str.replace(re, ".");
if (!preserveWs) str = str.replace(wsRe, ".");
return str;
},
}
/**
@ -201,7 +197,7 @@ const Utils = {
* // returns "\n"
* Utils.parseEscapedChars("\\n");
*/
parseEscapedChars: function(str) {
static parseEscapedChars(str) {
return str.replace(/(\\)?\\([bfnrtv0'"]|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\})/g, function(m, a, b) {
if (a === "\\") return "\\"+b;
switch (b[0]) {
@ -232,7 +228,7 @@ const Utils = {
return String.fromCharCode(parseInt(b.substr(1), 16));
}
});
},
}
/**
@ -246,9 +242,9 @@ const Utils = {
* // returns "\[example\]"
* Utils.escapeRegex("[example]");
*/
escapeRegex: function(str) {
static escapeRegex(str) {
return str.replace(/([.*+?^=!:${}()|[\]/\\])/g, "\\$1");
},
}
/**
@ -267,7 +263,7 @@ const Utils = {
* // returns ["a", "b", "c", "d", "0", "-", "3"]
* Utils.expandAlphRange("a-d0\\-3")
*/
expandAlphRange: function(alphStr) {
static expandAlphRange(alphStr) {
const alphArr = [];
for (let i = 0; i < alphStr.length; i++) {
@ -291,7 +287,7 @@ const Utils = {
}
}
return alphArr;
},
}
/**
@ -312,7 +308,7 @@ const Utils = {
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
* Utils.convertToByteArray("0JfQtNGA0LDQstGB0YLQstGD0LnRgtC1", "base64");
*/
convertToByteArray: function(str, type) {
static convertToByteArray(str, type) {
switch (type.toLowerCase()) {
case "hex":
return Utils.fromHex(str);
@ -324,7 +320,7 @@ const Utils = {
default:
return Utils.strToByteArray(str);
}
},
}
/**
@ -345,7 +341,7 @@ const Utils = {
* // returns "Здравствуйте"
* Utils.convertToByteString("0JfQtNGA0LDQstGB0YLQstGD0LnRgtC1", "base64");
*/
convertToByteString: function(str, type) {
static convertToByteString(str, type) {
switch (type.toLowerCase()) {
case "hex":
return Utils.byteArrayToChars(Utils.fromHex(str));
@ -357,7 +353,7 @@ const Utils = {
default:
return str;
}
},
}
/**
@ -374,7 +370,7 @@ const Utils = {
* // returns [228,189,160,229,165,189]
* Utils.strToByteArray("你好");
*/
strToByteArray: function(str) {
static strToByteArray(str) {
const byteArray = new Array(str.length);
let i = str.length, b;
while (i--) {
@ -384,7 +380,7 @@ const Utils = {
if (b > 255) return Utils.strToUtf8ByteArray(str);
}
return byteArray;
},
}
/**
@ -400,7 +396,7 @@ const Utils = {
* // returns [228,189,160,229,165,189]
* Utils.strToUtf8ByteArray("你好");
*/
strToUtf8ByteArray: function(str) {
static strToUtf8ByteArray(str) {
const utf8Str = utf8.encode(str);
if (str.length !== utf8Str.length) {
@ -412,7 +408,7 @@ const Utils = {
}
return Utils.strToByteArray(utf8Str);
},
}
/**
@ -428,7 +424,7 @@ const Utils = {
* // returns [20320,22909]
* Utils.strToCharcode("你好");
*/
strToCharcode: function(str) {
static strToCharcode(str) {
const charcode = [];
for (let i = 0; i < str.length; i++) {
@ -446,7 +442,7 @@ const Utils = {
}
return charcode;
},
}
/**
@ -462,7 +458,7 @@ const Utils = {
* // returns "你好"
* Utils.byteArrayToUtf8([228,189,160,229,165,189]);
*/
byteArrayToUtf8: function(byteArray) {
static byteArrayToUtf8(byteArray) {
const str = Utils.byteArrayToChars(byteArray);
try {
const utf8Str = utf8.decode(str);
@ -479,7 +475,7 @@ const Utils = {
// If it fails, treat it as ANSI
return str;
}
},
}
/**
@ -495,14 +491,14 @@ const Utils = {
* // returns "你好"
* Utils.byteArrayToChars([20320,22909]);
*/
byteArrayToChars: function(byteArray) {
static byteArrayToChars(byteArray) {
if (!byteArray) return "";
let str = "";
for (let i = 0; i < byteArray.length;) {
str += String.fromCharCode(byteArray[i++]);
}
return str;
},
}
/**
@ -516,17 +512,17 @@ const Utils = {
* // returns "hello"
* Utils.arrayBufferToStr(Uint8Array.from([104,101,108,108,111]).buffer);
*/
arrayBufferToStr: function(arrayBuffer, utf8=true) {
static arrayBufferToStr(arrayBuffer, utf8=true) {
const byteArray = Array.prototype.slice.call(new Uint8Array(arrayBuffer));
return utf8 ? Utils.byteArrayToUtf8(byteArray) : Utils.byteArrayToChars(byteArray);
},
}
/**
* Base64's the input byte array using the given alphabet, returning a string.
*
* @param {byteArray|Uint8Array|string} data
* @param {string} [alphabet]
* @param {string} [alphabet="A-Za-z0-9+/="]
* @returns {string}
*
* @example
@ -536,15 +532,14 @@ const Utils = {
* // returns "SGVsbG8="
* Utils.toBase64("Hello");
*/
toBase64: function(data, alphabet) {
static toBase64(data, alphabet="A-Za-z0-9+/=") {
if (!data) return "";
if (typeof data == "string") {
data = Utils.strToByteArray(data);
}
alphabet = alphabet ?
Utils.expandAlphRange(alphabet).join("") :
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
alphabet = Utils.expandAlphRange(alphabet).join("");
let output = "",
chr1, chr2, chr3,
enc1, enc2, enc3, enc4,
@ -571,14 +566,14 @@ const Utils = {
}
return output;
},
}
/**
* UnBase64's the input string using the given alphabet, returning a byte array.
*
* @param {byteArray} data
* @param {string} [alphabet]
* @param {string} [alphabet="A-Za-z0-9+/="]
* @param {string} [returnType="string"] - Either "string" or "byteArray"
* @param {boolean} [removeNonAlphChars=true]
* @returns {byteArray}
@ -590,18 +585,12 @@ const Utils = {
* // returns [72, 101, 108, 108, 111]
* Utils.fromBase64("SGVsbG8=", null, "byteArray");
*/
fromBase64: function(data, alphabet, returnType, removeNonAlphChars) {
returnType = returnType || "string";
static fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", removeNonAlphChars=true) {
if (!data) {
return returnType === "string" ? "" : [];
}
alphabet = alphabet ?
Utils.expandAlphRange(alphabet).join("") :
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
if (removeNonAlphChars === undefined)
removeNonAlphChars = true;
alphabet = Utils.expandAlphRange(alphabet).join("");
let output = [],
chr1, chr2, chr3,
@ -638,7 +627,7 @@ const Utils = {
}
return returnType === "string" ? Utils.byteArrayToUtf8(output) : output;
},
}
/**
@ -656,11 +645,9 @@ const Utils = {
* // returns "0a:14:1e"
* Utils.toHex([10,20,30], ":");
*/
toHex: function(data, delim, padding) {
static toHex(data, delim=" ", padding=2) {
if (!data) return "";
delim = typeof delim == "string" ? delim : " ";
padding = padding || 2;
let output = "";
for (let i = 0; i < data.length; i++) {
@ -675,7 +662,7 @@ const Utils = {
return output.slice(0, -delim.length);
else
return output;
},
}
/**
@ -688,7 +675,7 @@ const Utils = {
* // returns "0a141e"
* Utils.toHex([10,20,30]);
*/
toHexFast: function(data) {
static toHexFast(data) {
if (!data) return "";
const output = [];
@ -699,7 +686,7 @@ const Utils = {
}
return output.join("");
},
}
/**
@ -717,11 +704,10 @@ const Utils = {
* // returns [10,20,30]
* Utils.fromHex("0a:14:1e", "Colon");
*/
fromHex: function(data, delim, byteLen) {
static fromHex(data, delim, byteLen=2) {
delim = delim || (data.indexOf(" ") >= 0 ? "Space" : "None");
byteLen = byteLen || 2;
if (delim !== "None") {
const delimRegex = Utils.regexRep[delim];
const delimRegex = Utils.regexRep(delim);
data = data.replace(delimRegex, "");
}
@ -730,7 +716,7 @@ const Utils = {
output.push(parseInt(data.substr(i, byteLen), 16));
}
return output;
},
}
/**
@ -743,8 +729,7 @@ const Utils = {
* // returns [["head1", "head2"], ["data1", "data2"]]
* Utils.parseCSV("head1,head2\ndata1,data2");
*/
parseCSV: function(data) {
static parseCSV(data) {
let b,
ignoreNext = false,
inString = false,
@ -783,26 +768,27 @@ const Utils = {
}
return lines;
},
}
/**
* Removes all HTML (or XML) tags from the input string.
*
* @param {string} htmlStr
* @param {boolean} removeScriptAndStyle - Flag to specify whether to remove entire script or style blocks
* @param {boolean} [removeScriptAndStyle=false]
* - Flag to specify whether to remove entire script or style blocks
* @returns {string}
*
* @example
* // returns "Test"
* Utils.stripHtmlTags("<div>Test</div>");
*/
stripHtmlTags: function(htmlStr, removeScriptAndStyle) {
static stripHtmlTags(htmlStr, removeScriptAndStyle=false) {
if (removeScriptAndStyle) {
htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, "");
}
return htmlStr.replace(/<[^>]+>/g, "");
},
}
/**
@ -816,7 +802,7 @@ const Utils = {
* // return "A &lt;script&gt; tag"
* Utils.escapeHtml("A <script> tag");
*/
escapeHtml: function(str) {
static escapeHtml(str) {
const HTML_CHARS = {
"&": "&amp;",
"<": "&lt;",
@ -830,7 +816,7 @@ const Utils = {
return str.replace(/[&<>"'/`]/g, function (match) {
return HTML_CHARS[match];
});
},
}
/**
@ -843,7 +829,7 @@ const Utils = {
* // return "A <script> tag"
* Utils.unescapeHtml("A &lt;script&gt; tag");
*/
unescapeHtml: function(str) {
static unescapeHtml(str) {
const HTML_CHARS = {
"&amp;": "&",
"&lt;": "<",
@ -857,7 +843,7 @@ const Utils = {
return str.replace(/&#?x?[a-z0-9]{2,4};/ig, function (match) {
return HTML_CHARS[match] || match;
});
},
}
/**
@ -882,7 +868,7 @@ const Utils = {
* @param {string} str
* @returns {string}
*/
encodeURIFragment: function(str) {
static encodeURIFragment(str) {
const LEGAL_CHARS = {
"%2D": "-",
"%2E": ".",
@ -909,7 +895,7 @@ const Utils = {
return str.replace(/%[0-9A-F]{2}/g, function (match) {
return LEGAL_CHARS[match] || match;
});
},
}
/**
@ -923,10 +909,10 @@ const Utils = {
* for users.
*
* @param {Object[]} recipeConfig
* @param {boolean} newline - whether to add a newline after each operation
* @param {boolean} [newline=false] - whether to add a newline after each operation
* @returns {string}
*/
generatePrettyRecipe: function(recipeConfig, newline) {
static generatePrettyRecipe(recipeConfig, newline=false) {
let prettyConfig = "",
name = "",
args = "",
@ -949,17 +935,17 @@ const Utils = {
if (newline) prettyConfig += "\n";
});
return prettyConfig;
},
}
/**
* Converts a recipe string to the JSON representation of the recipe.
* Accepts either stringified JSON or bespoke "pretty" recipe format.
* Accepts either stringified JSON or the bespoke "pretty" recipe format.
*
* @param {string} recipe
* @returns {Object[]}
*/
parseRecipeConfig: function(recipe) {
static parseRecipeConfig(recipe) {
recipe = recipe.trim();
if (recipe.length === 0) return [];
if (recipe[0] === "[") return JSON.parse(recipe);
@ -989,7 +975,7 @@ const Utils = {
recipeConfig.push(op);
}
return recipeConfig;
},
}
/**
@ -1019,24 +1005,9 @@ const Utils = {
* // returns "5 days"
* Utils.fuzzyTime(456851321);
*/
fuzzyTime: function(ms) {
static fuzzyTime(ms) {
return moment.duration(ms, "milliseconds").humanize();
},
/**
* Adds the properties of one Object to another.
*
* @param {Object} a
* @param {Object} b
* @returns {Object}
*/
extend: function(a, b){
for (const key in b)
if (b.hasOwnProperty(key))
a[key] = b[key];
return a;
},
}
/**
@ -1050,7 +1021,7 @@ const Utils = {
* @param {Object[]} files
* @returns {html}
*/
displayFilesAsHTML: function(files) {
static displayFilesAsHTML(files) {
/* <NL> and <SP> used to denote newlines and spaces in HTML markup.
* If a non-html operation is used, all markup will be removed but these
* whitespace chars will remain for formatting purposes.
@ -1131,7 +1102,7 @@ const Utils = {
return html.replace(/(?:(<pre>(?:\n|.)*<\/pre>)|\s{2,})/g, "$1") // Remove whitespace from markup
.replace(/<NL>/g, "\n") // Replace <NP> with newlines
.replace(/<SP>/g, " "); // Replace <SP> with spaces
},
}
/**
@ -1145,7 +1116,7 @@ const Utils = {
* Utils.parseURIParams("?a=abc&b=123")
* Utils.parseURIParams("#a=abc&b=123")
*/
parseURIParams: function(paramStr) {
static parseURIParams(paramStr) {
if (paramStr === "") return {};
// Cut off ? or # and split on &
@ -1167,7 +1138,7 @@ const Utils = {
}
return result;
},
}
/**
@ -1178,9 +1149,9 @@ const Utils = {
* @param {number} y
* @returns {number}
*/
mod: function (x, y) {
static mod(x, y) {
return ((x % y) + y) % y;
},
}
/**
@ -1191,12 +1162,12 @@ const Utils = {
* @param {number} y
* @returns {number}
*/
gcd: function(x, y) {
static gcd(x, y) {
if (!y) {
return x;
}
return Utils.gcd(y, x % y);
},
}
/**
@ -1207,55 +1178,63 @@ const Utils = {
* @param {number} y
* @returns {number}
*/
modInv: function(x, y) {
static modInv(x, y) {
x %= y;
for (let i = 1; i < y; i++) {
if ((x * i) % 26 === 1) {
return i;
}
}
},
}
/**
* A mapping of names of delimiter characters to their symbols.
* @constant
*
* @param {string} token
* @returns {string}
*/
charRep: {
"Space": " ",
"Comma": ",",
"Semi-colon": ";",
"Colon": ":",
"Line feed": "\n",
"CRLF": "\r\n",
"Forward slash": "/",
"Backslash": "\\",
"0x": "0x",
"\\x": "\\x",
"Nothing (separate chars)": "",
"None": "",
},
static charRep(token) {
return {
"Space": " ",
"Comma": ",",
"Semi-colon": ";",
"Colon": ":",
"Line feed": "\n",
"CRLF": "\r\n",
"Forward slash": "/",
"Backslash": "\\",
"0x": "0x",
"\\x": "\\x",
"Nothing (separate chars)": "",
"None": "",
}[token];
}
/**
* A mapping of names of delimiter characters to regular expressions which can select them.
* @constant
*
* @param {string} token
* @returns {RegExp}
*/
regexRep: {
"Space": /\s+/g,
"Comma": /,/g,
"Semi-colon": /;/g,
"Colon": /:/g,
"Line feed": /\n/g,
"CRLF": /\r\n/g,
"Forward slash": /\//g,
"Backslash": /\\/g,
"0x": /0x/g,
"\\x": /\\x/g,
"None": /\s+/g // Included here to remove whitespace when there shouldn't be any
},
static regexRep(token) {
return {
"Space": /\s+/g,
"Comma": /,/g,
"Semi-colon": /;/g,
"Colon": /:/g,
"Line feed": /\n/g,
"CRLF": /\r\n/g,
"Forward slash": /\//g,
"Backslash": /\\/g,
"0x": /0x/g,
"\\x": /\\x/g,
"None": /\s+/g // Included here to remove whitespace when there shouldn't be any
}[token];
}
};
}
export default Utils;

View file

@ -21,7 +21,7 @@ const Categories = [
{
name: "Favourites",
ops: []
},
}/*,
{
name: "Data format",
ops: [
@ -354,7 +354,7 @@ const Categories = [
"Return",
"Comment"
]
},
},*/
];
export default Categories;

View file

@ -0,0 +1,141 @@
{
"To Base64": {
"module": "Default",
"description": "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.<br><br>This operation decodes data from an ASCII Base64 string back into its raw format.<br><br>e.g. <code>aGVsbG8=</code> becomes <code>hello</code>",
"inputType": "ArrayBuffer",
"outputType": "string",
"flowControl": false,
"args": [
{
"name": "Alphabet",
"type": "editableOption",
"value": [
{
"name": "Standard: A-Za-z0-9+/=",
"value": "A-Za-z0-9+/="
},
{
"name": "URL safe: A-Za-z0-9-_",
"value": "A-Za-z0-9-_"
},
{
"name": "Filename safe: A-Za-z0-9+-=",
"value": "A-Za-z0-9+\\-="
},
{
"name": "itoa64: ./0-9A-Za-z=",
"value": "./0-9A-Za-z="
},
{
"name": "XML: A-Za-z0-9_.",
"value": "A-Za-z0-9_."
},
{
"name": "y64: A-Za-z0-9._-",
"value": "A-Za-z0-9._-"
},
{
"name": "z64: 0-9a-zA-Z+/=",
"value": "0-9a-zA-Z+/="
},
{
"name": "Radix-64: 0-9A-Za-z+/=",
"value": "0-9A-Za-z+/="
},
{
"name": "Uuencoding: [space]-_",
"value": " -_"
},
{
"name": "Xxencoding: +-0-9A-Za-z",
"value": "+\\-0-9A-Za-z"
},
{
"name": "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r",
"value": "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"
},
{
"name": "ROT13: N-ZA-Mn-za-m0-9+/=",
"value": "N-ZA-Mn-za-m0-9+/="
},
{
"name": "UNIX crypt: ./0-9A-Za-z",
"value": "./0-9A-Za-z"
}
]
}
]
},
"From Base64": {
"module": "Default",
"description": "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.<br><br>This operation decodes data from an ASCII Base64 string back into its raw format.<br><br>e.g. <code>aGVsbG8=</code> becomes <code>hello</code>",
"inputType": "string",
"outputType": "byteArray",
"flowControl": false,
"args": [
{
"name": "Alphabet",
"type": "editableOption",
"value": [
{
"name": "Standard: A-Za-z0-9+/=",
"value": "A-Za-z0-9+/="
},
{
"name": "URL safe: A-Za-z0-9-_",
"value": "A-Za-z0-9-_"
},
{
"name": "Filename safe: A-Za-z0-9+-=",
"value": "A-Za-z0-9+\\-="
},
{
"name": "itoa64: ./0-9A-Za-z=",
"value": "./0-9A-Za-z="
},
{
"name": "XML: A-Za-z0-9_.",
"value": "A-Za-z0-9_."
},
{
"name": "y64: A-Za-z0-9._-",
"value": "A-Za-z0-9._-"
},
{
"name": "z64: 0-9a-zA-Z+/=",
"value": "0-9a-zA-Z+/="
},
{
"name": "Radix-64: 0-9A-Za-z+/=",
"value": "0-9A-Za-z+/="
},
{
"name": "Uuencoding: [space]-_",
"value": " -_"
},
{
"name": "Xxencoding: +-0-9A-Za-z",
"value": "+\\-0-9A-Za-z"
},
{
"name": "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r",
"value": "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"
},
{
"name": "ROT13: N-ZA-Mn-za-m0-9+/=",
"value": "N-ZA-Mn-za-m0-9+/="
},
{
"name": "UNIX crypt: ./0-9A-Za-z",
"value": "./0-9A-Za-z"
}
]
},
{
"name": "Remove non-alphabet chars",
"type": "boolean",
"value": true
}
]
}
}

View file

@ -1,87 +1,161 @@
import Arithmetic from "../operations/Arithmetic.js";
import Base from "../operations/Base.js";
import Base58 from "../operations/Base58.js";
import Base64 from "../operations/Base64.js";
import BCD from "../operations/BCD.js";
import BitwiseOp from "../operations/BitwiseOp.js";
import ByteRepr from "../operations/ByteRepr.js";
import CharEnc from "../operations/CharEnc.js";
import Cipher from "../operations/Cipher.js";
import Code from "../operations/Code.js";
import Compress from "../operations/Compress.js";
import Convert from "../operations/Convert.js";
import DateTime from "../operations/DateTime.js";
import Diff from "../operations/Diff.js";
import Endian from "../operations/Endian.js";
import Entropy from "../operations/Entropy.js";
import Extract from "../operations/Extract.js";
import Filetime from "../operations/Filetime.js";
import FileType from "../operations/FileType.js";
import Image from "../operations/Image.js";
import Hash from "../operations/Hash.js";
import Hexdump from "../operations/Hexdump.js";
import HTML from "../operations/HTML.js";
import HTTP from "../operations/HTTP.js";
import IP from "../operations/IP.js";
import JS from "../operations/JS.js";
import MAC from "../operations/MAC.js";
import MorseCode from "../operations/MorseCode.js";
import NetBIOS from "../operations/NetBIOS.js";
import PHP from "../operations/PHP.js";
import PublicKey from "../operations/PublicKey.js";
import Punycode from "../operations/Punycode.js";
import Regex from "../operations/Regex.js";
import Rotate from "../operations/Rotate.js";
import SeqUtils from "../operations/SeqUtils.js";
import Shellcode from "../operations/Shellcode.js";
import StrUtils from "../operations/StrUtils.js";
import Tidy from "../operations/Tidy.js";
import Unicode from "../operations/Unicode.js";
import URL_ from "../operations/URL.js";
/**
* Type definition for an OpConf.
*
* @typedef {Object} OpConf
* @property {string} module - The module to which the operation belongs
* @property {html} description - A description of the operation with optional HTML tags
* @property {string} inputType
* @property {string} outputType
* @property {Function|boolean} [highlight] - A function to calculate the highlight offset, or true
* if the offset does not change
* @property {Function|boolean} [highlightReverse] - A function to calculate the highlight offset
* in reverse, or true if the offset does not change
* @property {boolean} [flowControl] - True if the operation is for Flow Control
* @property {ArgConf[]} [args] - A list of configuration objects for the arguments
*/
/**
* Type definition for an ArgConf.
*
* @typedef {Object} ArgConf
* @property {string} name - The display name of the argument
* @property {string} type - The data type of the argument
* @property {*} value
* @property {number[]} [disableArgs] - A list of the indices of the operation's arguments which
* should be toggled on or off when this argument is changed
* @property {boolean} [disabled] - Whether or not this argument starts off disabled
*/
/**
* Operation configuration objects.
* This script automatically generates OperationConfig.json, containing metadata
* for each opeartion in the src/core/operations directory.
* It also generates modules in the src/core/config/modules directory to separate
* out operations into logical collections.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @copyright Crown Copyright 2018
* @license Apache-2.0
*
* @constant
* @type {Object.<string, OpConf>}
*/
const OperationConfig = {
"Fork": {
/*eslint no-console: ["off"] */
import path from "path";
import fs from "fs";
import process from "process";
import OpIndex from "../operations/index";
const dir = path.join(process.cwd() + "/src/core/config/");
if (!fs.existsSync(dir)) {
console.log("\nCWD: " + process.cwd());
console.log("Error: generateConfig.mjs should be run from the project root");
console.log("Example> node --experimental-modules src/core/config/generateConfig.mjs");
process.exit(1);
}
let operationConfig = {},
modules = {};
/**
* Generate operation config and module lists.
*/
OpIndex.forEach(opObj => {
const op = new opObj();
operationConfig[op.name] = {
module: op.module,
description: op.description,
inputType: op.inputType,
outputType: op.outputType,
flowControl: op.flowControl,
args: op.args
};
if (!modules.hasOwnProperty(op.module))
modules[op.module] = {};
modules[op.module][op.name] = op.name.replace(/\s/g, "");
});
/**
* Write OperationConfig.
*/
fs.writeFile(
path.join(dir, "OperationConfig.json"),
JSON.stringify(operationConfig, null, 4),
err => {
if (err) {
console.error(err);
process.exit(1);
}
}
);
/**
* Write modules.
*/
for (let module in modules) {
let code = `/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/generateConfig.mjs
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright ${new Date().getUTCFullYear()}
* @license Apache-2.0
*/
`;
for (let opName in modules[module]) {
const objName = modules[module][opName];
code += `import ${objName} from "../../operations/${objName}";\n`;
}
code += `
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.${module} = {
`;
for (let opName in modules[module]) {
const objName = modules[module][opName];
code += ` "${opName}": ${objName},\n`;
}
code += `};
export default OpModules;
`;
fs.writeFile(
path.join(dir, `modules/${module}.mjs`),
code,
err => {
if (err) {
console.error(err);
process.exit(1);
}
}
);
}
/**
* Write OpModules wrapper.
*/
let opModulesCode = `/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/generateConfig.mjs
*
* Imports all modules for builds which do not load modules separately.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright ${new Date().getUTCFullYear()}
* @license Apache-2.0
*/
`;
for (let module in modules) {
opModulesCode += `import ${module}Module from "./${module}";\n`;
}
opModulesCode += `
let OpModules = {};
Object.assign(
OpModules,
`;
for (let module in modules) {
opModulesCode += ` ${module}Module,\n`;
}
opModulesCode += `);
export default OpModules;
`;
fs.writeFile(
path.join(dir, "modules/OpModules.mjs"),
opModulesCode,
err => {
if (err) {
console.error(err);
process.exit(1);
}
}
);
/*"Fork": {
module: "Default",
description: "Split the input data up based on the specified delimiter and run all subsequent operations on each branch separately.<br><br>For example, to decode multiple Base64 strings, enter them all on separate lines then add the 'Fork' and 'From Base64' operations to the recipe. Each string will be decoded separately.",
inputType: "string",
@ -256,6 +330,9 @@ const OperationConfig = {
},
]
},
};*/
/*
"From Base58": {
module: "Default",
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).",
@ -4160,22 +4237,23 @@ const OperationConfig = {
args: []
},
};
*/
/**
* Exports the OperationConfig JSON object in val-loader format so that it can be loaded
* into the app without also importing all the dependencies.
*
* See https://github.com/webpack-contrib/val-loader
*
* @returns {Object}
*/
function valExport() {
return {
code: "module.exports = " + JSON.stringify(OperationConfig) + ";"
};
}
// /**
// * Exports the OperationConfig JSON object in val-loader format so that it can be loaded
// * into the app without also importing all the dependencies.
// *
// * See https://github.com/webpack-contrib/val-loader
// *
// * @returns {Object}
// */
// function valExport() {
// return {
// code: "module.exports = " + JSON.stringify(OperationConfig) + ";"
// };
// }
export default valExport;
// export default valExport;
export { OperationConfig };
// export {operations};

View file

@ -1,22 +0,0 @@
import BSON from "../../operations/BSON.js";
/**
* BSON module.
*
* Libraries:
* - bson
* - buffer
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.BSON = {
"BSON serialise": BSON.runBSONSerialise,
"BSON deserialise": BSON.runBSONDeserialise,
};
export default OpModules;

View file

@ -1,21 +0,0 @@
import CharEnc from "../../operations/CharEnc.js";
/**
* CharEnc module.
*
* Libraries:
* - cptable
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.CharEnc = {
"Encode text": CharEnc.runEncode,
"Decode text": CharEnc.runDecode,
};
export default OpModules;

View file

@ -1,44 +0,0 @@
import Cipher from "../../operations/Cipher.js";
/**
* Ciphers module.
*
* Libraries:
* - CryptoJS
* - Blowfish
* - Forge
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Ciphers = {
"AES Encrypt": Cipher.runAesEnc,
"AES Decrypt": Cipher.runAesDec,
"Blowfish Encrypt": Cipher.runBlowfishEnc,
"Blowfish Decrypt": Cipher.runBlowfishDec,
"DES Encrypt": Cipher.runDesEnc,
"DES Decrypt": Cipher.runDesDec,
"Triple DES Encrypt": Cipher.runTripleDesEnc,
"Triple DES Decrypt": Cipher.runTripleDesDec,
"Derive PBKDF2 key": Cipher.runPbkdf2,
"Derive EVP key": Cipher.runEvpkdf,
"RC4": Cipher.runRc4,
"RC4 Drop": Cipher.runRc4drop,
"RC2 Encrypt": Cipher.runRc2Enc,
"RC2 Decrypt": Cipher.runRc2Dec,
"Vigenère Encode": Cipher.runVigenereEnc,
"Vigenère Decode": Cipher.runVigenereDec,
"Bifid Cipher Encode": Cipher.runBifidEnc,
"Bifid Cipher Decode": Cipher.runBifidDec,
"Affine Cipher Encode": Cipher.runAffineEnc,
"Affine Cipher Decode": Cipher.runAffineDec,
"Atbash Cipher": Cipher.runAtbash,
"Substitute": Cipher.runSubstitute,
"Pseudo-Random Number Generator": Cipher.runPRNG,
};
export default OpModules;

View file

@ -1,44 +0,0 @@
import JS from "../../operations/JS.js";
import Code from "../../operations/Code.js";
/**
* Code module.
*
* Libraries:
* - lodash
* - vkbeautify
* - xmldom
* - xpath
* - jpath
* - highlight.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Code = {
"JavaScript Parser": JS.runParse,
"JavaScript Beautify": JS.runBeautify,
"JavaScript Minify": JS.runMinify,
"Syntax highlighter": Code.runSyntaxHighlight,
"Generic Code Beautify": Code.runGenericBeautify,
"JSON Beautify": Code.runJsonBeautify,
"JSON Minify": Code.runJsonMinify,
"XML Beautify": Code.runXmlBeautify,
"XML Minify": Code.runXmlMinify,
"SQL Beautify": Code.runSqlBeautify,
"SQL Minify": Code.runSqlMinify,
"CSS Beautify": Code.runCssBeautify,
"CSS Minify": Code.runCssMinify,
"XPath expression": Code.runXpath,
"CSS selector": Code.runCSSQuery,
"To Snake case": Code.runToSnakeCase,
"To Camel case": Code.runToCamelCase,
"To Kebab case": Code.runToKebabCase,
"JPath expression": Code.runJpath,
};
export default OpModules;

View file

@ -1,32 +0,0 @@
import Compress from "../../operations/Compress.js";
/**
* Compression module.
*
* Libraries:
* - zlib.js
* - bzip2.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Compression = {
"Raw Deflate": Compress.runRawDeflate,
"Raw Inflate": Compress.runRawInflate,
"Zlib Deflate": Compress.runZlibDeflate,
"Zlib Inflate": Compress.runZlibInflate,
"Gzip": Compress.runGzip,
"Gunzip": Compress.runGunzip,
"Zip": Compress.runPkzip,
"Unzip": Compress.runPkunzip,
"Bzip2 Decompress": Compress.runBzip2Decompress,
"Tar": Compress.runTar,
"Untar": Compress.runUntar,
};
export default OpModules;

View file

@ -1,199 +0,0 @@
import FlowControl from "../../FlowControl.js";
import Arithmetic from "../../operations/Arithmetic.js";
import Base from "../../operations/Base.js";
import Base58 from "../../operations/Base58.js";
import Base64 from "../../operations/Base64.js";
import BCD from "../../operations/BCD.js";
import BitwiseOp from "../../operations/BitwiseOp.js";
import ByteRepr from "../../operations/ByteRepr.js";
import Convert from "../../operations/Convert.js";
import DateTime from "../../operations/DateTime.js";
import Endian from "../../operations/Endian.js";
import Entropy from "../../operations/Entropy.js";
import Filetime from "../../operations/Filetime.js";
import FileType from "../../operations/FileType.js";
import Hexdump from "../../operations/Hexdump.js";
import HTML from "../../operations/HTML.js";
import MAC from "../../operations/MAC.js";
import MorseCode from "../../operations/MorseCode.js";
import MS from "../../operations/MS.js";
import NetBIOS from "../../operations/NetBIOS.js";
import Numberwang from "../../operations/Numberwang.js";
import OS from "../../operations/OS.js";
import OTP from "../../operations/OTP.js";
import PHP from "../../operations/PHP.js";
import QuotedPrintable from "../../operations/QuotedPrintable.js";
import Rotate from "../../operations/Rotate.js";
import SeqUtils from "../../operations/SeqUtils.js";
import StrUtils from "../../operations/StrUtils.js";
import Tidy from "../../operations/Tidy.js";
import Unicode from "../../operations/Unicode.js";
import UUID from "../../operations/UUID.js";
import XKCD from "../../operations/XKCD.js";
/**
* Default module.
*
* The Default module is for operations that are expected to be very commonly used or
* do not require any libraries. This module is loaded into the app at compile time.
*
* Libraries:
* - Utils.js
* - otp
* - crypto
* - bignumber.js
* - jsesc
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Default = {
"To Hexdump": Hexdump.runTo,
"From Hexdump": Hexdump.runFrom,
"To Hex": ByteRepr.runToHex,
"From Hex": ByteRepr.runFromHex,
"To Octal": ByteRepr.runToOct,
"From Octal": ByteRepr.runFromOct,
"To Charcode": ByteRepr.runToCharcode,
"From Charcode": ByteRepr.runFromCharcode,
"To Decimal": ByteRepr.runToDecimal,
"From Decimal": ByteRepr.runFromDecimal,
"To Binary": ByteRepr.runToBinary,
"From Binary": ByteRepr.runFromBinary,
"To Hex Content": ByteRepr.runToHexContent,
"From Hex Content": ByteRepr.runFromHexContent,
"To Base64": Base64.runTo,
"From Base64": Base64.runFrom,
"Show Base64 offsets": Base64.runOffsets,
"To Base32": Base64.runTo32,
"From Base32": Base64.runFrom32,
"To Base58": Base58.runTo,
"From Base58": Base58.runFrom,
"To Base": Base.runTo,
"From Base": Base.runFrom,
"To BCD": BCD.runToBCD,
"From BCD": BCD.runFromBCD,
"To HTML Entity": HTML.runToEntity,
"From HTML Entity": HTML.runFromEntity,
"Strip HTML tags": HTML.runStripTags,
"Parse colour code": HTML.runParseColourCode,
"Unescape Unicode Characters": Unicode.runUnescape,
"Escape Unicode Characters": Unicode.runEscape,
"To Quoted Printable": QuotedPrintable.runTo,
"From Quoted Printable": QuotedPrintable.runFrom,
"Swap endianness": Endian.runSwapEndianness,
"ROT13": Rotate.runRot13,
"ROT47": Rotate.runRot47,
"Rotate left": Rotate.runRotl,
"Rotate right": Rotate.runRotr,
"Bit shift left": BitwiseOp.runBitShiftLeft,
"Bit shift right": BitwiseOp.runBitShiftRight,
"XOR": BitwiseOp.runXor,
"XOR Brute Force": BitwiseOp.runXorBrute,
"OR": BitwiseOp.runOr,
"NOT": BitwiseOp.runNot,
"AND": BitwiseOp.runAnd,
"ADD": BitwiseOp.runAdd,
"SUB": BitwiseOp.runSub,
"To Morse Code": MorseCode.runTo,
"From Morse Code": MorseCode.runFrom,
"Format MAC addresses": MAC.runFormat,
"Encode NetBIOS Name": NetBIOS.runEncodeName,
"Decode NetBIOS Name": NetBIOS.runDecodeName,
"Offset checker": StrUtils.runOffsetChecker,
"To Upper case": StrUtils.runUpper,
"To Lower case": StrUtils.runLower,
"Split": StrUtils.runSplit,
"Filter": StrUtils.runFilter,
"Escape string": StrUtils.runEscape,
"Unescape string": StrUtils.runUnescape,
"Head": StrUtils.runHead,
"Tail": StrUtils.runTail,
"Hamming Distance": StrUtils.runHamming,
"Remove whitespace": Tidy.runRemoveWhitespace,
"Remove null bytes": Tidy.runRemoveNulls,
"Drop bytes": Tidy.runDropBytes,
"Take bytes": Tidy.runTakeBytes,
"Pad lines": Tidy.runPad,
"Reverse": SeqUtils.runReverse,
"Sort": SeqUtils.runSort,
"Unique": SeqUtils.runUnique,
"Count occurrences": SeqUtils.runCount,
"Add line numbers": SeqUtils.runAddLineNumbers,
"Remove line numbers": SeqUtils.runRemoveLineNumbers,
"Expand alphabet range": SeqUtils.runExpandAlphRange,
"Convert distance": Convert.runDistance,
"Convert area": Convert.runArea,
"Convert mass": Convert.runMass,
"Convert speed": Convert.runSpeed,
"Convert data units": Convert.runDataSize,
"Parse UNIX file permissions": OS.runParseUnixPerms,
"Parse DateTime": DateTime.runParse,
"Translate DateTime Format": DateTime.runTranslateFormat,
"From UNIX Timestamp": DateTime.runFromUnixTimestamp,
"To UNIX Timestamp": DateTime.runToUnixTimestamp,
"Sleep": DateTime.runSleep,
"Microsoft Script Decoder": MS.runDecodeScript,
"Entropy": Entropy.runEntropy,
"Frequency distribution": Entropy.runFreqDistrib,
"Chi Square": Entropy.runChiSq,
"Detect File Type": FileType.runDetect,
"Scan for Embedded Files": FileType.runScanForEmbeddedFiles,
"Generate UUID": UUID.runGenerateV4,
"Numberwang": Numberwang.run,
"Generate TOTP": OTP.runTOTP,
"Generate HOTP": OTP.runHOTP,
"Fork": FlowControl.runFork,
"Merge": FlowControl.runMerge,
"Register": FlowControl.runRegister,
"Label": FlowControl.runComment,
"Jump": FlowControl.runJump,
"Conditional Jump": FlowControl.runCondJump,
"Return": FlowControl.runReturn,
"Comment": FlowControl.runComment,
"PHP Deserialize": PHP.runDeserialize,
"Sum": Arithmetic.runSum,
"Subtract": Arithmetic.runSub,
"Multiply": Arithmetic.runMulti,
"Divide": Arithmetic.runDiv,
"Mean": Arithmetic.runMean,
"Median": Arithmetic.runMedian,
"Standard Deviation": Arithmetic.runStdDev,
"Windows Filetime to UNIX Timestamp": Filetime.runFromFiletimeToUnix,
"UNIX Timestamp to Windows Filetime": Filetime.runToFiletimeFromUnix,
"XKCD Random Number": XKCD.runRandomNumber,
/*
Highlighting functions.
This is a temporary solution as highlighting should be entirely
overhauled at some point.
*/
"From Base64-highlight": Base64.highlightFrom,
"From Base64-highlightReverse": Base64.highlightTo,
"To Base64-highlight": Base64.highlightTo,
"To Base64-highlightReverse": Base64.highlightFrom,
"From Hex-highlight": ByteRepr.highlightFrom,
"From Hex-highlightReverse": ByteRepr.highlightTo,
"To Hex-highlight": ByteRepr.highlightTo,
"To Hex-highlightReverse": ByteRepr.highlightFrom,
"From Charcode-highlight": ByteRepr.highlightFrom,
"From Charcode-highlightReverse": ByteRepr.highlightTo,
"To Charcode-highlight": ByteRepr.highlightTo,
"To Charcode-highlightReverse": ByteRepr.highlightFrom,
"From Binary-highlight": ByteRepr.highlightFromBinary,
"From Binary-highlightReverse": ByteRepr.highlightToBinary,
"To Binary-highlight": ByteRepr.highlightToBinary,
"To Binary-highlightReverse": ByteRepr.highlightFromBinary,
"From Hexdump-highlight": Hexdump.highlightFrom,
"From Hexdump-highlightReverse": Hexdump.highlightTo,
"To Hexdump-highlight": Hexdump.highlightTo,
"To Hexdump-highlightReverse": Hexdump.highlightFrom,
};
export default OpModules;

View file

@ -0,0 +1,18 @@
/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/generateConfig.mjs
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import ToBase64 from "../../operations/ToBase64";
import FromBase64 from "../../operations/FromBase64";
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Default = {
"To Base64": ToBase64,
"From Base64": FromBase64,
};
export default OpModules;

View file

@ -1,20 +0,0 @@
import Diff from "../../operations/Diff.js";
/**
* Diff module.
*
* Libraries:
* - JsDIff
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Diff = {
"Diff": Diff.runDiff,
};
export default OpModules;

View file

@ -1,21 +0,0 @@
import Punycode from "../../operations/Punycode.js";
/**
* Encodings module.
*
* Libraries:
* - punycode
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Encodings = {
"To Punycode": Punycode.runToAscii,
"From Punycode": Punycode.runToUnicode,
};
export default OpModules;

View file

@ -1,22 +0,0 @@
import HTTP from "../../operations/HTTP.js";
/**
* HTTP module.
*
* Libraries:
* - UAS_parser
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.HTTP = {
"HTTP request": HTTP.runHTTPRequest,
"Strip HTTP headers": HTTP.runStripHeaders,
"Parse User Agent": HTTP.runParseUserAgent,
};
export default OpModules;

View file

@ -1,56 +0,0 @@
import Checksum from "../../operations/Checksum.js";
import Hash from "../../operations/Hash.js";
/**
* Hashing module.
*
* Libraries:
* - CryptoApi
* - node-md6
* - js-sha3
* - ./Checksum.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Hashing = {
"Analyse hash": Hash.runAnalyse,
"Generate all hashes": Hash.runAll,
"MD2": Hash.runMD2,
"MD4": Hash.runMD4,
"MD5": Hash.runMD5,
"MD6": Hash.runMD6,
"SHA0": Hash.runSHA0,
"SHA1": Hash.runSHA1,
"SHA2": Hash.runSHA2,
"SHA3": Hash.runSHA3,
"Keccak": Hash.runKeccak,
"Shake": Hash.runShake,
"RIPEMD": Hash.runRIPEMD,
"HAS-160": Hash.runHAS,
"Whirlpool": Hash.runWhirlpool,
"Snefru": Hash.runSnefru,
"CTPH": Hash.runCTPH,
"SSDEEP": Hash.runSSDEEP,
"Compare CTPH hashes": Hash.runCompareCTPH,
"Compare SSDEEP hashes": Hash.runCompareSSDEEP,
"HMAC": Hash.runHMAC,
"Bcrypt": Hash.runBcrypt,
"Bcrypt compare": Hash.runBcryptCompare,
"Bcrypt parse": Hash.runBcryptParse,
"Scrypt": Hash.runScrypt,
"Fletcher-8 Checksum": Checksum.runFletcher8,
"Fletcher-16 Checksum": Checksum.runFletcher16,
"Fletcher-32 Checksum": Checksum.runFletcher32,
"Fletcher-64 Checksum": Checksum.runFletcher64,
"Adler-32 Checksum": Checksum.runAdler32,
"CRC-16 Checksum": Checksum.runCRC16,
"CRC-32 Checksum": Checksum.runCRC32,
"TCP/IP Checksum": Checksum.runTCPIP,
};
export default OpModules;

View file

@ -1,25 +0,0 @@
import Image from "../../operations/Image.js";
/**
* Image module.
*
* Libraries:
* - exif-parser
* - remove-exif
* - ./FileType.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Image = {
"Extract EXIF": Image.runExtractEXIF,
"Remove EXIF": Image.runRemoveEXIF,
"Render Image": Image.runRenderImage,
};
export default OpModules;

View file

@ -1,25 +0,0 @@
import IP from "../../operations/IP.js";
/**
* JSBN module.
*
* Libraries:
* - jsbn
* - ./Checksum.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.JSBN = {
"Parse IP range": IP.runParseIpRange,
"Parse IPv6 address": IP.runParseIPv6,
"Parse IPv4 header": IP.runParseIPv4Header,
"Change IP format": IP.runChangeIpFormat,
"Group IP addresses": IP.runGroupIps,
};
export default OpModules;

View file

@ -1,45 +0,0 @@
/**
* Imports all modules for builds which do not load modules separately.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import OpModules from "./Default.js";
import BSONModule from "./BSON.js";
import CharEncModule from "./CharEnc.js";
import CipherModule from "./Ciphers.js";
import CodeModule from "./Code.js";
import CompressionModule from "./Compression.js";
import DiffModule from "./Diff.js";
import EncodingModule from "./Encodings.js";
import HashingModule from "./Hashing.js";
import HTTPModule from "./HTTP.js";
import ImageModule from "./Image.js";
import JSBNModule from "./JSBN.js";
import PublicKeyModule from "./PublicKey.js";
import RegexModule from "./Regex.js";
import ShellcodeModule from "./Shellcode.js";
import URLModule from "./URL.js";
Object.assign(
OpModules,
BSONModule,
CharEncModule,
CipherModule,
CodeModule,
CompressionModule,
DiffModule,
EncodingModule,
HashingModule,
HTTPModule,
ImageModule,
JSBNModule,
PublicKeyModule,
RegexModule,
ShellcodeModule,
URLModule
);
export default OpModules;

View file

@ -0,0 +1,19 @@
/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/generateConfig.mjs
*
* Imports all modules for builds which do not load modules separately.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import DefaultModule from "./Default";
let OpModules = {};
Object.assign(
OpModules,
DefaultModule,
);
export default OpModules;

View file

@ -1,25 +0,0 @@
import PublicKey from "../../operations/PublicKey.js";
/**
* PublicKey module.
*
* Libraries:
* - jsrsasign
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.PublicKey = {
"Parse X.509 certificate": PublicKey.runParseX509,
"Parse ASN.1 hex string": PublicKey.runParseAsn1HexString,
"PEM to Hex": PublicKey.runPemToHex,
"Hex to PEM": PublicKey.runHexToPem,
"Hex to Object Identifier": PublicKey.runHexToObjectIdentifier,
"Object Identifier to Hex": PublicKey.runObjectIdentifierToHex,
};
export default OpModules;

View file

@ -1,30 +0,0 @@
import Extract from "../../operations/Extract.js";
import Regex from "../../operations/Regex.js";
/**
* Regex module.
*
* Libraries:
* - XRegExp
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Regex = {
"Regular expression": Regex.runRegex,
"Find / Replace": Regex.runFindReplace,
"Strings": Extract.runStrings,
"Extract IP addresses": Extract.runIp,
"Extract email addresses": Extract.runEmail,
"Extract MAC addresses": Extract.runMac,
"Extract URLs": Extract.runUrls,
"Extract domains": Extract.runDomains,
"Extract file paths": Extract.runFilePaths,
"Extract dates": Extract.runDates,
};
export default OpModules;

View file

@ -1,20 +0,0 @@
import Shellcode from "../../operations/Shellcode.js";
/**
* Shellcode module.
*
* Libraries:
* - DisassembleX86-64.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Shellcode = {
"Disassemble x86": Shellcode.runDisassemble,
};
export default OpModules;

View file

@ -1,23 +0,0 @@
import URL_ from "../../operations/URL.js";
/**
* URL module.
*
* Libraries:
* - Utils.js
* - url
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.URL = {
"URL Encode": URL_.runTo,
"URL Decode": URL_.runFrom,
"Parse URI": URL_.runParse,
};
export default OpModules;

View file

@ -1,4 +1,4 @@
import Utils from "../Utils.js";
import Utils from "../Utils";
/**
@ -12,65 +12,6 @@ import Utils from "../Utils.js";
*/
const Base64 = {
/**
* @constant
* @default
*/
ALPHABET: "A-Za-z0-9+/=",
/**
* @constant
* @default
*/
ALPHABET_OPTIONS: [
{name: "Standard: A-Za-z0-9+/=", value: "A-Za-z0-9+/="},
{name: "URL safe: A-Za-z0-9-_", value: "A-Za-z0-9-_"},
{name: "Filename safe: A-Za-z0-9+-=", value: "A-Za-z0-9+\\-="},
{name: "itoa64: ./0-9A-Za-z=", value: "./0-9A-Za-z="},
{name: "XML: A-Za-z0-9_.", value: "A-Za-z0-9_."},
{name: "y64: A-Za-z0-9._-", value: "A-Za-z0-9._-"},
{name: "z64: 0-9a-zA-Z+/=", value: "0-9a-zA-Z+/="},
{name: "Radix-64: 0-9A-Za-z+/=", value: "0-9A-Za-z+/="},
{name: "Uuencoding: [space]-_", value: " -_"},
{name: "Xxencoding: +-0-9A-Za-z", value: "+\\-0-9A-Za-z"},
{name: "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r", value: "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"},
{name: "ROT13: N-ZA-Mn-za-m0-9+/=", value: "N-ZA-Mn-za-m0-9+/="},
{name: "UNIX crypt: ./0-9A-Za-z", value: "./0-9A-Za-z"},
],
/**
* To Base64 operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
const alphabet = args[0] || Base64.ALPHABET;
return Utils.toBase64(new Uint8Array(input), alphabet);
},
/**
* @constant
* @default
*/
REMOVE_NON_ALPH_CHARS: true,
/**
* From Base64 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFrom: function(input, args) {
let alphabet = args[0] || Base64.ALPHABET,
removeNonAlphChars = args[1];
return Utils.fromBase64(input, alphabet, "byteArray", removeNonAlphChars);
},
/**
* @constant
* @default
@ -311,37 +252,24 @@ const Base64 = {
offset0 + "\n" + offset1 + "\n" + offset2);
},
/**
* Highlight to Base64
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightTo: function(pos, args) {
pos[0].start = Math.floor(pos[0].start / 3 * 4);
pos[0].end = Math.ceil(pos[0].end / 3 * 4);
return pos;
},
/**
* Highlight from Base64
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightFrom: function(pos, args) {
pos[0].start = Math.ceil(pos[0].start / 4 * 3);
pos[0].end = Math.floor(pos[0].end / 4 * 3);
return pos;
},
};
export default Base64;
export const ALPHABET = "A-Za-z0-9+/=";
export const ALPHABET_OPTIONS = [
{name: "Standard: A-Za-z0-9+/=", value: "A-Za-z0-9+/="},
{name: "URL safe: A-Za-z0-9-_", value: "A-Za-z0-9-_"},
{name: "Filename safe: A-Za-z0-9+-=", value: "A-Za-z0-9+\\-="},
{name: "itoa64: ./0-9A-Za-z=", value: "./0-9A-Za-z="},
{name: "XML: A-Za-z0-9_.", value: "A-Za-z0-9_."},
{name: "y64: A-Za-z0-9._-", value: "A-Za-z0-9._-"},
{name: "z64: 0-9a-zA-Z+/=", value: "0-9a-zA-Z+/="},
{name: "Radix-64: 0-9A-Za-z+/=", value: "0-9A-Za-z+/="},
{name: "Uuencoding: [space]-_", value: " -_"},
{name: "Xxencoding: +-0-9A-Za-z", value: "+\\-0-9A-Za-z"},
{name: "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r", value: "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"},
{name: "ROT13: N-ZA-Mn-za-m0-9+/=", value: "N-ZA-Mn-za-m0-9+/="},
{name: "UNIX crypt: ./0-9A-Za-z", value: "./0-9A-Za-z"},
];

View file

@ -0,0 +1,84 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {ALPHABET, ALPHABET_OPTIONS} from "../lib/Base64";
/**
* From Base64 operation
*/
class FromBase64 extends Operation {
/**
* ToBase64 constructor
*/
constructor() {
super();
this.name = "From Base64";
this.module = "Default";
this.description = "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.<br><br>This operation decodes data from an ASCII Base64 string back into its raw format.<br><br>e.g. <code>aGVsbG8=</code> becomes <code>hello</code>";
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 {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let alphabet = args[0] || ALPHABET,
removeNonAlphChars = args[1];
return Utils.fromBase64(input, alphabet, "byteArray", removeNonAlphChars);
}
/**
* Highlight to Base64
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
pos[0].start = Math.ceil(pos[0].start / 4 * 3);
pos[0].end = Math.floor(pos[0].end / 4 * 3);
return pos;
}
/**
* Highlight from Base64
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
pos[0].start = Math.floor(pos[0].start / 3 * 4);
pos[0].end = Math.ceil(pos[0].end / 3 * 4);
return pos;
}
}
export default FromBase64;

View file

@ -0,0 +1,77 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {ALPHABET, ALPHABET_OPTIONS} from "../lib/Base64";
/**
* To Base64 operation
*/
class ToBase64 extends Operation {
/**
* ToBase64 constructor
*/
constructor() {
super();
this.name = "To Base64";
this.module = "Default";
this.description = "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.<br><br>This operation decodes data from an ASCII Base64 string back into its raw format.<br><br>e.g. <code>aGVsbG8=</code> becomes <code>hello</code>";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
name: "Alphabet",
type: "editableOption",
value: ALPHABET_OPTIONS
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const alphabet = args[0] || ALPHABET;
return Utils.toBase64(new Uint8Array(input), alphabet);
}
/**
* Highlight to Base64
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
pos[0].start = Math.floor(pos[0].start / 3 * 4);
pos[0].end = Math.ceil(pos[0].end / 3 * 4);
return pos;
}
/**
* Highlight from Base64
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
pos[0].start = Math.ceil(pos[0].start / 4 * 3);
pos[0].end = Math.floor(pos[0].end / 4 * 3);
return pos;
}
}
export default ToBase64;

View file

@ -0,0 +1,7 @@
import ToBase64 from "./ToBase64";
import FromBase64 from "./FromBase64";
export default [
ToBase64,
FromBase64
];

View file

@ -120,7 +120,7 @@ const Arithmetic = {
* @returns {BigNumber[]}
*/
_createNumArray: function(input, delim) {
delim = Utils.charRep[delim || "Space"];
delim = Utils.charRep(delim || "Space");
let splitNumbers = input.split(delim),
numbers = [],
num;

View file

@ -36,7 +36,7 @@ const ByteRepr = {
* @returns {string}
*/
runToHex: function(input, args) {
const delim = Utils.charRep[args[0] || "Space"];
const delim = Utils.charRep(args[0] || "Space");
return Utils.toHex(new Uint8Array(input), delim, 2);
},
@ -63,7 +63,7 @@ const ByteRepr = {
* @returns {string}
*/
runToOct: function(input, args) {
const delim = Utils.charRep[args[0] || "Space"];
const delim = Utils.charRep(args[0] || "Space");
return input.map(val => val.toString(8)).join(delim);
},
@ -77,7 +77,7 @@ const ByteRepr = {
* @returns {byteArray}
*/
runFromOct: function(input, args) {
const delim = Utils.charRep[args[0] || "Space"];
const delim = Utils.charRep(args[0] || "Space");
if (input.length === 0) return [];
return input.split(delim).map(val => parseInt(val, 8));
},
@ -97,7 +97,7 @@ const ByteRepr = {
* @returns {string}
*/
runToCharcode: function(input, args) {
let delim = Utils.charRep[args[0] || "Space"],
let delim = Utils.charRep(args[0] || "Space"),
base = args[1],
output = "",
padding = 2,
@ -139,7 +139,7 @@ const ByteRepr = {
* @returns {byteArray}
*/
runFromCharcode: function(input, args) {
let delim = Utils.charRep[args[0] || "Space"],
let delim = Utils.charRep(args[0] || "Space"),
base = args[1],
bites = input.split(delim),
i = 0;
@ -181,7 +181,7 @@ const ByteRepr = {
* @returns {Object[]} pos
*/
highlightTo: function(pos, args) {
let delim = Utils.charRep[args[0] || "Space"],
let delim = Utils.charRep(args[0] || "Space"),
len = delim === "\r\n" ? 1 : delim.length;
pos[0].start = pos[0].start * (2 + len);
@ -206,7 +206,7 @@ const ByteRepr = {
* @returns {Object[]} pos
*/
highlightFrom: function(pos, args) {
let delim = Utils.charRep[args[0] || "Space"],
let delim = Utils.charRep(args[0] || "Space"),
len = delim === "\r\n" ? 1 : delim.length,
width = len + 2;
@ -232,7 +232,7 @@ const ByteRepr = {
* @returns {string}
*/
runToDecimal: function(input, args) {
const delim = Utils.charRep[args[0]];
const delim = Utils.charRep(args[0]);
return input.join(delim);
},
@ -245,7 +245,7 @@ const ByteRepr = {
* @returns {byteArray}
*/
runFromDecimal: function(input, args) {
const delim = Utils.charRep[args[0]];
const delim = Utils.charRep(args[0]);
let byteStr = input.split(delim), output = [];
if (byteStr[byteStr.length-1] === "")
byteStr = byteStr.slice(0, byteStr.length-1);
@ -265,7 +265,7 @@ const ByteRepr = {
* @returns {string}
*/
runToBinary: function(input, args) {
let delim = Utils.charRep[args[0] || "Space"],
let delim = Utils.charRep(args[0] || "Space"),
output = "",
padding = 8;
@ -289,7 +289,7 @@ const ByteRepr = {
* @returns {byteArray}
*/
runFromBinary: function(input, args) {
const delimRegex = Utils.regexRep[args[0] || "Space"];
const delimRegex = Utils.regexRep(args[0] || "Space");
input = input.replace(delimRegex, "");
const output = [];
@ -311,7 +311,7 @@ const ByteRepr = {
* @returns {Object[]} pos
*/
highlightToBinary: function(pos, args) {
const delim = Utils.charRep[args[0] || "Space"];
const delim = Utils.charRep(args[0] || "Space");
pos[0].start = pos[0].start * (8 + delim.length);
pos[0].end = pos[0].end * (8 + delim.length) - delim.length;
return pos;
@ -328,7 +328,7 @@ const ByteRepr = {
* @returns {Object[]} pos
*/
highlightFromBinary: function(pos, args) {
const delim = Utils.charRep[args[0] || "Space"];
const delim = Utils.charRep(args[0] || "Space");
pos[0].start = pos[0].start === 0 ? 0 : Math.floor(pos[0].start / (8 + delim.length));
pos[0].end = pos[0].end === 0 ? 0 : Math.ceil(pos[0].end / (8 + delim.length));
return pos;

View file

@ -1,4 +1,4 @@
import cptable from "../lib/js-codepage/cptable.js";
import cptable from "../vendor/js-codepage/cptable.js";
/**

View file

@ -4,7 +4,7 @@ import rawinflate from "zlibjs/bin/rawinflate.min";
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min";
import zip from "zlibjs/bin/zip.min";
import unzip from "zlibjs/bin/unzip.min";
import bzip2 from "exports-loader?bzip2!../lib/bzip2.js";
import bzip2 from "exports-loader?bzip2!../vendor/bzip2.js";
const Zlib = {
RawDeflate: rawdeflate.Zlib.RawDeflate,

View file

@ -378,7 +378,7 @@ const Hash = {
* @returns {Number}
*/
runCompareCTPH: function (input, args) {
const samples = input.split(Utils.charRep[args[0]]);
const samples = input.split(Utils.charRep(args[0]));
if (samples.length !== 2) throw "Incorrect number of samples.";
return ctph.similarity(samples[0], samples[1]);
},
@ -392,7 +392,7 @@ const Hash = {
* @returns {Number}
*/
runCompareSSDEEP: function (input, args) {
const samples = input.split(Utils.charRep[args[0]]);
const samples = input.split(Utils.charRep(args[0]));
if (samples.length !== 2) throw "Incorrect number of samples.";
return ssdeep.similarity(samples[0], samples[1]);
},

View file

@ -346,7 +346,7 @@ const IP = {
* @returns {string}
*/
runGroupIps: function(input, args) {
let delim = Utils.charRep[args[0]],
let delim = Utils.charRep(args[0]),
cidr = args[1],
onlySubnets = args[2],
ipv4Mask = cidr < 32 ? ~(0xFFFFFFFF >>> cidr) : 0xFFFFFFFF,

View file

@ -1,5 +1,5 @@
import * as ExifParser from "exif-parser";
import removeEXIF from "../lib/remove-exif.js";
import removeEXIF from "../vendor/remove-exif.js";
import Utils from "../Utils.js";
import FileType from "./FileType.js";

View file

@ -101,8 +101,8 @@ const MorseCode = {
const dash = format[0];
const dot = format[1];
const letterDelim = Utils.charRep[args[1]];
const wordDelim = Utils.charRep[args[2]];
const letterDelim = Utils.charRep(args[1]);
const wordDelim = Utils.charRep(args[2]);
input = input.split(/\r?\n/);
input = Array.prototype.map.call(input, function(line) {
@ -163,8 +163,8 @@ const MorseCode = {
reverseTable();
}
const letterDelim = Utils.charRep[args[0]];
const wordDelim = Utils.charRep[args[1]];
const letterDelim = Utils.charRep(args[0]);
const wordDelim = Utils.charRep(args[1]);
input = input.replace(/-|||_||—|dash/ig, "<dash>"); //hyphen-minus|hyphen|minus-sign|undersore|en-dash|em-dash
input = input.replace(/\.|·|dot/ig, "<dot>");

View file

@ -36,7 +36,7 @@ const SeqUtils = {
* @returns {string}
*/
runSort: function (input, args) {
let delim = Utils.charRep[args[0]],
let delim = Utils.charRep(args[0]),
sortReverse = args[1],
order = args[2],
sorted = input.split(delim);
@ -64,7 +64,7 @@ const SeqUtils = {
* @returns {string}
*/
runUnique: function (input, args) {
const delim = Utils.charRep[args[0]];
const delim = Utils.charRep(args[0]);
return input.split(delim).unique().join(delim);
},

View file

@ -1,4 +1,4 @@
import disassemble from "../lib/DisassembleX86-64.js";
import disassemble from "../vendor/DisassembleX86-64.js";
/**

View file

@ -120,7 +120,7 @@ const StrUtils = {
* @returns {string}
*/
runFilter: function(input, args) {
let delim = Utils.charRep[args[0]],
let delim = Utils.charRep(args[0]),
regex,
reverse = args[2];
@ -295,7 +295,7 @@ const StrUtils = {
let delimiter = args[0],
number = args[1];
delimiter = Utils.charRep[delimiter];
delimiter = Utils.charRep(delimiter);
const splitInput = input.split(delimiter);
return splitInput
@ -323,7 +323,7 @@ const StrUtils = {
let delimiter = args[0],
number = args[1];
delimiter = Utils.charRep[delimiter];
delimiter = Utils.charRep(delimiter);
const splitInput = input.split(delimiter);
return splitInput

Some files were not shown because too many files have changed in this diff Show more