merge esm

This commit is contained in:
d98762625 2018-04-04 16:00:37 +01:00
commit 7ce1bf1048
181 changed files with 5628 additions and 3545 deletions

View File

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

14
.editorconfig Normal file
View File

@ -0,0 +1,14 @@
# top-most EditorConfig file
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

View File

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

View File

@ -1,6 +1,6 @@
{
"parserOptions": {
"ecmaVersion": 8,
"ecmaVersion": 9,
"ecmaFeatures": {
"impliedStrict": true
},
@ -84,12 +84,12 @@
"no-whitespace-before-property": "error",
"operator-linebreak": ["error", "after"],
"space-in-parens": "error",
"no-var": "error"
"no-var": "error",
"prefer-const": "error"
},
"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", "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", "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 = {};
const 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;
@ -131,10 +130,10 @@ module.exports = function (grunt) {
grunt.initConfig({
clean: {
dev: ["build/dev/*", "src/core/config/MetaConfig.js"],
prod: ["build/prod/*", "src/core/config/MetaConfig.js"],
test: ["build/test/*", "src/core/config/MetaConfig.js"],
node: ["build/node/*", "src/core/config/MetaConfig.js"],
dev: ["build/dev/*"],
prod: ["build/prod/*"],
node: ["build/node/*"],
config: ["src/core/config/OperationConfig.json", "src/core/config/modules/*", "src/code/operations/index.mjs"],
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"
},
});
};

95
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "7.8.0",
"version": "7.9.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -519,6 +519,14 @@
"lodash": "4.17.5",
"source-map": "0.5.7",
"trim-right": "1.0.1"
},
"dependencies": {
"jsesc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
"integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
"dev": true
}
}
},
"babel-helper-builder-binary-assignment-operator-visitor": {
@ -1161,6 +1169,11 @@
"tweetnacl": "0.14.5"
}
},
"bcryptjs": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
"integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
},
"big.js": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
@ -1440,6 +1453,11 @@
}
}
},
"bson": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/bson/-/bson-2.0.4.tgz",
"integrity": "sha512-e/GPy6CE0xL7MOYYRMIEwPGKF21WNaQdPIpV0YvaQDoR7oc47KUZ8c2P/TlRJVQP8RZ4CEsArGBC1NbkCRvl1w=="
},
"buffer": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
@ -5282,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",
@ -5442,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",
@ -6877,10 +6869,9 @@
}
},
"jsesc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
"integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
"dev": true
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz",
"integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4="
},
"jshint": {
"version": "2.9.5",
@ -8402,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",
@ -10473,6 +10451,11 @@
"ajv": "5.2.3"
}
},
"scryptsy": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.0.0.tgz",
"integrity": "sha1-Jiw28CMc+nZU4jY/o5TNLexm83g="
},
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@ -11024,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",
@ -12176,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",
@ -12687,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

@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "7.8.0",
"version": "7.9.0",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef",
@ -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,19 +59,21 @@
"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": {
"babel-polyfill": "^6.26.0",
"bcryptjs": "^2.4.3",
"bignumber.js": "^6.0.0",
"bootstrap": "^3.3.7",
"bootstrap-colorpicker": "^2.5.2",
"bootstrap-switch": "^3.3.4",
"bson": "^2.0.4",
"crypto-api": "^0.8.0",
"crypto-js": "^3.1.9-1",
"ctph.js": "0.0.5",
@ -88,6 +88,7 @@
"js-crc": "^0.2.0",
"js-sha3": "^0.7.0",
"jsbn": "^1.1.0",
"jsesc": "^2.5.1",
"jsonpath": "^1.0.0",
"jsrsasign": "8.0.6",
"lodash": "^4.17.5",
@ -99,6 +100,7 @@
"node-md6": "^0.1.0",
"nwmatcher": "^1.4.3",
"otp": "^0.1.3",
"scryptsy": "^2.0.0",
"sladex-blowfish": "^0.8.1",
"sortablejs": "^1.7.0",
"split.js": "^1.3.5",

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");
const 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";
@ -132,7 +132,7 @@ function silentBake(data) {
*/
function loadRequiredModules(recipeConfig) {
recipeConfig.forEach(op => {
let module = self.OperationConfig[op.op].module;
const module = self.OperationConfig[op.op].module;
if (!OpModules.hasOwnProperty(module)) {
log.info("Loading module " + module);

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";
/**
@ -23,16 +23,16 @@ const FlowControl = {
* @returns {Object} The updated state of the recipe.
*/
runFork: async function(state) {
let opList = state.opList,
const opList = state.opList,
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],
subOpList = [],
inputs = [],
subOpList = [];
let inputs = [],
i;
if (input)
@ -41,15 +41,15 @@ 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]);
}
}
let recipe = new Recipe(),
output = "",
const recipe = new Recipe();
let output = "",
progress = 0;
state.forkOffset += state.progress + 1;
@ -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],
@ -223,7 +223,7 @@ const FlowControl = {
}
if (regexStr !== "") {
let strMatch = dish.get(Dish.STRING).search(regexStr) > -1;
const strMatch = dish.get(Dish.STRING).search(regexStr) > -1;
if (!invert && strMatch || invert && !strMatch) {
state.progress = jmpIndex;
state.numJumps++;
@ -274,9 +274,9 @@ const FlowControl = {
*/
_getLabelIndex: function(name, state) {
for (let o = 0; o < state.opList.length; o++) {
let operation = state.opList[o];
const operation = state.opList[o];
if (operation.name === "Label"){
let ings = operation.getIngValues();
const 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;

110
src/core/Ingredient.mjs Executable file
View File

@ -0,0 +1,110 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Utils from "./Utils";
import {fromHex} from "./lib/Hex";
/**
* 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 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++) {
const 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,16 +1,19 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import utf8 from "utf8";
import moment from "moment-timezone";
import {fromBase64} from "./lib/Base64";
import {toHexFast, fromHex} from "./lib/Hex";
/**
* 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.
@ -22,7 +25,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
@ -34,7 +37,7 @@ const Utils = {
}
return String.fromCharCode(o);
},
}
/**
@ -47,7 +50,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
@ -61,7 +64,7 @@ const Utils = {
}
return c.charCodeAt(0);
},
}
/**
@ -87,8 +90,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);
@ -97,7 +99,7 @@ const Utils = {
});
return paddedBytes;
},
}
/**
@ -115,13 +117,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;
},
}
/**
@ -138,11 +139,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");
},
}
/**
@ -159,11 +159,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");
},
}
/**
@ -173,7 +172,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));
}
@ -184,7 +183,7 @@ const Utils = {
str = str.replace(re, ".");
if (!preserveWs) str = str.replace(wsRe, ".");
return str;
},
}
/**
@ -200,25 +199,38 @@ const Utils = {
* // returns "\n"
* Utils.parseEscapedChars("\\n");
*/
parseEscapedChars: function(str) {
return str.replace(/(\\)?\\([nrtbf]|x[\da-fA-F]{2})/g, function(m, a, b) {
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]) {
case "n":
return "\n";
case "r":
return "\r";
case "t":
return "\t";
case "0":
return "\0";
case "b":
return "\b";
case "t":
return "\t";
case "n":
return "\n";
case "v":
return "\v";
case "f":
return "\f";
case "r":
return "\r";
case '"':
return '"';
case "'":
return "'";
case "x":
return Utils.chr(parseInt(b.substr(1), 16));
return String.fromCharCode(parseInt(b.substr(1), 16));
case "u":
if (b[1] === "{")
return String.fromCodePoint(parseInt(b.slice(2, -1), 16));
else
return String.fromCharCode(parseInt(b.substr(1), 16));
}
});
},
}
/**
@ -232,9 +244,9 @@ const Utils = {
* // returns "\[example\]"
* Utils.escapeRegex("[example]");
*/
escapeRegex: function(str) {
static escapeRegex(str) {
return str.replace(/([.*+?^=!:${}()|[\]/\\])/g, "\\$1");
},
}
/**
@ -253,14 +265,14 @@ 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++) {
if (i < alphStr.length - 2 &&
alphStr[i+1] === "-" &&
alphStr[i] !== "\\") {
let start = Utils.ord(alphStr[i]),
const start = Utils.ord(alphStr[i]),
end = Utils.ord(alphStr[i+2]);
for (let j = start; j <= end; j++) {
@ -277,7 +289,7 @@ const Utils = {
}
}
return alphArr;
},
}
/**
@ -298,19 +310,19 @@ 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);
return fromHex(str);
case "base64":
return Utils.fromBase64(str, null, "byteArray");
return fromBase64(str, null, "byteArray");
case "utf8":
return Utils.strToUtf8ByteArray(str);
case "latin1":
default:
return Utils.strToByteArray(str);
}
},
}
/**
@ -322,28 +334,28 @@ const Utils = {
* @returns {string}
*
* @example
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
* Utils.convertToByteArray("Привет", "utf8");
* // returns "Привет"
* Utils.convertToByteString("Привет", "utf8");
*
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
* Utils.convertToByteArray("d097d0b4d180d0b0d0b2d181d182d0b2d183d0b9d182d0b5", "hex");
* // returns "Здравствуйте"
* Utils.convertToByteString("d097d0b4d180d0b0d0b2d181d182d0b2d183d0b9d182d0b5", "hex");
*
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
* Utils.convertToByteArray("0JfQtNGA0LDQstGB0YLQstGD0LnRgtC1", "base64");
* // returns "Здравствуйте"
* Utils.convertToByteString("0JfQtNGA0LDQstGB0YLQstGD0LnRgtC1", "base64");
*/
convertToByteString: function(str, type) {
static convertToByteString(str, type) {
switch (type.toLowerCase()) {
case "hex":
return Utils.byteArrayToChars(Utils.fromHex(str));
return Utils.byteArrayToChars(fromHex(str));
case "base64":
return Utils.byteArrayToChars(Utils.fromBase64(str, null, "byteArray"));
return Utils.byteArrayToChars(fromBase64(str, null, "byteArray"));
case "utf8":
return utf8.encode(str);
case "latin1":
default:
return str;
}
},
}
/**
@ -360,7 +372,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--) {
@ -370,7 +382,7 @@ const Utils = {
if (b > 255) return Utils.strToUtf8ByteArray(str);
}
return byteArray;
},
}
/**
@ -386,7 +398,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) {
@ -398,7 +410,7 @@ const Utils = {
}
return Utils.strToByteArray(utf8Str);
},
}
/**
@ -414,7 +426,7 @@ const Utils = {
* // returns [20320,22909]
* Utils.strToCharcode("你好");
*/
strToCharcode: function(str) {
static strToCharcode(str) {
const charcode = [];
for (let i = 0; i < str.length; i++) {
@ -432,7 +444,7 @@ const Utils = {
}
return charcode;
},
}
/**
@ -448,7 +460,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);
@ -465,7 +477,7 @@ const Utils = {
// If it fails, treat it as ANSI
return str;
}
},
}
/**
@ -481,14 +493,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;
},
}
/**
@ -502,221 +514,10 @@ 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]
* @returns {string}
*
* @example
* // returns "SGVsbG8="
* Utils.toBase64([72, 101, 108, 108, 111]);
*
* // returns "SGVsbG8="
* Utils.toBase64("Hello");
*/
toBase64: function(data, alphabet) {
if (!data) return "";
if (typeof data == "string") {
data = Utils.strToByteArray(data);
}
alphabet = alphabet ?
Utils.expandAlphRange(alphabet).join("") :
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
let output = "",
chr1, chr2, chr3,
enc1, enc2, enc3, enc4,
i = 0;
while (i < data.length) {
chr1 = data[i++];
chr2 = data[i++];
chr3 = data[i++];
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output += alphabet.charAt(enc1) + alphabet.charAt(enc2) +
alphabet.charAt(enc3) + alphabet.charAt(enc4);
}
return output;
},
/**
* UnBase64's the input string using the given alphabet, returning a byte array.
*
* @param {byteArray} data
* @param {string} [alphabet]
* @param {string} [returnType="string"] - Either "string" or "byteArray"
* @param {boolean} [removeNonAlphChars=true]
* @returns {byteArray}
*
* @example
* // returns "Hello"
* Utils.fromBase64("SGVsbG8=");
*
* // returns [72, 101, 108, 108, 111]
* Utils.fromBase64("SGVsbG8=", null, "byteArray");
*/
fromBase64: function(data, alphabet, returnType, removeNonAlphChars) {
returnType = returnType || "string";
if (!data) {
return returnType === "string" ? "" : [];
}
alphabet = alphabet ?
Utils.expandAlphRange(alphabet).join("") :
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
if (removeNonAlphChars === undefined)
removeNonAlphChars = true;
let output = [],
chr1, chr2, chr3,
enc1, enc2, enc3, enc4,
i = 0;
if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
data = data.replace(re, "");
}
while (i < data.length) {
enc1 = alphabet.indexOf(data.charAt(i++));
enc2 = alphabet.indexOf(data.charAt(i++) || "=");
enc3 = alphabet.indexOf(data.charAt(i++) || "=");
enc4 = alphabet.indexOf(data.charAt(i++) || "=");
enc2 = enc2 === -1 ? 64 : enc2;
enc3 = enc3 === -1 ? 64 : enc3;
enc4 = enc4 === -1 ? 64 : enc4;
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output.push(chr1);
if (enc3 !== 64) {
output.push(chr2);
}
if (enc4 !== 64) {
output.push(chr3);
}
}
return returnType === "string" ? Utils.byteArrayToUtf8(output) : output;
},
/**
* Convert a byte array into a hex string.
*
* @param {Uint8Array|byteArray} data
* @param {string} [delim=" "]
* @param {number} [padding=2]
* @returns {string}
*
* @example
* // returns "0a 14 1e"
* Utils.toHex([10,20,30]);
*
* // returns "0a:14:1e"
* Utils.toHex([10,20,30], ":");
*/
toHex: function(data, delim, padding) {
if (!data) return "";
delim = typeof delim == "string" ? delim : " ";
padding = padding || 2;
let output = "";
for (let i = 0; i < data.length; i++) {
output += data[i].toString(16).padStart(padding, "0") + delim;
}
// Add \x or 0x to beginning
if (delim === "0x") output = "0x" + output;
if (delim === "\\x") output = "\\x" + output;
if (delim.length)
return output.slice(0, -delim.length);
else
return output;
},
/**
* Convert a byte array into a hex string as efficiently as possible with no options.
*
* @param {byteArray} data
* @returns {string}
*
* @example
* // returns "0a141e"
* Utils.toHex([10,20,30]);
*/
toHexFast: function(data) {
if (!data) return "";
const output = [];
for (let i = 0; i < data.length; i++) {
output.push((data[i] >>> 4).toString(16));
output.push((data[i] & 0x0f).toString(16));
}
return output.join("");
},
/**
* Convert a hex string into a byte array.
*
* @param {string} data
* @param {string} [delim]
* @param {number} [byteLen=2]
* @returns {byteArray}
*
* @example
* // returns [10,20,30]
* Utils.fromHex("0a 14 1e");
*
* // returns [10,20,30]
* Utils.fromHex("0a:14:1e", "Colon");
*/
fromHex: function(data, delim, byteLen) {
delim = delim || (data.indexOf(" ") >= 0 ? "Space" : "None");
byteLen = byteLen || 2;
if (delim !== "None") {
const delimRegex = Utils.regexRep[delim];
data = data.replace(delimRegex, "");
}
const output = [];
for (let i = 0; i < data.length; i += byteLen) {
output.push(parseInt(data.substr(i, byteLen), 16));
}
return output;
},
}
/**
@ -729,14 +530,13 @@ 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,
cell = "",
line = [],
lines = [];
line = [];
const lines = [];
for (let i = 0; i < data.length; i++) {
b = data[i];
@ -769,26 +569,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, "");
},
}
/**
@ -802,7 +603,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;",
@ -816,7 +617,7 @@ const Utils = {
return str.replace(/[&<>"'/`]/g, function (match) {
return HTML_CHARS[match];
});
},
}
/**
@ -829,7 +630,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;": "<",
@ -843,7 +644,7 @@ const Utils = {
return str.replace(/&#?x?[a-z0-9]{2,4};/ig, function (match) {
return HTML_CHARS[match] || match;
});
},
}
/**
@ -868,7 +669,7 @@ const Utils = {
* @param {string} str
* @returns {string}
*/
encodeURIFragment: function(str) {
static encodeURIFragment(str) {
const LEGAL_CHARS = {
"%2D": "-",
"%2E": ".",
@ -895,7 +696,7 @@ const Utils = {
return str.replace(/%[0-9A-F]{2}/g, function (match) {
return LEGAL_CHARS[match] || match;
});
},
}
/**
@ -909,10 +710,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 = "",
@ -935,27 +736,26 @@ 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);
// Parse bespoke recipe format
recipe = recipe.replace(/\n/g, "");
let m,
recipeRegex = /([^(]+)\(((?:'[^'\\]*(?:\\.[^'\\]*)*'|[^)/'])*)(\/[^)]+)?\)/g,
recipeConfig = [],
args;
let m, args;
const recipeRegex = /([^(]+)\(((?:'[^'\\]*(?:\\.[^'\\]*)*'|[^)/'])*)(\/[^)]+)?\)/g,
recipeConfig = [];
while ((m = recipeRegex.exec(recipe))) {
// Translate strings in args back to double-quotes
@ -966,7 +766,7 @@ const Utils = {
.replace(/\\'/g, "'"); // Unescape single quotes
args = "[" + args + "]";
let op = {
const op = {
op: m[1].replace(/_/g, " "),
args: JSON.parse(args)
};
@ -975,7 +775,7 @@ const Utils = {
recipeConfig.push(op);
}
return recipeConfig;
},
}
/**
@ -1005,24 +805,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;
},
}
/**
@ -1036,7 +821,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.
@ -1071,7 +856,7 @@ const Utils = {
title='Download ${Utils.escapeHtml(file.fileName)}'
download='${Utils.escapeHtml(file.fileName)}'>&#x1f4be;</a>`;
const hexFileData = Utils.toHexFast(new Uint8Array(file.bytes));
const hexFileData = toHexFast(new Uint8Array(file.bytes));
const switchToInputElem = `<a href='#switchFileToInput${i}'
class='file-switch'
@ -1117,7 +902,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
},
}
/**
@ -1131,7 +916,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 &
@ -1153,7 +938,7 @@ const Utils = {
}
return result;
},
}
/**
@ -1164,9 +949,9 @@ const Utils = {
* @param {number} y
* @returns {number}
*/
mod: function (x, y) {
static mod(x, y) {
return ((x % y) + y) % y;
},
}
/**
@ -1177,12 +962,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);
},
}
/**
@ -1193,55 +978,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;
@ -1259,7 +1052,7 @@ export default Utils;
* ["One", "Two", "Three", "One"].unique();
*/
Array.prototype.unique = function() {
let u = {}, a = [];
const u = {}, a = [];
for (let i = 0, l = this.length; i < l; i++) {
if (u.hasOwnProperty(this[i])) {
continue;

View File

@ -25,216 +25,219 @@ const Categories = [
{
name: "Data format",
ops: [
"To Hexdump",
"From Hexdump",
// "To Hexdump",
// "From Hexdump",
"To Hex",
"From Hex",
"To Charcode",
"From Charcode",
"To Decimal",
"From Decimal",
"To Binary",
"From Binary",
"To Octal",
"From Octal",
// "To Charcode",
// "From Charcode",
// "To Decimal",
// "From Decimal",
// "To Binary",
// "From Binary",
// "To Octal",
// "From Octal",
"To Base64",
"From Base64",
"Show Base64 offsets",
"To Base32",
"From Base32",
"To Base58",
"From Base58",
"To Base",
"From Base",
"To BCD",
"From BCD",
"To HTML Entity",
"From HTML Entity",
"URL Encode",
"URL Decode",
"Unescape Unicode Characters",
"To Quoted Printable",
"From Quoted Printable",
"To Punycode",
"From Punycode",
"To Hex Content",
"From Hex Content",
"PEM to Hex",
"Hex to PEM",
"Parse ASN.1 hex string",
"Change IP format",
"Encode text",
"Decode text",
"Swap endianness",
]
},
{
name: "Encryption / Encoding",
ops: [
"AES Encrypt",
"AES Decrypt",
"Blowfish Encrypt",
"Blowfish Decrypt",
"DES Encrypt",
"DES Decrypt",
"Triple DES Encrypt",
"Triple DES Decrypt",
"RC2 Encrypt",
"RC2 Decrypt",
"RC4",
"RC4 Drop",
"ROT13",
"ROT47",
"XOR",
"XOR Brute Force",
"Vigenère Encode",
"Vigenère Decode",
"To Morse Code",
"From Morse Code",
"Bifid Cipher Encode",
"Bifid Cipher Decode",
"Affine Cipher Encode",
"Affine Cipher Decode",
"Atbash Cipher",
"Substitute",
"Derive PBKDF2 key",
"Derive EVP key",
"Pseudo-Random Number Generator",
]
},
{
name: "Public Key",
ops: [
"Parse X.509 certificate",
"Parse ASN.1 hex string",
"PEM to Hex",
"Hex to PEM",
"Hex to Object Identifier",
"Object Identifier to Hex",
]
},
{
name: "Arithmetic / Logic",
ops: [
"XOR",
"XOR Brute Force",
"OR",
"NOT",
"AND",
"ADD",
"SUB",
"Sum",
"Subtract",
"Multiply",
"Divide",
"Mean",
"Median",
"Standard Deviation",
"Bit shift left",
"Bit shift right",
"Rotate left",
"Rotate right",
"ROT13",
]
},
{
name: "Networking",
ops: [
"HTTP request",
"Strip HTTP headers",
"Parse User Agent",
"Parse IP range",
"Parse IPv6 address",
"Parse IPv4 header",
"Parse URI",
"URL Encode",
"URL Decode",
"Format MAC addresses",
"Change IP format",
"Group IP addresses",
"Encode NetBIOS Name",
"Decode NetBIOS Name",
]
},
{
name: "Language",
ops: [
"Encode text",
"Decode text",
"Unescape Unicode Characters",
]
},
{
name: "Utils",
ops: [
"Diff",
"Remove whitespace",
"Remove null bytes",
"To Upper case",
"To Lower case",
"Add line numbers",
"Remove line numbers",
"Reverse",
"Sort",
"Unique",
"Split",
"Filter",
"Head",
"Tail",
"Count occurrences",
"Expand alphabet range",
"Drop bytes",
"Take bytes",
"Pad lines",
"Find / Replace",
"Regular expression",
"Offset checker",
"Hamming Distance",
"Convert distance",
"Convert area",
"Convert mass",
"Convert speed",
"Convert data units",
"Parse UNIX file permissions",
"Swap endianness",
"Parse colour code",
"Escape string",
"Unescape string",
"Pseudo-Random Number Generator",
"Sleep",
]
},
{
name: "Date / Time",
ops: [
"Parse DateTime",
"Translate DateTime Format",
"From UNIX Timestamp",
"To UNIX Timestamp",
"Windows Filetime to UNIX Timestamp",
"UNIX Timestamp to Windows Filetime",
"Extract dates",
"Sleep",
]
},
{
name: "Extractors",
ops: [
"Strings",
"Extract IP addresses",
"Extract email addresses",
"Extract MAC addresses",
"Extract URLs",
"Extract domains",
"Extract file paths",
"Extract dates",
"Regular expression",
"XPath expression",
"JPath expression",
"CSS selector",
"Extract EXIF",
// "To Base58",
// "From Base58",
// "To Base",
// "From Base",
// "To BCD",
// "From BCD",
// "To HTML Entity",
// "From HTML Entity",
// "URL Encode",
// "URL Decode",
// "Escape Unicode Characters",
// "Unescape Unicode Characters",
// "To Quoted Printable",
// "From Quoted Printable",
// "To Punycode",
// "From Punycode",
// "To Hex Content",
// "From Hex Content",
// "PEM to Hex",
// "Hex to PEM",
// "Parse ASN.1 hex string",
// "Change IP format",
// "Encode text",
// "Decode text",
// "Swap endianness",
// ]
// },
// {
// name: "Encryption / Encoding",
// ops: [
// "AES Encrypt",
// "AES Decrypt",
// "Blowfish Encrypt",
// "Blowfish Decrypt",
// "DES Encrypt",
// "DES Decrypt",
// "Triple DES Encrypt",
// "Triple DES Decrypt",
// "RC2 Encrypt",
// "RC2 Decrypt",
// "RC4",
// "RC4 Drop",
// "ROT13",
// "ROT47",
// "XOR",
// "XOR Brute Force",
// "Vigenère Encode",
// "Vigenère Decode",
// "To Morse Code",
// "From Morse Code",
// "Bifid Cipher Encode",
// "Bifid Cipher Decode",
// "Affine Cipher Encode",
// "Affine Cipher Decode",
// "Atbash Cipher",
// "Substitute",
// "Derive PBKDF2 key",
// "Derive EVP key",
// "Bcrypt",
// "Scrypt",
// "Pseudo-Random Number Generator",
]
},
// {
// name: "Public Key",
// ops: [
// "Parse X.509 certificate",
// "Parse ASN.1 hex string",
// "PEM to Hex",
// "Hex to PEM",
// "Hex to Object Identifier",
// "Object Identifier to Hex",
// ]
// },
// {
// name: "Arithmetic / Logic",
// ops: [
// "XOR",
// "XOR Brute Force",
// "OR",
// "NOT",
// "AND",
// "ADD",
// "SUB",
// "Sum",
// "Subtract",
// "Multiply",
// "Divide",
// "Mean",
// "Median",
// "Standard Deviation",
// "Bit shift left",
// "Bit shift right",
// "Rotate left",
// "Rotate right",
// "ROT13",
// ]
// },
// {
// name: "Networking",
// ops: [
// "HTTP request",
// "Strip HTTP headers",
// "Parse User Agent",
// "Parse IP range",
// "Parse IPv6 address",
// "Parse IPv4 header",
// "Parse URI",
// "URL Encode",
// "URL Decode",
// "Format MAC addresses",
// "Change IP format",
// "Group IP addresses",
// "Encode NetBIOS Name",
// "Decode NetBIOS Name",
// ]
// },
// {
// name: "Language",
// ops: [
// "Encode text",
// "Decode text",
// "Unescape Unicode Characters",
// ]
// },
// {
// name: "Utils",
// ops: [
// "Diff",
// "Remove whitespace",
// "Remove null bytes",
// "To Upper case",
// "To Lower case",
// "Add line numbers",
// "Remove line numbers",
// "Reverse",
// "Sort",
// "Unique",
// "Split",
// "Filter",
// "Head",
// "Tail",
// "Count occurrences",
// "Expand alphabet range",
// "Drop bytes",
// "Take bytes",
// "Pad lines",
// "Find / Replace",
// "Regular expression",
// "Offset checker",
// "Hamming Distance",
// "Convert distance",
// "Convert area",
// "Convert mass",
// "Convert speed",
// "Convert data units",
// "Parse UNIX file permissions",
// "Swap endianness",
// "Parse colour code",
// "Escape string",
// "Unescape string",
// "Pseudo-Random Number Generator",
// "Sleep",
// ]
// },
// {
// name: "Date / Time",
// ops: [
// "Parse DateTime",
// "Translate DateTime Format",
// "From UNIX Timestamp",
// "To UNIX Timestamp",
// "Windows Filetime to UNIX Timestamp",
// "UNIX Timestamp to Windows Filetime",
// "Extract dates",
// "Sleep",
// ]
// },
// {
// name: "Extractors",
// ops: [
// "Strings",
// "Extract IP addresses",
// "Extract email addresses",
// "Extract MAC addresses",
// "Extract URLs",
// "Extract domains",
// "Extract file paths",
// "Extract dates",
// "Regular expression",
// "XPath expression",
// "JPath expression",
// "CSS selector",
// "Extract EXIF",
// ]
// },
{
name: "Compression",
ops: [
@ -246,107 +249,112 @@ const Categories = [
"Gunzip",
"Zip",
"Unzip",
"Bzip2 Decompress",
"Tar",
"Untar",
]
},
{
name: "Hashing",
ops: [
"Analyse hash",
"Generate all hashes",
"MD2",
"MD4",
"MD5",
"MD6",
"SHA0",
"SHA1",
"SHA2",
"SHA3",
"Keccak",
"Shake",
"RIPEMD",
"HAS-160",
"Whirlpool",
"Snefru",
"SSDEEP",
"CTPH",
"Compare SSDEEP hashes",
"Compare CTPH hashes",
"HMAC",
"Fletcher-8 Checksum",
"Fletcher-16 Checksum",
"Fletcher-32 Checksum",
"Fletcher-64 Checksum",
"Adler-32 Checksum",
"CRC-16 Checksum",
"CRC-32 Checksum",
"TCP/IP Checksum",
]
},
{
name: "Code tidy",
ops: [
"Syntax highlighter",
"Generic Code Beautify",
"JavaScript Parser",
"JavaScript Beautify",
"JavaScript Minify",
"JSON Beautify",
"JSON Minify",
"XML Beautify",
"XML Minify",
"SQL Beautify",
"SQL Minify",
"CSS Beautify",
"CSS Minify",
"XPath expression",
"JPath expression",
"CSS selector",
"PHP Deserialize",
"Microsoft Script Decoder",
"Strip HTML tags",
"Diff",
"To Snake case",
"To Camel case",
"To Kebab case",
]
},
{
name: "Other",
ops: [
"Entropy",
"Frequency distribution",
"Chi Square",
"Detect File Type",
"Scan for Embedded Files",
"Disassemble x86",
"Pseudo-Random Number Generator",
"Generate UUID",
"Generate TOTP",
"Generate HOTP",
"Render Image",
"Remove EXIF",
"Extract EXIF",
"Numberwang",
"XKCD Random Number",
"Set Operations"
]
},
{
name: "Flow control",
ops: [
"Fork",
"Merge",
"Register",
"Label",
"Jump",
"Conditional Jump",
"Return",
"Comment"
// "Bzip2 Decompress",
// "Tar",
// "Untar",
]
},
// {
// name: "Hashing",
// ops: [
// "Analyse hash",
// "Generate all hashes",
// "MD2",
// "MD4",
// "MD5",
// "MD6",
// "SHA0",
// "SHA1",
// "SHA2",
// "SHA3",
// "Keccak",
// "Shake",
// "RIPEMD",
// "HAS-160",
// "Whirlpool",
// "Snefru",
// "SSDEEP",
// "CTPH",
// "Compare SSDEEP hashes",
// "Compare CTPH hashes",
// "HMAC",
// "Bcrypt",
// "Bcrypt compare",
// "Bcrypt parse",
// "Scrypt",
// "Fletcher-8 Checksum",
// "Fletcher-16 Checksum",
// "Fletcher-32 Checksum",
// "Fletcher-64 Checksum",
// "Adler-32 Checksum",
// "CRC-16 Checksum",
// "CRC-32 Checksum",
// "TCP/IP Checksum",
// ]
// },
// {
// name: "Code tidy",
// ops: [
// "Syntax highlighter",
// "Generic Code Beautify",
// "JavaScript Parser",
// "JavaScript Beautify",
// "JavaScript Minify",
// "JSON Beautify",
// "JSON Minify",
// "XML Beautify",
// "XML Minify",
// "SQL Beautify",
// "SQL Minify",
// "CSS Beautify",
// "CSS Minify",
// "XPath expression",
// "JPath expression",
// "CSS selector",
// "PHP Deserialize",
// "Microsoft Script Decoder",
// "Strip HTML tags",
// "Diff",
// "To Snake case",
// "To Camel case",
// "To Kebab case",
// "BSON serialise",
// "BSON deserialise",
// ]
// },
// {
// name: "Other",
// ops: [
// "Entropy",
// "Frequency distribution",
// "Chi Square",
// "Detect File Type",
// "Scan for Embedded Files",
// "Disassemble x86",
// "Pseudo-Random Number Generator",
// "Generate UUID",
// "Generate TOTP",
// "Generate HOTP",
// "Render Image",
// "Remove EXIF",
// "Extract EXIF",
// "Numberwang",
// "XKCD Random Number",
// ]
// },
// {
// name: "Flow control",
// ops: [
// "Fork",
// "Merge",
// "Register",
// "Label",
// "Jump",
// "Conditional Jump",
// "Return",
// "Comment"
// ]
// },
];
export default Categories;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,461 @@
{
"From Base32": {
"module": "Default",
"description": "Base32 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. It uses a smaller set of characters than Base64, usually the uppercase alphabet and the numbers 2 to 7.",
"inputType": "string",
"outputType": "byteArray",
"flowControl": false,
"args": [
{
"name": "Alphabet",
"type": "binaryString",
"value": "A-Z2-7="
},
{
"name": "Remove non-alphabet chars",
"type": "boolean",
"value": true
}
]
},
"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
}
]
},
"From Hex": {
"module": "Default",
"description": "Converts a hexadecimal byte string back into its raw value.<br><br>e.g. <code>ce 93 ce b5 ce b9 ce ac 20 cf 83 ce bf cf 85 0a</code> becomes the UTF-8 encoded string <code>Γειά σου</code>",
"inputType": "string",
"outputType": "byteArray",
"flowControl": false,
"args": [
{
"name": "Delimiter",
"type": "option",
"value": [
"Space",
"Comma",
"Semi-colon",
"Colon",
"Line feed",
"CRLF",
"0x",
"\\x",
"None"
]
}
]
},
"Gunzip": {
"module": "Compression",
"description": "Decompresses data which has been compressed using the deflate algorithm with gzip headers.",
"inputType": "byteArray",
"outputType": "byteArray",
"flowControl": false,
"args": []
},
"Gzip": {
"module": "Compression",
"description": "Compresses data using the deflate algorithm with gzip headers.",
"inputType": "byteArray",
"outputType": "byteArray",
"flowControl": false,
"args": [
{
"name": "Compression type",
"type": "option",
"value": [
"Dynamic Huffman Coding",
"Fixed Huffman Coding",
"None (Store)"
]
},
{
"name": "Filename (optional)",
"type": "string",
"value": ""
},
{
"name": "Comment (optional)",
"type": "string",
"value": ""
},
{
"name": "Include file checksum",
"type": "boolean",
"value": false
}
]
},
"Raw Deflate": {
"module": "Compression",
"description": "Compresses data using the deflate algorithm with no headers.",
"inputType": "byteArray",
"outputType": "byteArray",
"flowControl": false,
"args": [
{
"name": "Compression type",
"type": "option",
"value": [
"Dynamic Huffman Coding",
"Fixed Huffman Coding",
"None (Store)"
]
}
]
},
"Raw Inflate": {
"module": "Compression",
"description": "Decompresses data which has been compressed using the deflate algorithm with no headers.",
"inputType": "byteArray",
"outputType": "byteArray",
"flowControl": false,
"args": [
{
"name": "Start index",
"type": "number",
"value": 0
},
{
"name": "Initial output buffer size",
"type": "number",
"value": 0
},
{
"name": "Buffer expansion type",
"type": "option",
"value": [
"Adaptive",
"Block"
]
},
{
"name": "Resize buffer after decompression",
"type": "boolean",
"value": false
},
{
"name": "Verify result",
"type": "boolean",
"value": false
}
]
},
"Show Base64 offsets": {
"module": "Default",
"description": "When a string is within a block of data and the whole block is Base64'd, the string itself could be represented in Base64 in three distinct ways depending on its offset within the block.<br><br>This operation shows all possible offsets for a given string so that each possible encoding can be considered.",
"inputType": "byteArray",
"outputType": "html",
"flowControl": false,
"args": [
{
"name": "Alphabet",
"type": "binaryString",
"value": "A-Za-z0-9+/="
},
{
"name": "Show variable chars and padding",
"type": "boolean",
"value": true
}
]
},
"To Base32": {
"module": "Default",
"description": "Base32 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. It uses a smaller set of characters than Base64, usually the uppercase alphabet and the numbers 2 to 7.",
"inputType": "byteArray",
"outputType": "string",
"flowControl": false,
"args": [
{
"name": "Alphabet",
"type": "binaryString",
"value": "A-Z2-7="
}
]
},
"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"
}
]
}
]
},
"To Hex": {
"module": "Default",
"description": "Converts the input string to hexadecimal bytes separated by the specified delimiter.<br><br>e.g. The UTF-8 encoded string <code>Γειά σου</code> becomes <code>ce 93 ce b5 ce b9 ce ac 20 cf 83 ce bf cf 85 0a</code>",
"inputType": "ArrayBuffer",
"outputType": "string",
"flowControl": false,
"args": [
{
"name": "Delimiter",
"type": "option",
"value": [
"Space",
"Comma",
"Semi-colon",
"Colon",
"Line feed",
"CRLF",
"0x",
"\\x",
"None"
]
}
]
},
"Unzip": {
"module": "Compression",
"description": "Decompresses data using the PKZIP algorithm and displays it per file, with support for passwords.",
"inputType": "byteArray",
"outputType": "byteArray",
"flowControl": false,
"args": [
{
"name": "Password",
"type": "binaryString",
"value": ""
},
{
"name": "Verify result",
"type": "boolean",
"value": false
}
]
},
"Zip": {
"module": "Compression",
"description": "Compresses data using the PKZIP algorithm with the given filename.<br><br>No support for multiple files at this time.",
"inputType": "byteArray",
"outputType": "byteArray",
"flowControl": false,
"args": [
{
"name": "Filename",
"type": "string",
"value": "file.txt"
},
{
"name": "Comment",
"type": "string",
"value": ""
},
{
"name": "Password",
"type": "binaryString",
"value": ""
},
{
"name": "Compression method",
"type": "option",
"value": [
"Deflate",
"None (Store)"
]
},
{
"name": "Operating system",
"type": "option",
"value": [
"MSDOS",
"Unix",
"Macintosh"
]
},
{
"name": "Compression type",
"type": "option",
"value": [
"Dynamic Huffman Coding",
"Fixed Huffman Coding",
"None (Store)"
]
}
]
},
"Zlib Deflate": {
"module": "Compression",
"description": "Compresses data using the deflate algorithm adding zlib headers.",
"inputType": "byteArray",
"outputType": "byteArray",
"flowControl": false,
"args": [
{
"name": "Compression type",
"type": "option",
"value": [
"Dynamic Huffman Coding",
"Fixed Huffman Coding",
"None (Store)"
]
}
]
},
"Zlib Inflate": {
"module": "Compression",
"description": "Decompresses data which has been compressed using the deflate algorithm with zlib headers.",
"inputType": "byteArray",
"outputType": "byteArray",
"flowControl": false,
"args": [
{
"name": "Start index",
"type": "number",
"value": 0
},
{
"name": "Initial output buffer size",
"type": "number",
"value": 0
},
{
"name": "Buffer expansion type",
"type": "option",
"value": [
"Adaptive",
"Block"
]
},
{
"name": "Resize buffer after decompression",
"type": "boolean",
"value": false
},
{
"name": "Verify result",
"type": "boolean",
"value": false
}
]
}
}

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

@ -0,0 +1,30 @@
/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/scripts/generateConfig.mjs
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Gunzip from "../../operations/Gunzip";
import Gzip from "../../operations/Gzip";
import RawDeflate from "../../operations/RawDeflate";
import RawInflate from "../../operations/RawInflate";
import Unzip from "../../operations/Unzip";
import Zip from "../../operations/Zip";
import ZlibDeflate from "../../operations/ZlibDeflate";
import ZlibInflate from "../../operations/ZlibInflate";
const OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Compression = {
"Gunzip": Gunzip,
"Gzip": Gzip,
"Raw Deflate": RawDeflate,
"Raw Inflate": RawInflate,
"Unzip": Unzip,
"Zip": Zip,
"Zlib Deflate": ZlibDeflate,
"Zlib Inflate": ZlibInflate,
};
export default OpModules;

View File

@ -0,0 +1,28 @@
/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/scripts/generateConfig.mjs
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import FromBase32 from "../../operations/FromBase32";
import FromBase64 from "../../operations/FromBase64";
import FromHex from "../../operations/FromHex";
import ShowBase64Offsets from "../../operations/ShowBase64Offsets";
import ToBase32 from "../../operations/ToBase32";
import ToBase64 from "../../operations/ToBase64";
import ToHex from "../../operations/ToHex";
const OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Default = {
"From Base32": FromBase32,
"From Base64": FromBase64,
"From Hex": FromHex,
"Show Base64 offsets": ShowBase64Offsets,
"To Base32": ToBase32,
"To Base64": ToBase64,
"To Hex": ToHex,
};
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,52 +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,
"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,43 +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 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,
CharEncModule,
CipherModule,
CodeModule,
CompressionModule,
DiffModule,
EncodingModule,
HashingModule,
HTTPModule,
ImageModule,
JSBNModule,
PublicKeyModule,
RegexModule,
ShellcodeModule,
URLModule
);
export default OpModules;

View File

@ -0,0 +1,21 @@
/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/scripts/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";
import CompressionModule from "./Compression";
const OpModules = {};
Object.assign(
OpModules,
DefaultModule,
CompressionModule,
);
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,88 +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";
import SetOps from "../operations/SetOperations.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 operation 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 * as Ops 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/scripts/generateConfig.mjs");
process.exit(1);
}
const operationConfig = {},
modules = {};
/**
* Generate operation config and module lists.
*/
for (const opObj in Ops) {
const op = new Ops[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] = opObj;
}
/**
* 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 (const module in modules) {
let code = `/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/scripts/generateConfig.mjs
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright ${new Date().getUTCFullYear()}
* @license Apache-2.0
*/
`;
for (const opName in modules[module]) {
const objName = modules[module][opName];
code += `import ${objName} from "../../operations/${objName}";\n`;
}
code += `
const OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.${module} = {
`;
for (const 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/scripts/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 (const module in modules) {
opModulesCode += `import ${module}Module from "./${module}";\n`;
}
opModulesCode += `
const OpModules = {};
Object.assign(
OpModules,
`;
for (const 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",
@ -257,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).",
@ -913,6 +989,34 @@ const OperationConfig = {
}
]
},
"Escape Unicode Characters": {
module: "Default",
description: "Converts characters to their unicode-escaped notations.<br><br>Supports the prefixes:<ul><li><code>\\u</code></li><li><code>%u</code></li><li><code>U+</code></li></ul>e.g. <code>σου</code> becomes <code>\\u03C3\\u03BF\\u03C5</code>",
inputType: "string",
outputType: "string",
args: [
{
name: "Prefix",
type: "option",
value: Unicode.PREFIXES
},
{
name: "Encode all chars",
type: "boolean",
value: false
},
{
name: "Padding",
type: "number",
value: 4
},
{
name: "Uppercase hex",
type: "boolean",
value: true
}
]
},
"From Quoted Printable": {
module: "Default",
description: "Converts QP-encoded text back to standard text.",
@ -2433,7 +2537,7 @@ const OperationConfig = {
module: "Default",
description: "Parses a datetime string in UTC and returns the corresponding UNIX timestamp.<br><br>e.g. <code>Mon 1 January 2001 11:00:00</code> becomes <code>978346800</code><br><br>A UNIX timestamp is a 32-bit value representing the number of seconds since January 1, 1970 UTC (the UNIX epoch).",
inputType: "string",
outputType: "number",
outputType: "string",
args: [
{
name: "Units",
@ -2444,6 +2548,11 @@ const OperationConfig = {
name: "Treat as UTC",
type: "boolean",
value: DateTime.TREAT_AS_UTC
},
{
name: "Show parsed datetime",
type: "boolean",
value: true
}
]
},
@ -3555,14 +3664,40 @@ const OperationConfig = {
},
"Escape string": {
module: "Default",
description: "Escapes special characters in a string so that they do not cause conflicts. For example, <code>Don't stop me now</code> becomes <code>Don\\'t stop me now</code>.<br><br>Supports the following escape sequences:<ul><li><code>\\n</code> (Line feed/newline)</li><li><code>\\r</code> (Carriage return)</li><li><code>\\t</code> (Horizontal tab)</li><li><code>\\b</code> (Backspace)</li><li><code>\\f</code> (Form feed)</li><li><code>\\xnn</code> (Hex, where n is 0-f)</li><li><code>\\\\</code> (Backslash)</li><li><code>\\'</code> (Single quote)</li><li><code>\\&quot;</code> (Double quote)</li></ul>",
description: "Escapes special characters in a string so that they do not cause conflicts. For example, <code>Don't stop me now</code> becomes <code>Don\\'t stop me now</code>.<br><br>Supports the following escape sequences:<ul><li><code>\\n</code> (Line feed/newline)</li><li><code>\\r</code> (Carriage return)</li><li><code>\\t</code> (Horizontal tab)</li><li><code>\\b</code> (Backspace)</li><li><code>\\f</code> (Form feed)</li><li><code>\\xnn</code> (Hex, where n is 0-f)</li><li><code>\\\\</code> (Backslash)</li><li><code>\\'</code> (Single quote)</li><li><code>\\&quot;</code> (Double quote)</li><li><code>\\unnnn</code> (Unicode character)</li><li><code>\\u{nnnnnn}</code> (Unicode code point)</li></ul>",
inputType: "string",
outputType: "string",
args: []
args: [
{
name: "Escape level",
type: "option",
value: StrUtils.ESCAPE_LEVEL
},
{
name: "Escape quote",
type: "option",
value: StrUtils.QUOTE_TYPES
},
{
name: "JSON compatible",
type: "boolean",
value: false
},
{
name: "ES6 compatible",
type: "boolean",
value: true
},
{
name: "Uppercase hex",
type: "boolean",
value: false
}
]
},
"Unescape string": {
module: "Default",
description: "Unescapes characters in a string that have been escaped. For example, <code>Don\\'t stop me now</code> becomes <code>Don't stop me now</code>.<br><br>Supports the following escape sequences:<ul><li><code>\\n</code> (Line feed/newline)</li><li><code>\\r</code> (Carriage return)</li><li><code>\\t</code> (Horizontal tab)</li><li><code>\\b</code> (Backspace)</li><li><code>\\f</code> (Form feed)</li><li><code>\\xnn</code> (Hex, where n is 0-f)</li><li><code>\\\\</code> (Backslash)</li><li><code>\\'</code> (Single quote)</li><li><code>\\&quot;</code> (Double quote)</li></ul>",
description: "Unescapes characters in a string that have been escaped. For example, <code>Don\\'t stop me now</code> becomes <code>Don't stop me now</code>.<br><br>Supports the following escape sequences:<ul><li><code>\\n</code> (Line feed/newline)</li><li><code>\\r</code> (Carriage return)</li><li><code>\\t</code> (Horizontal tab)</li><li><code>\\b</code> (Backspace)</li><li><code>\\f</code> (Form feed)</li><li><code>\\xnn</code> (Hex, where n is 0-f)</li><li><code>\\\\</code> (Backslash)</li><li><code>\\'</code> (Single quote)</li><li><code>\\&quot;</code> (Double quote)</li><li><code>\\unnnn</code> (Unicode character)</li><li><code>\\u{nnnnnn}</code> (Unicode code point)</li></ul>",
inputType: "string",
outputType: "string",
args: []
@ -4020,6 +4155,7 @@ const OperationConfig = {
outputType: "number",
args: []
},
<<<<<<< HEAD:src/core/config/OperationConfig.js
"Set Operations": {
module: "Default",
description: "Performs set operations",
@ -4043,23 +4179,107 @@ const OperationConfig = {
}
]
}
=======
"Bcrypt": {
module: "Hashing",
description: "bcrypt is a password hashing function designed by Niels Provos and David Mazières, based on the Blowfish cipher, and presented at USENIX in 1999. Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the iteration count (rounds) can be increased to make it slower, so it remains resistant to brute-force search attacks even with increasing computation power.<br><br>Enter the password in the input to generate its hash.",
inputType: "string",
outputType: "string",
args: [
{
name: "Rounds",
type: "number",
value: Hash.BCRYPT_ROUNDS
}
]
},
"Bcrypt compare": {
module: "Hashing",
description: "Tests whether the input matches the given bcrypt hash. To test multiple possible passwords, use the 'Fork' operation.",
inputType: "string",
outputType: "string",
args: [
{
name: "Hash",
type: "string",
value: ""
}
]
},
"Bcrypt parse": {
module: "Hashing",
description: "Parses a bcrypt hash to determine the number of rounds used, the salt, and the password hash.",
inputType: "string",
outputType: "string",
args: []
},
"Scrypt": {
module: "Hashing",
description: "scrypt is a password-based key derivation function (PBKDF) created by Colin Percival. The algorithm was specifically designed to make it costly to perform large-scale custom hardware attacks by requiring large amounts of memory. In 2016, the scrypt algorithm was published by IETF as RFC 7914.<br><br>Enter the password in the input to generate its hash.",
inputType: "string",
outputType: "string",
args: [
{
name: "Salt",
type: "toggleString",
value: "",
toggleValues: Hash.KEY_FORMAT
},
{
name: "Iterations (N)",
type: "number",
value: Hash.SCRYPT_ITERATIONS
},
{
name: "Memory factor (r)",
type: "number",
value: Hash.SCRYPT_MEM_FACTOR
},
{
name: "Parallelization factor (p)",
type: "number",
value: Hash.SCRYPT_PARALLEL_FACTOR
},
{
name: "Key length",
type: "number",
value: Hash.SCRYPT_KEY_LENGTH
},
]
},
"BSON serialise": {
module: "BSON",
description: "BSON is a computer data interchange format used mainly as a data storage and network transfer format in the MongoDB database. It is a binary form for representing simple data structures, associative arrays (called objects or documents in MongoDB), and various data types of specific interest to MongoDB. The name 'BSON' is based on the term JSON and stands for 'Binary JSON'.<br><br>Input data should be valid JSON.",
inputType: "string",
outputType: "ArrayBuffer",
args: []
},
"BSON deserialise": {
module: "BSON",
description: "BSON is a computer data interchange format used mainly as a data storage and network transfer format in the MongoDB database. It is a binary form for representing simple data structures, associative arrays (called objects or documents in MongoDB), and various data types of specific interest to MongoDB. The name 'BSON' is based on the term JSON and stands for 'Binary JSON'.<br><br>Input data should be in a raw bytes format.",
inputType: "ArrayBuffer",
outputType: "string",
args: []
},
>>>>>>> esm:src/core/config/scripts/generateConfig.mjs
};
*/
/**
* 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

@ -0,0 +1,59 @@
/**
* This script automatically generates src/core/operations/index.mjs, containing
* imports for all operations in src/core/operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
/*eslint no-console: ["off"] */
import path from "path";
import fs from "fs";
import process from "process";
const dir = path.join(process.cwd() + "/src/core/config/");
if (!fs.existsSync(dir)) {
console.log("\nCWD: " + process.cwd());
console.log("Error: generateOpsIndex.mjs should be run from the project root");
console.log("Example> node --experimental-modules src/core/config/scripts/generateOpsIndex.mjs");
process.exit(1);
}
// Find all operation files
const opObjs = [];
fs.readdirSync(path.join(dir, "../operations")).forEach(file => {
if (!file.endsWith(".mjs") || file === "index.mjs") return;
opObjs.push(file.split(".mjs")[0]);
});
// Construct index file
let code = `/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/scripts/generateOpsIndex.mjs
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright ${new Date().getUTCFullYear()}
* @license Apache-2.0
*/
`;
opObjs.forEach(obj => {
code += `import ${obj} from "./${obj}";\n`;
});
code += `
export {
`;
opObjs.forEach(obj => {
code += ` ${obj},\n`;
});
code += "};\n";
// Write file
fs.writeFileSync(
path.join(dir, "../operations/index.mjs"),
code
);

141
src/core/lib/Base64.mjs Executable file
View File

@ -0,0 +1,141 @@
/**
* Base64 functions.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Utils from "../Utils";
/**
* Base64's the input byte array using the given alphabet, returning a string.
*
* @param {byteArray|Uint8Array|string} data
* @param {string} [alphabet="A-Za-z0-9+/="]
* @returns {string}
*
* @example
* // returns "SGVsbG8="
* toBase64([72, 101, 108, 108, 111]);
*
* // returns "SGVsbG8="
* toBase64("Hello");
*/
export function toBase64(data, alphabet="A-Za-z0-9+/=") {
if (!data) return "";
if (typeof data == "string") {
data = Utils.strToByteArray(data);
}
alphabet = Utils.expandAlphRange(alphabet).join("");
let output = "",
chr1, chr2, chr3,
enc1, enc2, enc3, enc4,
i = 0;
while (i < data.length) {
chr1 = data[i++];
chr2 = data[i++];
chr3 = data[i++];
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output += alphabet.charAt(enc1) + alphabet.charAt(enc2) +
alphabet.charAt(enc3) + alphabet.charAt(enc4);
}
return output;
}
/**
* UnBase64's the input string using the given alphabet, returning a byte array.
*
* @param {byteArray} data
* @param {string} [alphabet="A-Za-z0-9+/="]
* @param {string} [returnType="string"] - Either "string" or "byteArray"
* @param {boolean} [removeNonAlphChars=true]
* @returns {byteArray}
*
* @example
* // returns "Hello"
* fromBase64("SGVsbG8=");
*
* // returns [72, 101, 108, 108, 111]
* fromBase64("SGVsbG8=", null, "byteArray");
*/
export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", removeNonAlphChars=true) {
if (!data) {
return returnType === "string" ? "" : [];
}
alphabet = Utils.expandAlphRange(alphabet).join("");
const output = [];
let chr1, chr2, chr3,
enc1, enc2, enc3, enc4,
i = 0;
if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
data = data.replace(re, "");
}
while (i < data.length) {
enc1 = alphabet.indexOf(data.charAt(i++));
enc2 = alphabet.indexOf(data.charAt(i++) || "=");
enc3 = alphabet.indexOf(data.charAt(i++) || "=");
enc4 = alphabet.indexOf(data.charAt(i++) || "=");
enc2 = enc2 === -1 ? 64 : enc2;
enc3 = enc3 === -1 ? 64 : enc3;
enc4 = enc4 === -1 ? 64 : enc4;
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output.push(chr1);
if (enc3 !== 64) {
output.push(chr2);
}
if (enc4 !== 64) {
output.push(chr3);
}
}
return returnType === "string" ? Utils.byteArrayToUtf8(output) : output;
}
/**
* Base64 alphabets.
*/
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"},
];

104
src/core/lib/Hex.mjs Normal file
View File

@ -0,0 +1,104 @@
/**
* Byte representation functions.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Utils from "../Utils";
/**
* Convert a byte array into a hex string.
*
* @param {Uint8Array|byteArray} data
* @param {string} [delim=" "]
* @param {number} [padding=2]
* @returns {string}
*
* @example
* // returns "0a 14 1e"
* toHex([10,20,30]);
*
* // returns "0a:14:1e"
* toHex([10,20,30], ":");
*/
export function toHex(data, delim=" ", padding=2) {
if (!data) return "";
let output = "";
for (let i = 0; i < data.length; i++) {
output += data[i].toString(16).padStart(padding, "0") + delim;
}
// Add \x or 0x to beginning
if (delim === "0x") output = "0x" + output;
if (delim === "\\x") output = "\\x" + output;
if (delim.length)
return output.slice(0, -delim.length);
else
return output;
}
/**
* Convert a byte array into a hex string as efficiently as possible with no options.
*
* @param {byteArray} data
* @returns {string}
*
* @example
* // returns "0a141e"
* toHex([10,20,30]);
*/
export function toHexFast(data) {
if (!data) return "";
const output = [];
for (let i = 0; i < data.length; i++) {
output.push((data[i] >>> 4).toString(16));
output.push((data[i] & 0x0f).toString(16));
}
return output.join("");
}
/**
* Convert a hex string into a byte array.
*
* @param {string} data
* @param {string} [delim]
* @param {number} [byteLen=2]
* @returns {byteArray}
*
* @example
* // returns [10,20,30]
* fromHex("0a 14 1e");
*
* // returns [10,20,30]
* fromHex("0a:14:1e", "Colon");
*/
export function fromHex(data, delim, byteLen=2) {
delim = delim || (data.indexOf(" ") >= 0 ? "Space" : "None");
if (delim !== "None") {
const delimRegex = Utils.regexRep(delim);
data = data.replace(delimRegex, "");
}
const output = [];
for (let i = 0; i < data.length; i += byteLen) {
output.push(parseInt(data.substr(i, byteLen), 16));
}
return output;
}
/**
* Hexadecimal delimiters.
*/
export const HEX_DELIM_OPTIONS = ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"];

19
src/core/lib/Zlib.mjs Normal file
View File

@ -0,0 +1,19 @@
/**
* Zlib exports.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min";
const Zlib = zlibAndGzip.Zlib;
export const COMPRESSION_TYPE = ["Dynamic Huffman Coding", "Fixed Huffman Coding", "None (Store)"];
export const INFLATE_BUFFER_TYPE = ["Adaptive", "Block"];
export const ZLIB_COMPRESSION_TYPE_LOOKUP = {
"Fixed Huffman Coding": Zlib.Deflate.CompressionType.FIXED,
"Dynamic Huffman Coding": Zlib.Deflate.CompressionType.DYNAMIC,
"None (Store)": Zlib.Deflate.CompressionType.NONE,
};

View File

@ -1,347 +0,0 @@
import Utils from "../Utils.js";
/**
* Base64 operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
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
*/
BASE32_ALPHABET: "A-Z2-7=",
/**
* To Base32 operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runTo32: function(input, args) {
if (!input) return "";
let alphabet = args[0] ?
Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
output = "",
chr1, chr2, chr3, chr4, chr5,
enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
i = 0;
while (i < input.length) {
chr1 = input[i++];
chr2 = input[i++];
chr3 = input[i++];
chr4 = input[i++];
chr5 = input[i++];
enc1 = chr1 >> 3;
enc2 = ((chr1 & 7) << 2) | (chr2 >> 6);
enc3 = (chr2 >> 1) & 31;
enc4 = ((chr2 & 1) << 4) | (chr3 >> 4);
enc5 = ((chr3 & 15) << 1) | (chr4 >> 7);
enc6 = (chr4 >> 2) & 31;
enc7 = ((chr4 & 3) << 3) | (chr5 >> 5);
enc8 = chr5 & 31;
if (isNaN(chr2)) {
enc3 = enc4 = enc5 = enc6 = enc7 = enc8 = 32;
} else if (isNaN(chr3)) {
enc5 = enc6 = enc7 = enc8 = 32;
} else if (isNaN(chr4)) {
enc6 = enc7 = enc8 = 32;
} else if (isNaN(chr5)) {
enc8 = 32;
}
output += alphabet.charAt(enc1) + alphabet.charAt(enc2) + alphabet.charAt(enc3) +
alphabet.charAt(enc4) + alphabet.charAt(enc5) + alphabet.charAt(enc6) +
alphabet.charAt(enc7) + alphabet.charAt(enc8);
}
return output;
},
/**
* From Base32 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFrom32: function(input, args) {
if (!input) return [];
let alphabet = args[0] ?
Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
removeNonAlphChars = args[0];
let output = [],
chr1, chr2, chr3, chr4, chr5,
enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
i = 0;
if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[\]\\\-^]/g, "\\$&") + "]", "g");
input = input.replace(re, "");
}
while (i < input.length) {
enc1 = alphabet.indexOf(input.charAt(i++));
enc2 = alphabet.indexOf(input.charAt(i++) || "=");
enc3 = alphabet.indexOf(input.charAt(i++) || "=");
enc4 = alphabet.indexOf(input.charAt(i++) || "=");
enc5 = alphabet.indexOf(input.charAt(i++) || "=");
enc6 = alphabet.indexOf(input.charAt(i++) || "=");
enc7 = alphabet.indexOf(input.charAt(i++) || "=");
enc8 = alphabet.indexOf(input.charAt(i++) || "=");
chr1 = (enc1 << 3) | (enc2 >> 2);
chr2 = ((enc2 & 3) << 6) | (enc3 << 1) | (enc4 >> 4);
chr3 = ((enc4 & 15) << 4) | (enc5 >> 1);
chr4 = ((enc5 & 1) << 7) | (enc6 << 2) | (enc7 >> 3);
chr5 = ((enc7 & 7) << 5) | enc8;
output.push(chr1);
if (enc2 & 3 !== 0 || enc3 !== 32) output.push(chr2);
if (enc4 & 15 !== 0 || enc5 !== 32) output.push(chr3);
if (enc5 & 1 !== 0 || enc6 !== 32) output.push(chr4);
if (enc7 & 7 !== 0 || enc8 !== 32) output.push(chr5);
}
return output;
},
/**
* @constant
* @default
*/
SHOW_IN_BINARY: false,
/**
* @constant
* @default
*/
OFFSETS_SHOW_VARIABLE: true,
/**
* Show Base64 offsets operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {html}
*/
runOffsets: function(input, args) {
let alphabet = args[0] || Base64.ALPHABET,
showVariable = args[1],
offset0 = Utils.toBase64(input, alphabet),
offset1 = Utils.toBase64([0].concat(input), alphabet),
offset2 = Utils.toBase64([0, 0].concat(input), alphabet),
len0 = offset0.indexOf("="),
len1 = offset1.indexOf("="),
len2 = offset2.indexOf("="),
script = "<script type='application/javascript'>$('[data-toggle=\"tooltip\"]').tooltip()</script>",
staticSection = "",
padding = "";
if (input.length < 1) {
return "Please enter a string.";
}
// Highlight offset 0
if (len0 % 4 === 2) {
staticSection = offset0.slice(0, -3);
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset0.substr(offset0.length - 3, 1) + "</span>" +
"<span class='hl3'>" + offset0.substr(offset0.length - 2) + "</span>";
} else if (len0 % 4 === 3) {
staticSection = offset0.slice(0, -2);
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -1)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset0.substr(offset0.length - 2, 1) + "</span>" +
"<span class='hl3'>" + offset0.substr(offset0.length - 1) + "</span>";
} else {
staticSection = offset0;
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet)) + "'>" +
staticSection + "</span>";
}
if (!showVariable) {
offset0 = staticSection;
}
// Highlight offset 1
padding = "<span class='hl3'>" + offset1.substr(0, 1) + "</span>" +
"<span class='hl5'>" + offset1.substr(1, 1) + "</span>";
offset1 = offset1.substr(2);
if (len1 % 4 === 2) {
staticSection = offset1.slice(0, -3);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset1.substr(offset1.length - 3, 1) + "</span>" +
"<span class='hl3'>" + offset1.substr(offset1.length - 2) + "</span>";
} else if (len1 % 4 === 3) {
staticSection = offset1.slice(0, -2);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -1)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset1.substr(offset1.length - 2, 1) + "</span>" +
"<span class='hl3'>" + offset1.substr(offset1.length - 1) + "</span>";
} else {
staticSection = offset1;
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1)) + "'>" +
staticSection + "</span>";
}
if (!showVariable) {
offset1 = staticSection;
}
// Highlight offset 2
padding = "<span class='hl3'>" + offset2.substr(0, 2) + "</span>" +
"<span class='hl5'>" + offset2.substr(2, 1) + "</span>";
offset2 = offset2.substr(3);
if (len2 % 4 === 2) {
staticSection = offset2.slice(0, -3);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset2.substr(offset2.length - 3, 1) + "</span>" +
"<span class='hl3'>" + offset2.substr(offset2.length - 2) + "</span>";
} else if (len2 % 4 === 3) {
staticSection = offset2.slice(0, -2);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset2.substr(offset2.length - 2, 1) + "</span>" +
"<span class='hl3'>" + offset2.substr(offset2.length - 1) + "</span>";
} else {
staticSection = offset2;
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2)) + "'>" +
staticSection + "</span>";
}
if (!showVariable) {
offset2 = staticSection;
}
return (showVariable ? "Characters highlighted in <span class='hl5'>green</span> could change if the input is surrounded by more data." +
"\nCharacters highlighted in <span class='hl3'>red</span> are for padding purposes only." +
"\nUnhighlighted characters are <span data-toggle='tooltip' data-placement='top' title='Tooltip on left'>static</span>." +
"\nHover over the static sections to see what they decode to on their own.\n" +
"\nOffset 0: " + offset0 +
"\nOffset 1: " + offset1 +
"\nOffset 2: " + offset2 +
script :
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;

View File

@ -1,578 +0,0 @@
import Utils from "../Utils.js";
import rawdeflate from "zlibjs/bin/rawdeflate.min";
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";
const Zlib = {
RawDeflate: rawdeflate.Zlib.RawDeflate,
RawInflate: rawinflate.Zlib.RawInflate,
Deflate: zlibAndGzip.Zlib.Deflate,
Inflate: zlibAndGzip.Zlib.Inflate,
Gzip: zlibAndGzip.Zlib.Gzip,
Gunzip: zlibAndGzip.Zlib.Gunzip,
Zip: zip.Zlib.Zip,
Unzip: unzip.Zlib.Unzip,
};
/**
* Compression operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Compress = {
/**
* @constant
* @default
*/
COMPRESSION_TYPE: ["Dynamic Huffman Coding", "Fixed Huffman Coding", "None (Store)"],
/**
* @constant
* @default
*/
INFLATE_BUFFER_TYPE: ["Adaptive", "Block"],
/**
* @constant
* @default
*/
COMPRESSION_METHOD: ["Deflate", "None (Store)"],
/**
* @constant
* @default
*/
OS: ["MSDOS", "Unix", "Macintosh"],
/**
* @constant
* @default
*/
RAW_COMPRESSION_TYPE_LOOKUP: {
"Fixed Huffman Coding": Zlib.RawDeflate.CompressionType.FIXED,
"Dynamic Huffman Coding": Zlib.RawDeflate.CompressionType.DYNAMIC,
"None (Store)": Zlib.RawDeflate.CompressionType.NONE,
},
/**
* Raw Deflate operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runRawDeflate: function(input, args) {
const deflate = new Zlib.RawDeflate(input, {
compressionType: Compress.RAW_COMPRESSION_TYPE_LOOKUP[args[0]]
});
return Array.prototype.slice.call(deflate.compress());
},
/**
* @constant
* @default
*/
INFLATE_INDEX: 0,
/**
* @constant
* @default
*/
INFLATE_BUFFER_SIZE: 0,
/**
* @constant
* @default
*/
INFLATE_RESIZE: false,
/**
* @constant
* @default
*/
INFLATE_VERIFY: false,
/**
* @constant
* @default
*/
RAW_BUFFER_TYPE_LOOKUP: {
"Adaptive": Zlib.RawInflate.BufferType.ADAPTIVE,
"Block": Zlib.RawInflate.BufferType.BLOCK,
},
/**
* Raw Inflate operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runRawInflate: function(input, args) {
// Deal with character encoding issues
input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
let inflate = new Zlib.RawInflate(input, {
index: args[0],
bufferSize: args[1],
bufferType: Compress.RAW_BUFFER_TYPE_LOOKUP[args[2]],
resize: args[3],
verify: args[4]
}),
result = Array.prototype.slice.call(inflate.decompress());
// Raw Inflate somethimes messes up and returns nonsense like this:
// ]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]...
// e.g. Input data of [8b, 1d, dc, 44]
// Look for the first two square brackets:
if (result.length > 158 && result[0] === 93 && result[5] === 93) {
// If the first two square brackets are there, check that the others
// are also there. If they are, throw an error. If not, continue.
let valid = false;
for (let i = 0; i < 155; i += 5) {
if (result[i] !== 93) {
valid = true;
}
}
if (!valid) {
throw "Error: Unable to inflate data";
}
}
// Trust me, this is the easiest way...
return result;
},
/**
* @constant
* @default
*/
ZLIB_COMPRESSION_TYPE_LOOKUP: {
"Fixed Huffman Coding": Zlib.Deflate.CompressionType.FIXED,
"Dynamic Huffman Coding": Zlib.Deflate.CompressionType.DYNAMIC,
"None (Store)": Zlib.Deflate.CompressionType.NONE,
},
/**
* Zlib Deflate operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runZlibDeflate: function(input, args) {
const deflate = new Zlib.Deflate(input, {
compressionType: Compress.ZLIB_COMPRESSION_TYPE_LOOKUP[args[0]]
});
return Array.prototype.slice.call(deflate.compress());
},
/**
* @constant
* @default
*/
ZLIB_BUFFER_TYPE_LOOKUP: {
"Adaptive": Zlib.Inflate.BufferType.ADAPTIVE,
"Block": Zlib.Inflate.BufferType.BLOCK,
},
/**
* Zlib Inflate operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runZlibInflate: function(input, args) {
// Deal with character encoding issues
input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
const inflate = new Zlib.Inflate(input, {
index: args[0],
bufferSize: args[1],
bufferType: Compress.ZLIB_BUFFER_TYPE_LOOKUP[args[2]],
resize: args[3],
verify: args[4]
});
return Array.prototype.slice.call(inflate.decompress());
},
/**
* @constant
* @default
*/
GZIP_CHECKSUM: false,
/**
* Gzip operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runGzip: function(input, args) {
let filename = args[1],
comment = args[2],
options = {
deflateOptions: {
compressionType: Compress.ZLIB_COMPRESSION_TYPE_LOOKUP[args[0]]
},
flags: {
fhcrc: args[3]
}
};
if (filename.length) {
options.flags.fname = true;
options.filename = filename;
}
if (comment.length) {
options.flags.fcommenct = true;
options.comment = comment;
}
const gzip = new Zlib.Gzip(input, options);
return Array.prototype.slice.call(gzip.compress());
},
/**
* Gunzip operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runGunzip: function(input, args) {
// Deal with character encoding issues
input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
const gunzip = new Zlib.Gunzip(input);
return Array.prototype.slice.call(gunzip.decompress());
},
/**
* @constant
* @default
*/
PKZIP_FILENAME: "file.txt",
/**
* @constant
* @default
*/
ZIP_COMPRESSION_METHOD_LOOKUP: {
"Deflate": Zlib.Zip.CompressionMethod.DEFLATE,
"None (Store)": Zlib.Zip.CompressionMethod.STORE
},
/**
* @constant
* @default
*/
ZIP_OS_LOOKUP: {
"MSDOS": Zlib.Zip.OperatingSystem.MSDOS,
"Unix": Zlib.Zip.OperatingSystem.UNIX,
"Macintosh": Zlib.Zip.OperatingSystem.MACINTOSH
},
/**
* Zip operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runPkzip: function(input, args) {
let password = Utils.strToByteArray(args[2]),
options = {
filename: Utils.strToByteArray(args[0]),
comment: Utils.strToByteArray(args[1]),
compressionMethod: Compress.ZIP_COMPRESSION_METHOD_LOOKUP[args[3]],
os: Compress.ZIP_OS_LOOKUP[args[4]],
deflateOption: {
compressionType: Compress.ZLIB_COMPRESSION_TYPE_LOOKUP[args[5]]
},
},
zip = new Zlib.Zip();
if (password.length)
zip.setPassword(password);
zip.addFile(input, options);
return Array.prototype.slice.call(zip.compress());
},
/**
* @constant
* @default
*/
PKUNZIP_VERIFY: false,
/**
* Unzip operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runPkunzip: function(input, args) {
let options = {
password: Utils.strToByteArray(args[0]),
verify: args[1]
},
unzip = new Zlib.Unzip(input, options),
filenames = unzip.getFilenames(),
files = [];
filenames.forEach(function(fileName) {
const bytes = unzip.decompress(fileName);
const contents = Utils.byteArrayToUtf8(bytes);
const file = {
fileName: fileName,
size: contents.length,
};
const isDir = contents.length === 0 && fileName.endsWith("/");
if (!isDir) {
file.bytes = bytes;
file.contents = contents;
}
files.push(file);
});
return Utils.displayFilesAsHTML(files);
},
/**
* Bzip2 Decompress operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runBzip2Decompress: function(input, args) {
let compressed = new Uint8Array(input),
bzip2Reader,
plain = "";
bzip2Reader = bzip2.array(compressed);
plain = bzip2.simple(bzip2Reader);
return plain;
},
/**
* @constant
* @default
*/
TAR_FILENAME: "file.txt",
/**
* Tar pack operation.
*
* @author tlwr [toby@toby.codes]
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runTar: function(input, args) {
const Tarball = function() {
this.bytes = new Array(512);
this.position = 0;
};
Tarball.prototype.addEmptyBlock = function() {
const filler = new Array(512);
filler.fill(0);
this.bytes = this.bytes.concat(filler);
};
Tarball.prototype.writeBytes = function(bytes) {
const self = this;
if (this.position + bytes.length > this.bytes.length) {
this.addEmptyBlock();
}
Array.prototype.forEach.call(bytes, function(b, i) {
if (typeof b.charCodeAt !== "undefined") {
b = b.charCodeAt();
}
self.bytes[self.position] = b;
self.position += 1;
});
};
Tarball.prototype.writeEndBlocks = function() {
const numEmptyBlocks = 2;
for (let i = 0; i < numEmptyBlocks; i++) {
this.addEmptyBlock();
}
};
const fileSize = input.length.toString(8).padStart(11, "0");
const currentUnixTimestamp = Math.floor(Date.now() / 1000);
const lastModTime = currentUnixTimestamp.toString(8).padStart(11, "0");
const file = {
fileName: Utils.padBytesRight(args[0], 100),
fileMode: Utils.padBytesRight("0000664", 8),
ownerUID: Utils.padBytesRight("0", 8),
ownerGID: Utils.padBytesRight("0", 8),
size: Utils.padBytesRight(fileSize, 12),
lastModTime: Utils.padBytesRight(lastModTime, 12),
checksum: " ",
type: "0",
linkedFileName: Utils.padBytesRight("", 100),
USTARFormat: Utils.padBytesRight("ustar", 6),
version: "00",
ownerUserName: Utils.padBytesRight("", 32),
ownerGroupName: Utils.padBytesRight("", 32),
deviceMajor: Utils.padBytesRight("", 8),
deviceMinor: Utils.padBytesRight("", 8),
fileNamePrefix: Utils.padBytesRight("", 155),
};
let checksum = 0;
for (const key in file) {
const bytes = file[key];
Array.prototype.forEach.call(bytes, function(b) {
if (typeof b.charCodeAt !== "undefined") {
checksum += b.charCodeAt();
} else {
checksum += b;
}
});
}
checksum = Utils.padBytesRight(checksum.toString(8).padStart(7, "0"), 8);
file.checksum = checksum;
const tarball = new Tarball();
tarball.writeBytes(file.fileName);
tarball.writeBytes(file.fileMode);
tarball.writeBytes(file.ownerUID);
tarball.writeBytes(file.ownerGID);
tarball.writeBytes(file.size);
tarball.writeBytes(file.lastModTime);
tarball.writeBytes(file.checksum);
tarball.writeBytes(file.type);
tarball.writeBytes(file.linkedFileName);
tarball.writeBytes(file.USTARFormat);
tarball.writeBytes(file.version);
tarball.writeBytes(file.ownerUserName);
tarball.writeBytes(file.ownerGroupName);
tarball.writeBytes(file.deviceMajor);
tarball.writeBytes(file.deviceMinor);
tarball.writeBytes(file.fileNamePrefix);
tarball.writeBytes(Utils.padBytesRight("", 12));
tarball.writeBytes(input);
tarball.writeEndBlocks();
return tarball.bytes;
},
/**
* Untar unpack operation.
*
* @author tlwr [toby@toby.codes]
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {html}
*/
runUntar: function(input, args) {
const Stream = function(input) {
this.bytes = input;
this.position = 0;
};
Stream.prototype.getBytes = function(bytesToGet) {
const newPosition = this.position + bytesToGet;
const bytes = this.bytes.slice(this.position, newPosition);
this.position = newPosition;
return bytes;
};
Stream.prototype.readString = function(numBytes) {
let result = "";
for (let i = this.position; i < this.position + numBytes; i++) {
const currentByte = this.bytes[i];
if (currentByte === 0) break;
result += String.fromCharCode(currentByte);
}
this.position += numBytes;
return result;
};
Stream.prototype.readInt = function(numBytes, base) {
const string = this.readString(numBytes);
return parseInt(string, base);
};
Stream.prototype.hasMore = function() {
return this.position < this.bytes.length;
};
let stream = new Stream(input),
files = [];
while (stream.hasMore()) {
const dataPosition = stream.position + 512;
const file = {
fileName: stream.readString(100),
fileMode: stream.readString(8),
ownerUID: stream.readString(8),
ownerGID: stream.readString(8),
size: parseInt(stream.readString(12), 8), // Octal
lastModTime: new Date(1000 * stream.readInt(12, 8)), // Octal
checksum: stream.readString(8),
type: stream.readString(1),
linkedFileName: stream.readString(100),
USTARFormat: stream.readString(6).indexOf("ustar") >= 0,
};
if (file.USTARFormat) {
file.version = stream.readString(2);
file.ownerUserName = stream.readString(32);
file.ownerGroupName = stream.readString(32);
file.deviceMajor = stream.readString(8);
file.deviceMinor = stream.readString(8);
file.filenamePrefix = stream.readString(155);
}
stream.position = dataPosition;
if (file.type === "0") {
// File
files.push(file);
let endPosition = stream.position + file.size;
if (file.size % 512 !== 0) {
endPosition += 512 - (file.size % 512);
}
file.bytes = stream.getBytes(file.size);
file.contents = Utils.byteArrayToUtf8(file.bytes);
stream.position = endPosition;
} else if (file.type === "5") {
// Directory
files.push(file);
} else {
// Symlink or empty bytes
}
}
return Utils.displayFilesAsHTML(files);
},
};
export default Compress;

View File

@ -0,0 +1,90 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* From Base32 operation
*/
class FromBase32 extends Operation {
/**
* FromBase32 constructor
*/
constructor() {
super();
this.name = "From Base32";
this.module = "Default";
this.description = "Base32 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. It uses a smaller set of characters than Base64, usually the uppercase alphabet and the numbers 2 to 7.";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
name: "Alphabet",
type: "binaryString",
value: "A-Z2-7="
},
{
name: "Remove non-alphabet chars",
type: "boolean",
value: true
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
if (!input) return [];
const alphabet = args[0] ?
Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
removeNonAlphChars = args[1],
output = [];
let chr1, chr2, chr3, chr4, chr5,
enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
i = 0;
if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[\]\\\-^]/g, "\\$&") + "]", "g");
input = input.replace(re, "");
}
while (i < input.length) {
enc1 = alphabet.indexOf(input.charAt(i++));
enc2 = alphabet.indexOf(input.charAt(i++) || "=");
enc3 = alphabet.indexOf(input.charAt(i++) || "=");
enc4 = alphabet.indexOf(input.charAt(i++) || "=");
enc5 = alphabet.indexOf(input.charAt(i++) || "=");
enc6 = alphabet.indexOf(input.charAt(i++) || "=");
enc7 = alphabet.indexOf(input.charAt(i++) || "=");
enc8 = alphabet.indexOf(input.charAt(i++) || "=");
chr1 = (enc1 << 3) | (enc2 >> 2);
chr2 = ((enc2 & 3) << 6) | (enc3 << 1) | (enc4 >> 4);
chr3 = ((enc4 & 15) << 4) | (enc5 >> 1);
chr4 = ((enc5 & 1) << 7) | (enc6 << 2) | (enc7 >> 3);
chr5 = ((enc7 & 7) << 5) | enc8;
output.push(chr1);
if (enc2 & 3 !== 0 || enc3 !== 32) output.push(chr2);
if (enc4 & 15 !== 0 || enc5 !== 32) output.push(chr3);
if (enc5 & 1 !== 0 || enc6 !== 32) output.push(chr4);
if (enc7 & 7 !== 0 || enc8 !== 32) output.push(chr5);
}
return output;
}
}
export default FromBase32;

View File

@ -0,0 +1,82 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {fromBase64, ALPHABET_OPTIONS} from "../lib/Base64";
/**
* From Base64 operation
*/
class FromBase64 extends Operation {
/**
* FromBase64 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) {
const [alphabet, removeNonAlphChars] = args;
return 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,98 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {fromHex, HEX_DELIM_OPTIONS} from "../lib/Hex";
import Utils from "../Utils";
/**
* From Hex operation
*/
class FromHex extends Operation {
/**
* FromHex constructor
*/
constructor() {
super();
this.name = "From Hex";
this.module = "Default";
this.description = "Converts a hexadecimal byte string back into its raw value.<br><br>e.g. <code>ce 93 ce b5 ce b9 ce ac 20 cf 83 ce bf cf 85 0a</code> becomes the UTF-8 encoded string <code>Γειά σου</code>";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
name: "Delimiter",
type: "option",
value: HEX_DELIM_OPTIONS
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const delim = args[0] || "Space";
return fromHex(input, delim, 2);
}
/**
* Highlight to Hex
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
const delim = Utils.charRep(args[0] || "Space"),
len = delim === "\r\n" ? 1 : delim.length,
width = len + 2;
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
if (delim === "0x" || delim === "\\x") {
if (pos[0].start > 1) pos[0].start -= 2;
else pos[0].start = 0;
if (pos[0].end > 1) pos[0].end -= 2;
else pos[0].end = 0;
}
pos[0].start = pos[0].start === 0 ? 0 : Math.round(pos[0].start / width);
pos[0].end = pos[0].end === 0 ? 0 : Math.ceil(pos[0].end / width);
return pos;
}
/**
* Highlight from Hex
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
const delim = Utils.charRep(args[0] || "Space"),
len = delim === "\r\n" ? 1 : delim.length;
pos[0].start = pos[0].start * (2 + len);
pos[0].end = pos[0].end * (2 + len) - len;
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
if (delim === "0x" || delim === "\\x") {
pos[0].start += 2;
pos[0].end += 2;
}
return pos;
}
}
export default FromHex;

View File

@ -0,0 +1,46 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min";
const Zlib = zlibAndGzip.Zlib;
/**
* Gunzip operation
*/
class Gunzip extends Operation {
/**
* Gunzip constructor
*/
constructor() {
super();
this.name = "Gunzip";
this.module = "Compression";
this.description = "Decompresses data which has been compressed using the deflate algorithm with gzip headers.";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
// Deal with character encoding issues
input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
const gunzip = new Zlib.Gunzip(input);
return Array.prototype.slice.call(gunzip.decompress());
}
}
export default Gunzip;

View File

@ -0,0 +1,85 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib";
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min";
const Zlib = zlibAndGzip.Zlib;
/**
* Gzip operation
*/
class Gzip extends Operation {
/**
* Gzip constructor
*/
constructor() {
super();
this.name = "Gzip";
this.module = "Compression";
this.description = "Compresses data using the deflate algorithm with gzip headers.";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
name: "Compression type",
type: "option",
value: COMPRESSION_TYPE
},
{
name: "Filename (optional)",
type: "string",
value: ""
},
{
name: "Comment (optional)",
type: "string",
value: ""
},
{
name: "Include file checksum",
type: "boolean",
value: false
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const filename = args[1],
comment = args[2],
options = {
deflateOptions: {
compressionType: ZLIB_COMPRESSION_TYPE_LOOKUP[args[0]]
},
flags: {
fhcrc: args[3]
}
};
if (filename.length) {
options.flags.fname = true;
options.filename = filename;
}
if (comment.length) {
options.flags.fcommenct = true;
options.comment = comment;
}
const gzip = new Zlib.Gzip(input, options);
return Array.prototype.slice.call(gzip.compress());
}
}
export default Gzip;

View File

@ -0,0 +1,58 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {COMPRESSION_TYPE} from "../lib/Zlib";
import rawdeflate from "zlibjs/bin/rawdeflate.min";
const Zlib = rawdeflate.Zlib;
const RAW_COMPRESSION_TYPE_LOOKUP = {
"Fixed Huffman Coding": Zlib.RawDeflate.CompressionType.FIXED,
"Dynamic Huffman Coding": Zlib.RawDeflate.CompressionType.DYNAMIC,
"None (Store)": Zlib.RawDeflate.CompressionType.NONE,
};
/**
* Raw Deflate operation
*/
class RawDeflate extends Operation {
/**
* RawDeflate constructor
*/
constructor() {
super();
this.name = "Raw Deflate";
this.module = "Compression";
this.description = "Compresses data using the deflate algorithm with no headers.";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
name: "Compression type",
type: "option",
value: COMPRESSION_TYPE
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const deflate = new Zlib.RawDeflate(input, {
compressionType: RAW_COMPRESSION_TYPE_LOOKUP[args[0]]
});
return Array.prototype.slice.call(deflate.compress());
}
}
export default RawDeflate;

View File

@ -0,0 +1,105 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {INFLATE_BUFFER_TYPE} from "../lib/Zlib";
import rawinflate from "zlibjs/bin/rawinflate.min";
const Zlib = rawinflate.Zlib;
const RAW_BUFFER_TYPE_LOOKUP = {
"Adaptive": Zlib.RawInflate.BufferType.ADAPTIVE,
"Block": Zlib.RawInflate.BufferType.BLOCK,
};
/**
* Raw Inflate operation
*/
class RawInflate extends Operation {
/**
* RawInflate constructor
*/
constructor() {
super();
this.name = "Raw Inflate";
this.module = "Compression";
this.description = "Decompresses data which has been compressed using the deflate algorithm with no headers.";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
name: "Start index",
type: "number",
value: 0
},
{
name: "Initial output buffer size",
type: "number",
value: 0
},
{
name: "Buffer expansion type",
type: "option",
value: INFLATE_BUFFER_TYPE
},
{
name: "Resize buffer after decompression",
type: "boolean",
value: false
},
{
name: "Verify result",
type: "boolean",
value: false
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
// Deal with character encoding issues
input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
const inflate = new Zlib.RawInflate(input, {
index: args[0],
bufferSize: args[1],
bufferType: RAW_BUFFER_TYPE_LOOKUP[args[2]],
resize: args[3],
verify: args[4]
}),
result = Array.prototype.slice.call(inflate.decompress());
// Raw Inflate somethimes messes up and returns nonsense like this:
// ]....]....]....]....]....]....]....]....]....]....]....]....]....]...
// e.g. Input data of [8b, 1d, dc, 44]
// Look for the first two square brackets:
if (result.length > 158 && result[0] === 93 && result[5] === 93) {
// If the first two square brackets are there, check that the others
// are also there. If they are, throw an error. If not, continue.
let valid = false;
for (let i = 0; i < 155; i += 5) {
if (result[i] !== 93) {
valid = true;
}
}
if (!valid) {
throw "Error: Unable to inflate data";
}
}
// This seems to be the easiest way...
return result;
}
}
export default RawInflate;

View File

@ -0,0 +1,162 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {fromBase64, toBase64} from "../lib/Base64";
/**
* Show Base64 offsets operation
*/
class ShowBase64Offsets extends Operation {
/**
* ShowBase64Offsets constructor
*/
constructor() {
super();
this.name = "Show Base64 offsets";
this.module = "Default";
this.description = "When a string is within a block of data and the whole block is Base64'd, the string itself could be represented in Base64 in three distinct ways depending on its offset within the block.<br><br>This operation shows all possible offsets for a given string so that each possible encoding can be considered.";
this.inputType = "byteArray";
this.outputType = "html";
this.args = [
{
name: "Alphabet",
type: "binaryString",
value: "A-Za-z0-9+/="
},
{
name: "Show variable chars and padding",
type: "boolean",
value: true
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {html}
*/
run(input, args) {
const [alphabet, showVariable] = args;
let offset0 = toBase64(input, alphabet),
offset1 = toBase64([0].concat(input), alphabet),
offset2 = toBase64([0, 0].concat(input), alphabet),
staticSection = "",
padding = "";
const len0 = offset0.indexOf("="),
len1 = offset1.indexOf("="),
len2 = offset2.indexOf("="),
script = "<script type='application/javascript'>$('[data-toggle=\"tooltip\"]').tooltip()</script>";
if (input.length < 1) {
return "Please enter a string.";
}
// Highlight offset 0
if (len0 % 4 === 2) {
staticSection = offset0.slice(0, -3);
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(fromBase64(staticSection, alphabet).slice(0, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset0.substr(offset0.length - 3, 1) + "</span>" +
"<span class='hl3'>" + offset0.substr(offset0.length - 2) + "</span>";
} else if (len0 % 4 === 3) {
staticSection = offset0.slice(0, -2);
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(fromBase64(staticSection, alphabet).slice(0, -1)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset0.substr(offset0.length - 2, 1) + "</span>" +
"<span class='hl3'>" + offset0.substr(offset0.length - 1) + "</span>";
} else {
staticSection = offset0;
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(fromBase64(staticSection, alphabet)) + "'>" +
staticSection + "</span>";
}
if (!showVariable) {
offset0 = staticSection;
}
// Highlight offset 1
padding = "<span class='hl3'>" + offset1.substr(0, 1) + "</span>" +
"<span class='hl5'>" + offset1.substr(1, 1) + "</span>";
offset1 = offset1.substr(2);
if (len1 % 4 === 2) {
staticSection = offset1.slice(0, -3);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(fromBase64("AA" + staticSection, alphabet).slice(1, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset1.substr(offset1.length - 3, 1) + "</span>" +
"<span class='hl3'>" + offset1.substr(offset1.length - 2) + "</span>";
} else if (len1 % 4 === 3) {
staticSection = offset1.slice(0, -2);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(fromBase64("AA" + staticSection, alphabet).slice(1, -1)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset1.substr(offset1.length - 2, 1) + "</span>" +
"<span class='hl3'>" + offset1.substr(offset1.length - 1) + "</span>";
} else {
staticSection = offset1;
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(fromBase64("AA" + staticSection, alphabet).slice(1)) + "'>" +
staticSection + "</span>";
}
if (!showVariable) {
offset1 = staticSection;
}
// Highlight offset 2
padding = "<span class='hl3'>" + offset2.substr(0, 2) + "</span>" +
"<span class='hl5'>" + offset2.substr(2, 1) + "</span>";
offset2 = offset2.substr(3);
if (len2 % 4 === 2) {
staticSection = offset2.slice(0, -3);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset2.substr(offset2.length - 3, 1) + "</span>" +
"<span class='hl3'>" + offset2.substr(offset2.length - 2) + "</span>";
} else if (len2 % 4 === 3) {
staticSection = offset2.slice(0, -2);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset2.substr(offset2.length - 2, 1) + "</span>" +
"<span class='hl3'>" + offset2.substr(offset2.length - 1) + "</span>";
} else {
staticSection = offset2;
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(fromBase64("AAA" + staticSection, alphabet).slice(2)) + "'>" +
staticSection + "</span>";
}
if (!showVariable) {
offset2 = staticSection;
}
return (showVariable ? "Characters highlighted in <span class='hl5'>green</span> could change if the input is surrounded by more data." +
"\nCharacters highlighted in <span class='hl3'>red</span> are for padding purposes only." +
"\nUnhighlighted characters are <span data-toggle='tooltip' data-placement='top' title='Tooltip on left'>static</span>." +
"\nHover over the static sections to see what they decode to on their own.\n" +
"\nOffset 0: " + offset0 +
"\nOffset 1: " + offset1 +
"\nOffset 2: " + offset2 +
script :
offset0 + "\n" + offset1 + "\n" + offset2);
}
}
export default ShowBase64Offsets;

View File

@ -0,0 +1,85 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* To Base32 operation
*/
class ToBase32 extends Operation {
/**
* ToBase32 constructor
*/
constructor() {
super();
this.name = "To Base32";
this.module = "Default";
this.description = "Base32 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. It uses a smaller set of characters than Base64, usually the uppercase alphabet and the numbers 2 to 7.";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [
{
name: "Alphabet",
type: "binaryString",
value: "A-Z2-7="
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!input) return "";
const alphabet = args[0] ? Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=";
let output = "",
chr1, chr2, chr3, chr4, chr5,
enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
i = 0;
while (i < input.length) {
chr1 = input[i++];
chr2 = input[i++];
chr3 = input[i++];
chr4 = input[i++];
chr5 = input[i++];
enc1 = chr1 >> 3;
enc2 = ((chr1 & 7) << 2) | (chr2 >> 6);
enc3 = (chr2 >> 1) & 31;
enc4 = ((chr2 & 1) << 4) | (chr3 >> 4);
enc5 = ((chr3 & 15) << 1) | (chr4 >> 7);
enc6 = (chr4 >> 2) & 31;
enc7 = ((chr4 & 3) << 3) | (chr5 >> 5);
enc8 = chr5 & 31;
if (isNaN(chr2)) {
enc3 = enc4 = enc5 = enc6 = enc7 = enc8 = 32;
} else if (isNaN(chr3)) {
enc5 = enc6 = enc7 = enc8 = 32;
} else if (isNaN(chr4)) {
enc6 = enc7 = enc8 = 32;
} else if (isNaN(chr5)) {
enc8 = 32;
}
output += alphabet.charAt(enc1) + alphabet.charAt(enc2) + alphabet.charAt(enc3) +
alphabet.charAt(enc4) + alphabet.charAt(enc5) + alphabet.charAt(enc6) +
alphabet.charAt(enc7) + alphabet.charAt(enc8);
}
return output;
}
}
export default ToBase32;

View File

@ -0,0 +1,76 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {toBase64, 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];
return 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,98 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {toHex, HEX_DELIM_OPTIONS} from "../lib/Hex";
import Utils from "../Utils";
/**
* To Hex operation
*/
class ToHex extends Operation {
/**
* ToHex constructor
*/
constructor() {
super();
this.name = "To Hex";
this.module = "Default";
this.description = "Converts the input string to hexadecimal bytes separated by the specified delimiter.<br><br>e.g. The UTF-8 encoded string <code>Γειά σου</code> becomes <code>ce 93 ce b5 ce b9 ce ac 20 cf 83 ce bf cf 85 0a</code>";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
name: "Delimiter",
type: "option",
value: HEX_DELIM_OPTIONS
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const delim = Utils.charRep(args[0] || "Space");
return toHex(new Uint8Array(input), delim, 2);
}
/**
* Highlight to Hex
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
const delim = Utils.charRep(args[0] || "Space"),
len = delim === "\r\n" ? 1 : delim.length;
pos[0].start = pos[0].start * (2 + len);
pos[0].end = pos[0].end * (2 + len) - len;
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
if (delim === "0x" || delim === "\\x") {
pos[0].start += 2;
pos[0].end += 2;
}
return pos;
}
/**
* Highlight from Hex
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
const delim = Utils.charRep(args[0] || "Space"),
len = delim === "\r\n" ? 1 : delim.length,
width = len + 2;
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
if (delim === "0x" || delim === "\\x") {
if (pos[0].start > 1) pos[0].start -= 2;
else pos[0].start = 0;
if (pos[0].end > 1) pos[0].end -= 2;
else pos[0].end = 0;
}
pos[0].start = pos[0].start === 0 ? 0 : Math.round(pos[0].start / width);
pos[0].end = pos[0].end === 0 ? 0 : Math.ceil(pos[0].end / width);
return pos;
}
}
export default ToHex;

View File

@ -0,0 +1,80 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import unzip from "zlibjs/bin/unzip.min";
const Zlib = unzip.Zlib;
/**
* Unzip operation
*/
class Unzip extends Operation {
/**
* Unzip constructor
*/
constructor() {
super();
this.name = "Unzip";
this.module = "Compression";
this.description = "Decompresses data using the PKZIP algorithm and displays it per file, with support for passwords.";
this.inputType = "byteArray";
this.outputType = "html";
this.args = [
{
name: "Password",
type: "binaryString",
value: ""
},
{
name: "Verify result",
type: "boolean",
value: false
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {html}
*/
run(input, args) {
const options = {
password: Utils.strToByteArray(args[0]),
verify: args[1]
},
unzip = new Zlib.Unzip(input, options),
filenames = unzip.getFilenames(),
files = [];
filenames.forEach(function(fileName) {
const bytes = unzip.decompress(fileName);
const contents = Utils.byteArrayToUtf8(bytes);
const file = {
fileName: fileName,
size: contents.length,
};
const isDir = contents.length === 0 && fileName.endsWith("/");
if (!isDir) {
file.bytes = bytes;
file.contents = contents;
}
files.push(file);
});
return Utils.displayFilesAsHTML(files);
}
}
export default Unzip;

101
src/core/operations/Zip.mjs Normal file
View File

@ -0,0 +1,101 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib";
import zip from "zlibjs/bin/zip.min";
const Zlib = zip.Zlib;
const ZIP_COMPRESSION_METHOD_LOOKUP = {
"Deflate": Zlib.Zip.CompressionMethod.DEFLATE,
"None (Store)": Zlib.Zip.CompressionMethod.STORE
};
const ZIP_OS_LOOKUP = {
"MSDOS": Zlib.Zip.OperatingSystem.MSDOS,
"Unix": Zlib.Zip.OperatingSystem.UNIX,
"Macintosh": Zlib.Zip.OperatingSystem.MACINTOSH
};
/**
* Zip operation
*/
class Zip extends Operation {
/**
* Zip constructor
*/
constructor() {
super();
this.name = "Zip";
this.module = "Compression";
this.description = "Compresses data using the PKZIP algorithm with the given filename.<br><br>No support for multiple files at this time.";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
name: "Filename",
type: "string",
value: "file.txt"
},
{
name: "Comment",
type: "string",
value: ""
},
{
name: "Password",
type: "binaryString",
value: ""
},
{
name: "Compression method",
type: "option",
value: ["Deflate", "None (Store)"]
},
{
name: "Operating system",
type: "option",
value: ["MSDOS", "Unix", "Macintosh"]
},
{
name: "Compression type",
type: "option",
value: COMPRESSION_TYPE
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const password = Utils.strToByteArray(args[2]),
options = {
filename: Utils.strToByteArray(args[0]),
comment: Utils.strToByteArray(args[1]),
compressionMethod: ZIP_COMPRESSION_METHOD_LOOKUP[args[3]],
os: ZIP_OS_LOOKUP[args[4]],
deflateOption: {
compressionType: ZLIB_COMPRESSION_TYPE_LOOKUP[args[5]]
},
},
zip = new Zlib.Zip();
if (password.length)
zip.setPassword(password);
zip.addFile(input, options);
return Array.prototype.slice.call(zip.compress());
}
}
export default Zip;

View File

@ -0,0 +1,52 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib";
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min";
const Zlib = zlibAndGzip.Zlib;
/**
* Zlib Deflate operation
*/
class ZlibDeflate extends Operation {
/**
* ZlibDeflate constructor
*/
constructor() {
super();
this.name = "Zlib Deflate";
this.module = "Compression";
this.description = "Compresses data using the deflate algorithm adding zlib headers.";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
name: "Compression type",
type: "option",
value: COMPRESSION_TYPE
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const deflate = new Zlib.Deflate(input, {
compressionType: ZLIB_COMPRESSION_TYPE_LOOKUP[args[0]]
});
return Array.prototype.slice.call(deflate.compress());
}
}
export default ZlibDeflate;

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 {INFLATE_BUFFER_TYPE} from "../lib/Zlib";
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min";
const Zlib = zlibAndGzip.Zlib;
const ZLIB_BUFFER_TYPE_LOOKUP = {
"Adaptive": Zlib.Inflate.BufferType.ADAPTIVE,
"Block": Zlib.Inflate.BufferType.BLOCK,
};
/**
* Zlib Inflate operation
*/
class ZlibInflate extends Operation {
/**
* ZlibInflate constructor
*/
constructor() {
super();
this.name = "Zlib Inflate";
this.module = "Compression";
this.description = "Decompresses data which has been compressed using the deflate algorithm with zlib headers.";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
name: "Start index",
type: "number",
value: 0
},
{
name: "Initial output buffer size",
type: "number",
value: 0
},
{
name: "Buffer expansion type",
type: "option",
value: INFLATE_BUFFER_TYPE
},
{
name: "Resize buffer after decompression",
type: "boolean",
value: false
},
{
name: "Verify result",
type: "boolean",
value: false
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
// Deal with character encoding issues
input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
const inflate = new Zlib.Inflate(input, {
index: args[0],
bufferSize: args[1],
bufferType: ZLIB_BUFFER_TYPE_LOOKUP[args[2]],
resize: args[3],
verify: args[4]
});
return Array.prototype.slice.call(inflate.decompress());
}
}
export default ZlibInflate;

View File

@ -0,0 +1,40 @@
/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/scripts/generateOpsIndex.mjs
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import FromBase32 from "./FromBase32";
import FromBase64 from "./FromBase64";
import FromHex from "./FromHex";
import Gunzip from "./Gunzip";
import Gzip from "./Gzip";
import RawDeflate from "./RawDeflate";
import RawInflate from "./RawInflate";
import ShowBase64Offsets from "./ShowBase64Offsets";
import ToBase32 from "./ToBase32";
import ToBase64 from "./ToBase64";
import ToHex from "./ToHex";
import Unzip from "./Unzip";
import Zip from "./Zip";
import ZlibDeflate from "./ZlibDeflate";
import ZlibInflate from "./ZlibInflate";
export {
FromBase32,
FromBase64,
FromHex,
Gunzip,
Gzip,
RawDeflate,
RawInflate,
ShowBase64Offsets,
ToBase32,
ToBase64,
ToHex,
Unzip,
Zip,
ZlibDeflate,
ZlibInflate,
};

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

@ -0,0 +1,59 @@
import bsonjs from "bson";
import {Buffer} from "buffer";
/**
* BSON operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*
* @namespace
*/
const BSON = {
/**
* BSON serialise operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
runBSONSerialise(input, args) {
if (!input) return new ArrayBuffer();
const bson = new bsonjs();
try {
const data = JSON.parse(input);
return bson.serialize(data).buffer;
} catch (err) {
return err.toString();
}
},
/**
* BSON deserialise operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*
*/
runBSONDeserialise(input, args) {
if (!input.byteLength) return "";
const bson = new bsonjs();
try {
const data = bson.deserialize(new Buffer(input));
return JSON.stringify(data, null, 2);
} catch (err) {
return err.toString();
}
},
};
export default BSON;

View File

@ -1,4 +1,5 @@
import Utils from "../Utils.js";
import {toHex} from "../lib/Hex";
/**
@ -166,7 +167,7 @@ const BitwiseOp = {
if (crib && resultUtf8.toLowerCase().indexOf(crib) < 0) continue;
if (printKey) record += "Key = " + Utils.hex(key, (2*keyLength)) + ": ";
if (outputHex) {
record += Utils.toHex(result);
record += toHex(result);
} else {
record += Utils.printable(resultUtf8, false);
}

View File

@ -1,4 +1,5 @@
import Utils from "../Utils.js";
import {toHex, fromHex} from "../lib/Hex";
/**
@ -17,42 +18,12 @@ const ByteRepr = {
* @default
*/
DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF"],
/**
* @constant
* @default
*/
HEX_DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"],
/**
* @constant
* @default
*/
BIN_DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "None"],
/**
* To Hex operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runToHex: function(input, args) {
const delim = Utils.charRep[args[0] || "Space"];
return Utils.toHex(new Uint8Array(input), delim, 2);
},
/**
* From Hex operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromHex: function(input, args) {
const delim = args[0] || "Space";
return Utils.fromHex(input, delim, 2);
},
/**
* To Octal operation.
@ -63,7 +34,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 +48,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 +68,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 +110,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;
@ -148,6 +119,10 @@ const ByteRepr = {
throw "Error: Base argument must be between 2 and 36";
}
if (input.length === 0) {
return [];
}
if (base !== 16 && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
// Split into groups of 2 if the whole string is concatenated and
@ -167,59 +142,6 @@ const ByteRepr = {
},
/**
* Highlight to hex
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightTo: function(pos, args) {
let delim = Utils.charRep[args[0] || "Space"],
len = delim === "\r\n" ? 1 : delim.length;
pos[0].start = pos[0].start * (2 + len);
pos[0].end = pos[0].end * (2 + len) - len;
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
if (delim === "0x" || delim === "\\x") {
pos[0].start += 2;
pos[0].end += 2;
}
return pos;
},
/**
* Highlight from hex
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightFrom: function(pos, args) {
let delim = Utils.charRep[args[0] || "Space"],
len = delim === "\r\n" ? 1 : delim.length,
width = len + 2;
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
if (delim === "0x" || delim === "\\x") {
if (pos[0].start > 1) pos[0].start -= 2;
else pos[0].start = 0;
if (pos[0].end > 1) pos[0].end -= 2;
else pos[0].end = 0;
}
pos[0].start = pos[0].start === 0 ? 0 : Math.round(pos[0].start / width);
pos[0].end = pos[0].end === 0 ? 0 : Math.ceil(pos[0].end / width);
return pos;
},
/**
* To Decimal operation.
*
@ -228,7 +150,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);
},
@ -241,7 +163,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);
@ -261,7 +183,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;
@ -285,7 +207,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 = [];
@ -307,7 +229,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;
@ -324,7 +246,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;
@ -353,7 +275,7 @@ const ByteRepr = {
const convert = args[0];
const spaces = args[1];
if (convert === "All chars") {
let result = "|" + Utils.toHex(input) + "|";
let result = "|" + toHex(input) + "|";
if (!spaces) result = result.replace(/ /g, "");
return result;
}
@ -369,7 +291,7 @@ const ByteRepr = {
output += "|";
inHex = true;
} else if (spaces) output += " ";
output += Utils.toHex([b]);
output += toHex([b]);
} else {
if (inHex) {
output += "|";
@ -399,7 +321,7 @@ const ByteRepr = {
output.push(Utils.ord(input[i++]));
// Add match
const bytes = Utils.fromHex(m[1]);
const bytes = fromHex(m[1]);
if (bytes) {
for (let a = 0; a < bytes.length;)
output.push(bytes[a++]);

View File

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

View File

@ -1,4 +1,6 @@
import Utils from "../Utils.js";
import {toBase64} from "../lib/Base64";
import {toHexFast} from "../lib/Hex";
import CryptoJS from "crypto-js";
import forge from "imports-loader?jQuery=>null!node-forge/dist/forge.min.js";
import {blowfish as Blowfish} from "sladex-blowfish";
@ -366,7 +368,7 @@ DES uses a key length of 8 bytes (64 bits).`;
input = Utils.convertToByteString(input, inputType);
Blowfish.setIV(Utils.toBase64(iv), 0);
Blowfish.setIV(toBase64(iv), 0);
const enc = Blowfish.encrypt(input, key, {
outputType: Cipher._BLOWFISH_OUTPUT_TYPE_LOOKUP[outputType],
@ -395,14 +397,14 @@ DES uses a key length of 8 bytes (64 bits).`;
input = inputType === "Raw" ? Utils.strToByteArray(input) : input;
Blowfish.setIV(Utils.toBase64(iv), 0);
Blowfish.setIV(toBase64(iv), 0);
const result = Blowfish.decrypt(input, key, {
outputType: Cipher._BLOWFISH_OUTPUT_TYPE_LOOKUP[inputType], // This actually means inputType. The library is weird.
cipherMode: Cipher._BLOWFISH_MODE_LOOKUP[mode]
});
return outputType === "Hex" ? Utils.toHexFast(Utils.strToByteArray(result)) : result;
return outputType === "Hex" ? toHexFast(Utils.strToByteArray(result)) : result;
},

View File

@ -483,12 +483,11 @@ const Code = {
/**
* Converts to snake_case.
* To Snake Case operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
*/
runToSnakeCase(input, args) {
const smart = args[0];
@ -502,12 +501,11 @@ const Code = {
/**
* Converts to camelCase.
* To Camel Case operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
*/
runToCamelCase(input, args) {
const smart = args[0];
@ -521,12 +519,11 @@ const Code = {
/**
* Converts to kebab-case.
* To Kebab Case operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
*/
runToKebabCase(input, args) {
const smart = args[0];
@ -537,6 +534,7 @@ const Code = {
return kebabCase(input);
}
},
};
export default Code;

View File

@ -0,0 +1,243 @@
import Utils from "../Utils.js";
import bzip2 from "exports-loader?bzip2!../vendor/bzip2.js";
/**
* Compression operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Compress = {
/**
* Bzip2 Decompress operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runBzip2Decompress: function(input, args) {
let compressed = new Uint8Array(input),
bzip2Reader,
plain = "";
bzip2Reader = bzip2.array(compressed);
plain = bzip2.simple(bzip2Reader);
return plain;
},
/**
* @constant
* @default
*/
TAR_FILENAME: "file.txt",
/**
* Tar pack operation.
*
* @author tlwr [toby@toby.codes]
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runTar: function(input, args) {
const Tarball = function() {
this.bytes = new Array(512);
this.position = 0;
};
Tarball.prototype.addEmptyBlock = function() {
const filler = new Array(512);
filler.fill(0);
this.bytes = this.bytes.concat(filler);
};
Tarball.prototype.writeBytes = function(bytes) {
const self = this;
if (this.position + bytes.length > this.bytes.length) {
this.addEmptyBlock();
}
Array.prototype.forEach.call(bytes, function(b, i) {
if (typeof b.charCodeAt !== "undefined") {
b = b.charCodeAt();
}
self.bytes[self.position] = b;
self.position += 1;
});
};
Tarball.prototype.writeEndBlocks = function() {
const numEmptyBlocks = 2;
for (let i = 0; i < numEmptyBlocks; i++) {
this.addEmptyBlock();
}
};
const fileSize = input.length.toString(8).padStart(11, "0");
const currentUnixTimestamp = Math.floor(Date.now() / 1000);
const lastModTime = currentUnixTimestamp.toString(8).padStart(11, "0");
const file = {
fileName: Utils.padBytesRight(args[0], 100),
fileMode: Utils.padBytesRight("0000664", 8),
ownerUID: Utils.padBytesRight("0", 8),
ownerGID: Utils.padBytesRight("0", 8),
size: Utils.padBytesRight(fileSize, 12),
lastModTime: Utils.padBytesRight(lastModTime, 12),
checksum: " ",
type: "0",
linkedFileName: Utils.padBytesRight("", 100),
USTARFormat: Utils.padBytesRight("ustar", 6),
version: "00",
ownerUserName: Utils.padBytesRight("", 32),
ownerGroupName: Utils.padBytesRight("", 32),
deviceMajor: Utils.padBytesRight("", 8),
deviceMinor: Utils.padBytesRight("", 8),
fileNamePrefix: Utils.padBytesRight("", 155),
};
let checksum = 0;
for (const key in file) {
const bytes = file[key];
Array.prototype.forEach.call(bytes, function(b) {
if (typeof b.charCodeAt !== "undefined") {
checksum += b.charCodeAt();
} else {
checksum += b;
}
});
}
checksum = Utils.padBytesRight(checksum.toString(8).padStart(7, "0"), 8);
file.checksum = checksum;
const tarball = new Tarball();
tarball.writeBytes(file.fileName);
tarball.writeBytes(file.fileMode);
tarball.writeBytes(file.ownerUID);
tarball.writeBytes(file.ownerGID);
tarball.writeBytes(file.size);
tarball.writeBytes(file.lastModTime);
tarball.writeBytes(file.checksum);
tarball.writeBytes(file.type);
tarball.writeBytes(file.linkedFileName);
tarball.writeBytes(file.USTARFormat);
tarball.writeBytes(file.version);
tarball.writeBytes(file.ownerUserName);
tarball.writeBytes(file.ownerGroupName);
tarball.writeBytes(file.deviceMajor);
tarball.writeBytes(file.deviceMinor);
tarball.writeBytes(file.fileNamePrefix);
tarball.writeBytes(Utils.padBytesRight("", 12));
tarball.writeBytes(input);
tarball.writeEndBlocks();
return tarball.bytes;
},
/**
* Untar unpack operation.
*
* @author tlwr [toby@toby.codes]
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {html}
*/
runUntar: function(input, args) {
const Stream = function(input) {
this.bytes = input;
this.position = 0;
};
Stream.prototype.getBytes = function(bytesToGet) {
const newPosition = this.position + bytesToGet;
const bytes = this.bytes.slice(this.position, newPosition);
this.position = newPosition;
return bytes;
};
Stream.prototype.readString = function(numBytes) {
let result = "";
for (let i = this.position; i < this.position + numBytes; i++) {
const currentByte = this.bytes[i];
if (currentByte === 0) break;
result += String.fromCharCode(currentByte);
}
this.position += numBytes;
return result;
};
Stream.prototype.readInt = function(numBytes, base) {
const string = this.readString(numBytes);
return parseInt(string, base);
};
Stream.prototype.hasMore = function() {
return this.position < this.bytes.length;
};
let stream = new Stream(input),
files = [];
while (stream.hasMore()) {
const dataPosition = stream.position + 512;
const file = {
fileName: stream.readString(100),
fileMode: stream.readString(8),
ownerUID: stream.readString(8),
ownerGID: stream.readString(8),
size: parseInt(stream.readString(12), 8), // Octal
lastModTime: new Date(1000 * stream.readInt(12, 8)), // Octal
checksum: stream.readString(8),
type: stream.readString(1),
linkedFileName: stream.readString(100),
USTARFormat: stream.readString(6).indexOf("ustar") >= 0,
};
if (file.USTARFormat) {
file.version = stream.readString(2);
file.ownerUserName = stream.readString(32);
file.ownerGroupName = stream.readString(32);
file.deviceMajor = stream.readString(8);
file.deviceMinor = stream.readString(8);
file.filenamePrefix = stream.readString(155);
}
stream.position = dataPosition;
if (file.type === "0") {
// File
files.push(file);
let endPosition = stream.position + file.size;
if (file.size % 512 !== 0) {
endPosition += 512 - (file.size % 512);
}
file.bytes = stream.getBytes(file.size);
file.contents = Utils.byteArrayToUtf8(file.bytes);
stream.position = endPosition;
} else if (file.type === "5") {
// Directory
files.push(file);
} else {
// Symlink or empty bytes
}
}
return Utils.displayFilesAsHTML(files);
},
};
export default Compress;

View File

@ -1,3 +1,6 @@
import moment from "moment-timezone";
/**
* Date and time operations.
*
@ -57,24 +60,29 @@ const DateTime = {
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
* @returns {string}
*/
runToUnixTimestamp: function(input, args) {
let units = args[0],
const units = args[0],
treatAsUTC = args[1],
showDateTime = args[2],
d = treatAsUTC ? moment.utc(input) : moment(input);
let result = "";
if (units === "Seconds (s)") {
return d.unix();
result = d.unix();
} else if (units === "Milliseconds (ms)") {
return d.valueOf();
result = d.valueOf();
} else if (units === "Microseconds (μs)") {
return d.valueOf() * 1000;
result = d.valueOf() * 1000;
} else if (units === "Nanoseconds (ns)") {
return d.valueOf() * 1000000;
result = d.valueOf() * 1000000;
} else {
throw "Unrecognised unit";
}
return showDateTime ? `${result} (${d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss")} UTC)` : result.toString();
},

View File

@ -1,4 +1,5 @@
import Utils from "../Utils.js";
import {toHex, fromHex} from "../lib/Hex";
/**
@ -52,7 +53,7 @@ const Endian = {
// Convert input to raw data based on specified data format
switch (dataFormat) {
case "Hex":
data = Utils.fromHex(input);
data = fromHex(input);
break;
case "Raw":
data = Utils.strToByteArray(input);
@ -86,7 +87,7 @@ const Endian = {
// Convert data back to specified data format
switch (dataFormat) {
case "Hex":
return Utils.toHex(result);
return toHex(result);
case "Raw":
return Utils.byteArrayToUtf8(result);
default:

View File

@ -5,6 +5,8 @@ import * as SHA3 from "js-sha3";
import Checksum from "./Checksum.js";
import ctph from "ctph.js";
import ssdeep from "ssdeep.js";
import bcrypt from "bcryptjs";
import scrypt from "scryptsy";
/**
@ -376,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]);
},
@ -390,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]);
},
@ -449,6 +451,127 @@ const Hash = {
},
/**
* @constant
* @default
*/
BCRYPT_ROUNDS: 10,
/**
* Bcrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runBcrypt: async function (input, args) {
const rounds = args[0];
const salt = await bcrypt.genSalt(rounds);
return await bcrypt.hash(input, salt, null, p => {
// Progress callback
if (ENVIRONMENT_IS_WORKER())
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
});
},
/**
* Bcrypt compare operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runBcryptCompare: async function (input, args) {
const hash = args[0];
const match = await bcrypt.compare(input, hash, null, p => {
// Progress callback
if (ENVIRONMENT_IS_WORKER())
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
});
return match ? "Match: " + input : "No match";
},
/**
* Bcrypt parse operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runBcryptParse: async function (input, args) {
try {
return `Rounds: ${bcrypt.getRounds(input)}
Salt: ${bcrypt.getSalt(input)}
Password hash: ${input.split(bcrypt.getSalt(input))[1]}
Full hash: ${input}`;
} catch (err) {
return "Error: " + err.toString();
}
},
/**
* @constant
* @default
*/
KEY_FORMAT: ["Hex", "Base64", "UTF8", "Latin1"],
/**
* @constant
* @default
*/
SCRYPT_ITERATIONS: 16384,
/**
* @constant
* @default
*/
SCRYPT_MEM_FACTOR: 8,
/**
* @constant
* @default
*/
SCRYPT_PARALLEL_FACTOR: 1,
/**
* @constant
* @default
*/
SCRYPT_KEY_LENGTH: 64,
/**
* Scrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runScrypt: function (input, args) {
const salt = Utils.convertToByteString(args[0].string || "", args[0].option),
iterations = args[1],
memFactor = args[2],
parallelFactor = args[3],
keyLength = args[4];
try {
const data = scrypt(
input, salt, iterations, memFactor, parallelFactor, keyLength,
p => {
// Progress callback
if (ENVIRONMENT_IS_WORKER())
self.sendStatusMessage(`Progress: ${p.percent.toFixed(0)}%`);
}
);
return data.toString("hex");
} catch (err) {
return "Error: " + err.toString();
}
},
/**
* Generate all hashes operation.
*

View File

@ -1,4 +1,5 @@
import Utils from "../Utils.js";
import {fromHex} from "../lib/Hex";
/**
@ -78,11 +79,11 @@ const Hexdump = {
*/
runFrom: function(input, args) {
let output = [],
regex = /^\s*(?:[\dA-F]{4,16}:?)?\s*((?:[\dA-F]{2}\s){1,8}(?:\s|[\dA-F]{2}-)(?:[\dA-F]{2}\s){1,8}|(?:[\dA-F]{2}\s|[\dA-F]{4}\s)+)/igm,
regex = /^\s*(?:[\dA-F]{4,16}h?:?)?\s*((?:[\dA-F]{2}\s){1,8}(?:\s|[\dA-F]{2}-)(?:[\dA-F]{2}\s){1,8}|(?:[\dA-F]{2}\s|[\dA-F]{4}\s)+)/igm,
block, line;
while ((block = regex.exec(input))) {
line = Utils.fromHex(block[1].replace(/-/g, " "));
line = fromHex(block[1].replace(/-/g, " "));
for (let i = 0; i < line.length; i++) {
output.push(line[i]);
}

View File

@ -1,4 +1,5 @@
import Utils from "../Utils.js";
import {toHex, fromHex} from "../lib/Hex";
import Checksum from "./Checksum.js";
import {BigInteger} from "jsbn";
@ -283,7 +284,7 @@ const IP = {
baIp.push(decimal & 255);
break;
case "Hex":
baIp = Utils.fromHex(lines[i]);
baIp = fromHex(lines[i]);
break;
default:
throw "Unsupported input IP format";
@ -346,7 +347,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,
@ -445,7 +446,7 @@ const IP = {
output;
if (format === "Hex") {
input = Utils.fromHex(input);
input = fromHex(input);
} else if (format === "Raw") {
input = Utils.strToByteArray(input);
} else {
@ -516,7 +517,7 @@ const IP = {
"<tr><td>Destination IP address</td><td>" + IP._ipv4ToStr(dstIP) + "</td></tr>";
if (ihl > 5) {
output += "<tr><td>Options</td><td>" + Utils.toHex(options) + "</td></tr>";
output += "<tr><td>Options</td><td>" + toHex(options) + "</td></tr>";
}
return output + "</table>";

View File

@ -1,7 +1,9 @@
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";
import {fromBase64, toBase64} from "../lib/Base64";
import {fromHex} from "../lib/Hex";
/**
@ -91,12 +93,12 @@ const Image = {
// Convert input to raw bytes
switch (inputFormat) {
case "Hex":
input = Utils.fromHex(input);
input = fromHex(input);
break;
case "Base64":
// Don't trust the Base64 entered by the user.
// Unwrap it first, then re-encode later.
input = Utils.fromBase64(input, null, "byteArray");
input = fromBase64(input, null, "byteArray");
break;
case "Raw":
default:
@ -113,7 +115,7 @@ const Image = {
}
// Add image data to URI
dataURI += "base64," + Utils.toBase64(input);
dataURI += "base64," + toBase64(input);
return "<img src='" + dataURI + "'>";
},

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>");

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