Merged ESM into feature-bootstrap4

This commit is contained in:
n1474335 2018-06-09 10:48:35 +01:00
commit 27b0505ede
96 changed files with 3814 additions and 15668 deletions

View File

@ -1,2 +1 @@
src/core/vendor/**
src/core/operations/legacy/**

136
package-lock.json generated
View File

@ -2191,9 +2191,10 @@
}
},
"colors": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz",
"integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q="
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.3.0.tgz",
"integrity": "sha512-EDpX3a7wHMWFA7PUHWPHNWqOxIIRSJetuwl0AS5Oi/5FMV8kWm69RTlgm00GKjBO1xFHMtBbL49yRtMMdticBw==",
"dev": true
},
"combined-stream": {
"version": "1.0.6",
@ -2647,6 +2648,12 @@
"array-find-index": "^1.0.1"
}
},
"cycle": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
"integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=",
"dev": true
},
"cyclist": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
@ -3911,6 +3918,12 @@
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
"dev": true
},
"eyes": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
"integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=",
"dev": true
},
"fast-deep-equal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
@ -5671,6 +5684,12 @@
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
"dev": true
},
"i": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz",
"integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=",
"dev": true
},
"iced-error": {
"version": "0.0.12",
"resolved": "https://registry.npmjs.org/iced-error/-/iced-error-0.0.12.tgz",
@ -7408,6 +7427,12 @@
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
"ncp": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz",
"integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=",
"dev": true
},
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
@ -7694,7 +7719,6 @@
"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.5.2.tgz",
"integrity": "sha1-9DRUSKhTz71cDSYyDyR3qwUm/i8=",
"requires": {
"colors": "0.5.x",
"underscore": "1.1.x"
},
"dependencies": {
@ -8293,6 +8317,12 @@
}
}
},
"pkginfo": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz",
"integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=",
"dev": true
},
"pluralize": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
@ -9152,6 +9182,20 @@
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"dev": true
},
"prompt": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prompt/-/prompt-1.0.0.tgz",
"integrity": "sha1-jlcSPDlquYiJf7Mn/Trtw+c15P4=",
"dev": true,
"requires": {
"colors": "^1.1.2",
"pkginfo": "0.x.x",
"read": "1.0.x",
"revalidator": "0.1.x",
"utile": "0.3.x",
"winston": "2.1.x"
}
},
"proxy-addr": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz",
@ -9314,6 +9358,15 @@
"strip-json-comments": "~2.0.1"
}
},
"read": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
"integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
"dev": true,
"requires": {
"mute-stream": "~0.0.4"
}
},
"read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -9803,6 +9856,12 @@
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
"dev": true
},
"revalidator": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz",
"integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=",
"dev": true
},
"rimraf": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
@ -10726,6 +10785,12 @@
"safe-buffer": "^5.1.1"
}
},
"stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
"dev": true
},
"static-eval": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.0.tgz",
@ -11628,6 +11693,34 @@
"integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=",
"dev": true
},
"utile": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/utile/-/utile-0.3.0.tgz",
"integrity": "sha1-E1LDQOuCDk2N26A5pPv6oy7U7zo=",
"dev": true,
"requires": {
"async": "~0.9.0",
"deep-equal": "~0.2.1",
"i": "0.3.x",
"mkdirp": "0.x.x",
"ncp": "1.0.x",
"rimraf": "2.x.x"
},
"dependencies": {
"async": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
"integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=",
"dev": true
},
"deep-equal": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.2.tgz",
"integrity": "sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0=",
"dev": true
}
}
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@ -12112,6 +12205,41 @@
"string-width": "^1.0.2 || 2"
}
},
"winston": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz",
"integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=",
"dev": true,
"requires": {
"async": "~1.0.0",
"colors": "1.0.x",
"cycle": "1.0.x",
"eyes": "0.1.x",
"isstream": "0.1.x",
"pkginfo": "0.3.x",
"stack-trace": "0.0.x"
},
"dependencies": {
"async": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz",
"integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=",
"dev": true
},
"colors": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
"integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=",
"dev": true
},
"pkginfo": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz",
"integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=",
"dev": true
}
}
},
"wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",

View File

@ -34,6 +34,7 @@
"babel-loader": "^7.1.4",
"babel-preset-env": "^1.6.1",
"bootstrap": "^4.1.1",
"colors": "^1.3.0",
"css-loader": "^0.28.11",
"eslint": "^4.19.1",
"exports-loader": "^0.7.0",
@ -60,6 +61,7 @@
"postcss-import": "^11.1.0",
"postcss-loader": "^2.1.4",
"sass-loader": "^7.0.2",
"prompt": "^1.0.0",
"sitemap": "^1.13.0",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
@ -127,6 +129,7 @@
"test": "grunt test",
"docs": "grunt docs",
"lint": "grunt lint",
"newop": "node --experimental-modules src/core/config/scripts/newOperation.mjs",
"postinstall": "[ -f node_modules/crypto-api/src/crypto-api.mjs ] || npx j2m node_modules/crypto-api/src/crypto-api.js"
}
}

View File

@ -9,7 +9,7 @@
import "babel-polyfill";
import Chef from "./Chef";
import OperationConfig from "./config/OperationConfig.json";
import OpModules from "./config/modules/Default";
import OpModules from "./config/modules/OpModules";
// Add ">" to the start of all log messages in the Chef Worker
import loglevelMessagePrefix from "loglevel-message-prefix";
@ -104,12 +104,16 @@ async function bake(data) {
self.postMessage({
action: "bakeComplete",
data: response
data: Object.assign(response, {
id: data.id
})
});
} catch (err) {
self.postMessage({
action: "bakeError",
data: err
data: Object.assign(err, {
id: data.id
})
});
}
}

View File

@ -0,0 +1,223 @@
/**
* Interactive script for generating a new operation template.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
/*eslint no-console: ["off"] */
import prompt from "prompt";
import colors from "colors";
import process from "process";
import fs from "fs";
import path from "path";
import EscapeString from "../../operations/EscapeString";
const dir = path.join(process.cwd() + "/src/core/operations/");
if (!fs.existsSync(dir)) {
console.log("\nCWD: " + process.cwd());
console.log("Error: newOperation.mjs should be run from the project root");
console.log("Example> node --experimental-modules src/core/config/scripts/newOperation.mjs");
process.exit(1);
}
const ioTypes = ["string", "byteArray", "number", "html", "ArrayBuffer", "BigNumber", "JSON", "File", "List<File>"];
const schema = {
properties: {
opName: {
description: "The operation name should be short but descriptive.",
example: "URL Decode",
prompt: "Operation name",
type: "string",
pattern: /^[\w\s-/().]+$/,
required: true,
message: "Operation names should consist of letters, numbers or the following symbols: _-/()."
},
module: {
description: `Modules are used to group operations that rely on large libraries. Any operation that is not in the Default module will be loaded in dynamically when it is first called. All operations in the same module will also be loaded at this time. This system prevents the CyberChef web app from getting too bloated and taking a long time to load initially.
If your operation does not rely on a library, just leave this blank and it will be added to the Default module. If it relies on the same library as other operations, enter the name of the module those operations are in. If it relies on a new large library, enter a new module name (capitalise the first letter).`,
example: "Crypto",
prompt: "Module",
type: "string",
pattern: /^[A-Z][A-Za-z\d]+$/,
message: "Module names should start with a capital letter and not contain any spaces or symbols.",
default: "Default"
},
description: {
description: "The description should explain what the operation is and how it works. It can describe how the arguments should be entered and give examples of expected input and output. HTML markup is supported. Use <code> tags for examples. The description is scanned during searches, so include terms that are likely to be searched for when someone is looking for your operation.",
example: "Converts URI/URL percent-encoded characters back to their raw values.<br><br>e.g. <code>%3d</code> becomes <code>=</code>",
prompt: "Description",
type: "string"
},
inputType: {
description: `The input type defines how the input data will be presented to your operation. Check the project wiki for a full description of each type. The options are: ${ioTypes.join(", ")}.`,
example: "string",
prompt: "Input type",
type: "string",
pattern: new RegExp(`^(${ioTypes.join("|")})$`),
required: true,
message: `The input type should be one of: ${ioTypes.join(", ")}.`
},
outputType: {
description: `The output type tells CyberChef what sort of data you are returning from your operation. Check the project wiki for a full description of each type. The options are: ${ioTypes.join(", ")}.`,
example: "string",
prompt: "Output type",
type: "string",
pattern: new RegExp(`^(${ioTypes.join("|")})$`),
required: true,
message: `The output type should be one of: ${ioTypes.join(", ")}.`
},
highlight: {
description: "If your operation does not change the length of the input in any way, we can enable highlighting. If it does change the length in a predictable way, we may still be able to enable highlighting and calculate the correct offsets. If this is not possible, we will disable highlighting for this operation.",
example: "true/false",
prompt: "Enable highlighting",
type: "boolean",
default: "false",
message: "Enter true or false to specify if highlighting should be enabled."
},
authorName: {
description: "Your name or username will be added to the @author tag for this operation.",
example: "n1474335",
prompt: "Username",
type: "string"
},
authorEmail: {
description: "Your email address will also be added to the @author tag for this operation.",
example: "n1474335@gmail.com",
prompt: "Email",
type: "string"
}
}
};
// Build schema
for (const prop in schema.properties) {
const p = schema.properties[prop];
p.description = "\n" + colors.white(p.description) + colors.cyan("\nExample: " + p.example) + "\n" + colors.green(p.prompt);
}
console.log("\n\nThis script will generate a new operation template based on the information you provide. These values can be changed manually later.".yellow);
prompt.message = "";
prompt.delimiter = ":".green;
prompt.start();
prompt.get(schema, (err, result) => {
if (err) {
console.log("\nExiting build script.");
process.exit(0);
}
const moduleName = result.opName.replace(/\w\S*/g, txt => {
return txt.charAt(0).toUpperCase() + txt.substr(1);
}).replace(/[\s-()/./]/g, "");
const template = `/**
* @author ${result.authorName} [${result.authorEmail}]
* @copyright Crown Copyright ${(new Date()).getFullYear()}
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
/**
* ${result.opName} operation
*/
class ${moduleName} extends Operation {
/**
* ${moduleName} constructor
*/
constructor() {
super();
this.name = "${result.opName}";
this.module = "${result.module}";
this.description = "${(new EscapeString).run(result.description, ["Special chars", "Double"])}";
this.inputType = "${result.inputType}";
this.outputType = "${result.outputType}";
this.args = [
/* Example arguments. See the project wiki for full details.
{
name: "First arg",
type: "string",
value: "Don't Panic"
},
{
name: "Second arg",
type: "number",
value: 42
}
*/
];
}
/**
* @param {${result.inputType}} input
* @param {Object[]} args
* @returns {${result.outputType}}
*/
run(input, args) {
// const [firstArg, secondArg] = args;
throw new OperationError("Test");
}
${result.highlight ? `
/**
* Highlight ${result.opName}
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight ${result.opName} in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
` : ""}
}
export default ${moduleName};
`;
//console.log(template);
const filename = path.join(dir, `./${moduleName}.mjs`);
if (fs.existsSync(filename)) {
console.log(`${filename} already exists. It has NOT been overwritten.`.red);
console.log("Choose a different operation name to avoid conflicts.");
process.exit(0);
}
fs.writeFileSync(filename, template);
console.log(`\nOperation template written to ${colors.green(filename)}`);
console.log(`\nNext steps:
1. Add your operation to ${colors.green("src/core/config/Categories.json")}
2. Write your operation code.
3. Write tests in ${colors.green("test/tests/operations/")}
4. Run ${colors.cyan("npm run lint")} and ${colors.cyan("npm run test")}
5. Submit a Pull Request to get your operation added to the official CyberChef repository.`);
});

File diff suppressed because it is too large Load Diff

View File

@ -41,6 +41,11 @@ export const ARITHMETIC_DELIM_OPTIONS = ["Line feed", "Space", "Comma", "Semi-co
*/
export const HASH_DELIM_OPTIONS = ["Line feed", "CRLF", "Space", "Comma"];
/**
* IP delimiters
*/
export const IP_DELIM_OPTIONS = ["Line feed", "CRLF", "Space", "Comma", "Semi-colon"];
/**
* Split delimiters.
*/

557
src/core/lib/IP.mjs Normal file
View File

@ -0,0 +1,557 @@
/**
* IP resources.
*
* @author picapi
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
/**
* Parses an IPv4 CIDR range (e.g. 192.168.0.0/24) and displays information about it.
*
* @param {RegExp} cidr
* @param {boolean} includeNetworkInfo
* @param {boolean} enumerateAddresses
* @param {boolean} allowLargeList
* @returns {string}
*/
export function ipv4CidrRange(cidr, includeNetworkInfo, enumerateAddresses, allowLargeList) {
const network = strToIpv4(cidr[1]),
cidrRange = parseInt(cidr[2], 10);
let output = "";
if (cidrRange < 0 || cidrRange > 31) {
return "IPv4 CIDR must be less than 32";
}
const mask = ~(0xFFFFFFFF >>> cidrRange),
ip1 = network & mask,
ip2 = ip1 | ~mask;
if (includeNetworkInfo) {
output += "Network: " + ipv4ToStr(network) + "\n";
output += "CIDR: " + cidrRange + "\n";
output += "Mask: " + ipv4ToStr(mask) + "\n";
output += "Range: " + ipv4ToStr(ip1) + " - " + ipv4ToStr(ip2) + "\n";
output += "Total addresses in range: " + (((ip2 - ip1) >>> 0) + 1) + "\n\n";
}
if (enumerateAddresses) {
if (cidrRange >= 16 || allowLargeList) {
output += generateIpv4Range(ip1, ip2).join("\n");
} else {
output += _LARGE_RANGE_ERROR;
}
}
return output;
}
/**
* Parses an IPv6 CIDR range (e.g. ff00::/48) and displays information about it.
*
* @param {RegExp} cidr
* @param {boolean} includeNetworkInfo
* @returns {string}
*/
export function ipv6CidrRange(cidr, includeNetworkInfo) {
let output = "";
const network = strToIpv6(cidr[1]),
cidrRange = parseInt(cidr[cidr.length-1], 10);
if (cidrRange < 0 || cidrRange > 127) {
return "IPv6 CIDR must be less than 128";
}
const ip1 = new Array(8),
ip2 = new Array(8),
total = new Array(128);
const mask = genIpv6Mask(cidrRange);
let totalDiff = "";
for (let i = 0; i < 8; i++) {
ip1[i] = network[i] & mask[i];
ip2[i] = ip1[i] | (~mask[i] & 0x0000FFFF);
totalDiff = (ip2[i] - ip1[i]).toString(2);
if (totalDiff !== "0") {
for (let n = 0; n < totalDiff.length; n++) {
total[i*16 + 16-(totalDiff.length-n)] = totalDiff[n];
}
}
}
if (includeNetworkInfo) {
output += "Network: " + ipv6ToStr(network) + "\n";
output += "Shorthand: " + ipv6ToStr(network, true) + "\n";
output += "CIDR: " + cidrRange + "\n";
output += "Mask: " + ipv6ToStr(mask) + "\n";
output += "Range: " + ipv6ToStr(ip1) + " - " + ipv6ToStr(ip2) + "\n";
output += "Total addresses in range: " + (parseInt(total.join(""), 2) + 1) + "\n\n";
}
return output;
}
/**
* Parses an IPv4 hyphenated range (e.g. 192.168.0.0 - 192.168.0.255) and displays information
* about it.
*
* @param {RegExp} range
* @param {boolean} includeNetworkInfo
* @param {boolean} enumerateAddresses
* @param {boolean} allowLargeList
* @returns {string}
*/
export function ipv4HyphenatedRange(range, includeNetworkInfo, enumerateAddresses, allowLargeList) {
const ip1 = strToIpv4(range[1]),
ip2 = strToIpv4(range[2]);
let output = "";
// Calculate mask
let diff = ip1 ^ ip2,
cidr = 32,
mask = 0;
while (diff !== 0) {
diff >>= 1;
cidr--;
mask = (mask << 1) | 1;
}
mask = ~mask >>> 0;
const network = ip1 & mask,
subIp1 = network & mask,
subIp2 = subIp1 | ~mask;
if (includeNetworkInfo) {
output += `Minimum subnet required to hold this range:
\tNetwork: ${ipv4ToStr(network)}
\tCIDR: ${cidr}
\tMask: ${ipv4ToStr(mask)}
\tSubnet range: ${ipv4ToStr(subIp1)} - ${ipv4ToStr(subIp2)}
\tTotal addresses in subnet: ${(((subIp2 - subIp1) >>> 0) + 1)}
Range: ${ipv4ToStr(ip1)} - ${ipv4ToStr(ip2)}
Total addresses in range: ${(((ip2 - ip1) >>> 0) + 1)}
`;
}
if (enumerateAddresses) {
if (((ip2 - ip1) >>> 0) <= 65536 || allowLargeList) {
output += generateIpv4Range(ip1, ip2).join("\n");
} else {
output += _LARGE_RANGE_ERROR;
}
}
return output;
}
/**
* Parses an IPv6 hyphenated range (e.g. ff00:: - ffff::) and displays information about it.
*
* @param {RegExp} range
* @param {boolean} includeNetworkInfo
* @returns {string}
*/
export function ipv6HyphenatedRange(range, includeNetworkInfo) {
const ip1 = strToIpv6(range[1]),
ip2 = strToIpv6(range[14]),
total = new Array(128).fill();
let output = "",
t = "",
i;
for (i = 0; i < 8; i++) {
t = (ip2[i] - ip1[i]).toString(2);
if (t !== "0") {
for (let n = 0; n < t.length; n++) {
total[i*16 + 16-(t.length-n)] = t[n];
}
}
}
if (includeNetworkInfo) {
output += "Range: " + ipv6ToStr(ip1) + " - " + ipv6ToStr(ip2) + "\n";
output += "Shorthand range: " + ipv6ToStr(ip1, true) + " - " + ipv6ToStr(ip2, true) + "\n";
output += "Total addresses in range: " + (parseInt(total.join(""), 2) + 1) + "\n\n";
}
return output;
}
/**
* Converts an IPv4 address from string format to numerical format.
*
* @param {string} ipStr
* @returns {number}
*
* @example
* // returns 168427520
* strToIpv4("10.10.0.0");
*/
export function strToIpv4(ipStr) {
const blocks = ipStr.split("."),
numBlocks = parseBlocks(blocks);
let result = 0;
result += numBlocks[0] << 24;
result += numBlocks[1] << 16;
result += numBlocks[2] << 8;
result += numBlocks[3];
return result;
/**
* Converts a list of 4 numeric strings in the range 0-255 to a list of numbers.
*/
function parseBlocks(blocks) {
if (blocks.length !== 4)
throw new OperationError("More than 4 blocks.");
const numBlocks = [];
for (let i = 0; i < 4; i++) {
numBlocks[i] = parseInt(blocks[i], 10);
if (numBlocks[i] < 0 || numBlocks[i] > 255)
throw new OperationError("Block out of range.");
}
return numBlocks;
}
}
/**
* Converts an IPv4 address from numerical format to string format.
*
* @param {number} ipInt
* @returns {string}
*
* @example
* // returns "10.10.0.0"
* ipv4ToStr(168427520);
*/
export function ipv4ToStr(ipInt) {
const blockA = (ipInt >> 24) & 255,
blockB = (ipInt >> 16) & 255,
blockC = (ipInt >> 8) & 255,
blockD = ipInt & 255;
return blockA + "." + blockB + "." + blockC + "." + blockD;
}
/**
* Converts an IPv6 address from string format to numerical array format.
*
* @param {string} ipStr
* @returns {number[]}
*
* @example
* // returns [65280, 0, 0, 0, 0, 0, 4369, 8738]
* strToIpv6("ff00::1111:2222");
*/
export function strToIpv6(ipStr) {
let j = 0;
const blocks = ipStr.split(":"),
numBlocks = parseBlocks(blocks),
ipv6 = new Array(8);
for (let i = 0; i < 8; i++) {
if (isNaN(numBlocks[j])) {
ipv6[i] = 0;
if (i === (8-numBlocks.slice(j).length)) j++;
} else {
ipv6[i] = numBlocks[j];
j++;
}
}
return ipv6;
/**
* Converts a list of 3-8 numeric hex strings in the range 0-65535 to a list of numbers.
*/
function parseBlocks(blocks) {
if (blocks.length < 3 || blocks.length > 8)
throw new OperationError("Badly formatted IPv6 address.");
const numBlocks = [];
for (let i = 0; i < blocks.length; i++) {
numBlocks[i] = parseInt(blocks[i], 16);
if (numBlocks[i] < 0 || numBlocks[i] > 65535)
throw new OperationError("Block out of range.");
}
return numBlocks;
}
}
/**
* Converts an IPv6 address from numerical array format to string format.
*
* @param {number[]} ipv6
* @param {boolean} compact - Whether or not to return the address in shorthand or not
* @returns {string}
*
* @example
* // returns "ff00::1111:2222"
* ipv6ToStr([65280, 0, 0, 0, 0, 0, 4369, 8738], true);
*
* // returns "ff00:0000:0000:0000:0000:0000:1111:2222"
* ipv6ToStr([65280, 0, 0, 0, 0, 0, 4369, 8738], false);
*/
export function ipv6ToStr(ipv6, compact) {
let output = "",
i = 0;
if (compact) {
let start = -1,
end = -1,
s = 0,
e = -1;
for (i = 0; i < 8; i++) {
if (ipv6[i] === 0 && e === (i-1)) {
e = i;
} else if (ipv6[i] === 0) {
s = i; e = i;
}
if (e >= 0 && (e-s) > (end - start)) {
start = s;
end = e;
}
}
for (i = 0; i < 8; i++) {
if (i !== start) {
output += Utils.hex(ipv6[i], 1) + ":";
} else {
output += ":";
i = end;
if (end === 7) output += ":";
}
}
if (output[0] === ":")
output = ":" + output;
} else {
for (i = 0; i < 8; i++) {
output += Utils.hex(ipv6[i], 4) + ":";
}
}
return output.slice(0, output.length-1);
}
/**
* Generates a list of IPv4 addresses in string format between two given numerical values.
*
* @param {number} ip
* @param {number} endIp
* @returns {string[]}
*
* @example
* // returns ["0.0.0.1", "0.0.0.2", "0.0.0.3"]
* IP.generateIpv4Range(1, 3);
*/
export function generateIpv4Range(ip, endIp) {
const range = [];
if (endIp >= ip) {
for (; ip <= endIp; ip++) {
range.push(ipv4ToStr(ip));
}
} else {
range[0] = "Second IP address smaller than first.";
}
return range;
}
/**
* Generates an IPv6 subnet mask given a CIDR value.
*
* @param {number} cidr
* @returns {number[]}
*/
export function genIpv6Mask(cidr) {
const mask = new Array(8);
let shift;
for (let i = 0; i < 8; i++) {
if (cidr > ((i+1)*16)) {
mask[i] = 0x0000FFFF;
} else {
shift = cidr-(i*16);
if (shift < 0) shift = 0;
mask[i] = ~((0x0000FFFF >>> shift) | 0xFFFF0000);
}
}
return mask;
}
const _LARGE_RANGE_ERROR = "The specified range contains more than 65,536 addresses. Running this query could crash your browser. If you want to run it, select the \"Allow large queries\" option. You are advised to turn off \"Auto Bake\" whilst editing large ranges.";
/**
* A regular expression that matches an IPv4 address
*/
export const IPV4_REGEX = /^\s*((?:\d{1,3}\.){3}\d{1,3})\s*$/;
/**
* A regular expression that matches an IPv6 address
*/
export const IPV6_REGEX = /^\s*(((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\4)::|:\b|(?![\dA-F])))|(?!\3\4)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4}))\s*$/i;
/**
* Lookup table for Internet Protocols.
* Taken from https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
*/
export const protocolLookup = {
0: {keyword: "HOPOPT", protocol: "IPv6 Hop-by-Hop Option"},
1: {keyword: "ICMP", protocol: "Internet Control Message"},
2: {keyword: "IGMP", protocol: "Internet Group Management"},
3: {keyword: "GGP", protocol: "Gateway-to-Gateway"},
4: {keyword: "IPv4", protocol: "IPv4 encapsulation"},
5: {keyword: "ST", protocol: "Stream"},
6: {keyword: "TCP", protocol: "Transmission Control"},
7: {keyword: "CBT", protocol: "CBT"},
8: {keyword: "EGP", protocol: "Exterior Gateway Protocol"},
9: {keyword: "IGP", protocol: "any private interior gateway (used by Cisco for their IGRP)"},
10: {keyword: "BBN-RCC-MON", protocol: "BBN RCC Monitoring"},
11: {keyword: "NVP-II", protocol: "Network Voice Protocol"},
12: {keyword: "PUP", protocol: "PUP"},
13: {keyword: "ARGUS (deprecated)", protocol: "ARGUS"},
14: {keyword: "EMCON", protocol: "EMCON"},
15: {keyword: "XNET", protocol: "Cross Net Debugger"},
16: {keyword: "CHAOS", protocol: "Chaos"},
17: {keyword: "UDP", protocol: "User Datagram"},
18: {keyword: "MUX", protocol: "Multiplexing"},
19: {keyword: "DCN-MEAS", protocol: "DCN Measurement Subsystems"},
20: {keyword: "HMP", protocol: "Host Monitoring"},
21: {keyword: "PRM", protocol: "Packet Radio Measurement"},
22: {keyword: "XNS-IDP", protocol: "XEROX NS IDP"},
23: {keyword: "TRUNK-1", protocol: "Trunk-1"},
24: {keyword: "TRUNK-2", protocol: "Trunk-2"},
25: {keyword: "LEAF-1", protocol: "Leaf-1"},
26: {keyword: "LEAF-2", protocol: "Leaf-2"},
27: {keyword: "RDP", protocol: "Reliable Data Protocol"},
28: {keyword: "IRTP", protocol: "Internet Reliable Transaction"},
29: {keyword: "ISO-TP4", protocol: "ISO Transport Protocol Class 4"},
30: {keyword: "NETBLT", protocol: "Bulk Data Transfer Protocol"},
31: {keyword: "MFE-NSP", protocol: "MFE Network Services Protocol"},
32: {keyword: "MERIT-INP", protocol: "MERIT Internodal Protocol"},
33: {keyword: "DCCP", protocol: "Datagram Congestion Control Protocol"},
34: {keyword: "3PC", protocol: "Third Party Connect Protocol"},
35: {keyword: "IDPR", protocol: "Inter-Domain Policy Routing Protocol"},
36: {keyword: "XTP", protocol: "XTP"},
37: {keyword: "DDP", protocol: "Datagram Delivery Protocol"},
38: {keyword: "IDPR-CMTP", protocol: "IDPR Control Message Transport Proto"},
39: {keyword: "TP++", protocol: "TP++ Transport Protocol"},
40: {keyword: "IL", protocol: "IL Transport Protocol"},
41: {keyword: "IPv6", protocol: "IPv6 encapsulation"},
42: {keyword: "SDRP", protocol: "Source Demand Routing Protocol"},
43: {keyword: "IPv6-Route", protocol: "Routing Header for IPv6"},
44: {keyword: "IPv6-Frag", protocol: "Fragment Header for IPv6"},
45: {keyword: "IDRP", protocol: "Inter-Domain Routing Protocol"},
46: {keyword: "RSVP", protocol: "Reservation Protocol"},
47: {keyword: "GRE", protocol: "Generic Routing Encapsulation"},
48: {keyword: "DSR", protocol: "Dynamic Source Routing Protocol"},
49: {keyword: "BNA", protocol: "BNA"},
50: {keyword: "ESP", protocol: "Encap Security Payload"},
51: {keyword: "AH", protocol: "Authentication Header"},
52: {keyword: "I-NLSP", protocol: "Integrated Net Layer Security TUBA"},
53: {keyword: "SWIPE (deprecated)", protocol: "IP with Encryption"},
54: {keyword: "NARP", protocol: "NBMA Address Resolution Protocol"},
55: {keyword: "MOBILE", protocol: "IP Mobility"},
56: {keyword: "TLSP", protocol: "Transport Layer Security Protocol using Kryptonet key management"},
57: {keyword: "SKIP", protocol: "SKIP"},
58: {keyword: "IPv6-ICMP", protocol: "ICMP for IPv6"},
59: {keyword: "IPv6-NoNxt", protocol: "No Next Header for IPv6"},
60: {keyword: "IPv6-Opts", protocol: "Destination Options for IPv6"},
61: {keyword: "", protocol: "any host internal protocol"},
62: {keyword: "CFTP", protocol: "CFTP"},
63: {keyword: "", protocol: "any local network"},
64: {keyword: "SAT-EXPAK", protocol: "SATNET and Backroom EXPAK"},
65: {keyword: "KRYPTOLAN", protocol: "Kryptolan"},
66: {keyword: "RVD", protocol: "MIT Remote Virtual Disk Protocol"},
67: {keyword: "IPPC", protocol: "Internet Pluribus Packet Core"},
68: {keyword: "", protocol: "any distributed file system"},
69: {keyword: "SAT-MON", protocol: "SATNET Monitoring"},
70: {keyword: "VISA", protocol: "VISA Protocol"},
71: {keyword: "IPCV", protocol: "Internet Packet Core Utility"},
72: {keyword: "CPNX", protocol: "Computer Protocol Network Executive"},
73: {keyword: "CPHB", protocol: "Computer Protocol Heart Beat"},
74: {keyword: "WSN", protocol: "Wang Span Network"},
75: {keyword: "PVP", protocol: "Packet Video Protocol"},
76: {keyword: "BR-SAT-MON", protocol: "Backroom SATNET Monitoring"},
77: {keyword: "SUN-ND", protocol: "SUN ND PROTOCOL-Temporary"},
78: {keyword: "WB-MON", protocol: "WIDEBAND Monitoring"},
79: {keyword: "WB-EXPAK", protocol: "WIDEBAND EXPAK"},
80: {keyword: "ISO-IP", protocol: "ISO Internet Protocol"},
81: {keyword: "VMTP", protocol: "VMTP"},
82: {keyword: "SECURE-VMTP", protocol: "SECURE-VMTP"},
83: {keyword: "VINES", protocol: "VINES"},
84: {keyword: "TTP", protocol: "Transaction Transport Protocol"},
85: {keyword: "NSFNET-IGP", protocol: "NSFNET-IGP"},
86: {keyword: "DGP", protocol: "Dissimilar Gateway Protocol"},
87: {keyword: "TCF", protocol: "TCF"},
88: {keyword: "EIGRP", protocol: "EIGRP"},
89: {keyword: "OSPFIGP", protocol: "OSPFIGP"},
90: {keyword: "Sprite-RPC", protocol: "Sprite RPC Protocol"},
91: {keyword: "LARP", protocol: "Locus Address Resolution Protocol"},
92: {keyword: "MTP", protocol: "Multicast Transport Protocol"},
93: {keyword: "AX.25", protocol: "AX.25 Frames"},
94: {keyword: "IPIP", protocol: "IP-within-IP Encapsulation Protocol"},
95: {keyword: "MICP (deprecated)", protocol: "Mobile Internetworking Control Pro."},
96: {keyword: "SCC-SP", protocol: "Semaphore Communications Sec. Pro."},
97: {keyword: "ETHERIP", protocol: "Ethernet-within-IP Encapsulation"},
98: {keyword: "ENCAP", protocol: "Encapsulation Header"},
99: {keyword: "", protocol: "any private encryption scheme"},
100: {keyword: "GMTP", protocol: "GMTP"},
101: {keyword: "IFMP", protocol: "Ipsilon Flow Management Protocol"},
102: {keyword: "PNNI", protocol: "PNNI over IP"},
103: {keyword: "PIM", protocol: "Protocol Independent Multicast"},
104: {keyword: "ARIS", protocol: "ARIS"},
105: {keyword: "SCPS", protocol: "SCPS"},
106: {keyword: "QNX", protocol: "QNX"},
107: {keyword: "A/N", protocol: "Active Networks"},
108: {keyword: "IPComp", protocol: "IP Payload Compression Protocol"},
109: {keyword: "SNP", protocol: "Sitara Networks Protocol"},
110: {keyword: "Compaq-Peer", protocol: "Compaq Peer Protocol"},
111: {keyword: "IPX-in-IP", protocol: "IPX in IP"},
112: {keyword: "VRRP", protocol: "Virtual Router Redundancy Protocol"},
113: {keyword: "PGM", protocol: "PGM Reliable Transport Protocol"},
114: {keyword: "", protocol: "any 0-hop protocol"},
115: {keyword: "L2TP", protocol: "Layer Two Tunneling Protocol"},
116: {keyword: "DDX", protocol: "D-II Data Exchange (DDX)"},
117: {keyword: "IATP", protocol: "Interactive Agent Transfer Protocol"},
118: {keyword: "STP", protocol: "Schedule Transfer Protocol"},
119: {keyword: "SRP", protocol: "SpectraLink Radio Protocol"},
120: {keyword: "UTI", protocol: "UTI"},
121: {keyword: "SMP", protocol: "Simple Message Protocol"},
122: {keyword: "SM (deprecated)", protocol: "Simple Multicast Protocol"},
123: {keyword: "PTP", protocol: "Performance Transparency Protocol"},
124: {keyword: "ISIS over IPv4", protocol: ""},
125: {keyword: "FIRE", protocol: ""},
126: {keyword: "CRTP", protocol: "Combat Radio Transport Protocol"},
127: {keyword: "CRUDP", protocol: "Combat Radio User Datagram"},
128: {keyword: "SSCOPMCE", protocol: ""},
129: {keyword: "IPLT", protocol: ""},
130: {keyword: "SPS", protocol: "Secure Packet Shield"},
131: {keyword: "PIPE", protocol: "Private IP Encapsulation within IP"},
132: {keyword: "SCTP", protocol: "Stream Control Transmission Protocol"},
133: {keyword: "FC", protocol: "Fibre Channel"},
134: {keyword: "RSVP-E2E-IGNORE", protocol: ""},
135: {keyword: "Mobility Header", protocol: ""},
136: {keyword: "UDPLite", protocol: ""},
137: {keyword: "MPLS-in-IP", protocol: ""},
138: {keyword: "manet", protocol: "MANET Protocols"},
139: {keyword: "HIP", protocol: "Host Identity Protocol"},
140: {keyword: "Shim6", protocol: "Shim6 Protocol"},
141: {keyword: "WESP", protocol: "Wrapped Encapsulating Security Payload"},
142: {keyword: "ROHC", protocol: "Robust Header Compression"},
253: {keyword: "", protocol: "Use for experimentation and testing"},
254: {keyword: "", protocol: "Use for experimentation and testing"},
255: {keyword: "Reserved", protocol: ""}
};

View File

@ -229,17 +229,22 @@ class Magic {
const testEnc = async op => {
for (let i = 0; i < encodings.length; i++) {
const conf = {
op: op,
args: [encodings[i]]
},
data = await this._runRecipe([conf], sample.buffer);
op: op,
args: [encodings[i]]
};
// Only add to the results if it changed the data
if (!_buffersEqual(data, sample.buffer)) {
results.push({
data: data,
conf: conf
});
try {
const data = await this._runRecipe([conf], sample.buffer);
// Only add to the results if it changed the data
if (!_buffersEqual(data, sample.buffer)) {
results.push({
data: data,
conf: conf
});
}
} catch (err) {
continue;
}
}
};
@ -344,6 +349,11 @@ class Magic {
aScore += a.entropy;
bScore += b.entropy;
// A result with no recipe but matching ops suggests there are better options
if ((!a.recipe.length && a.matchingOps.length) &&
b.recipe.length)
return 1;
return aScore - bScore;
});
}
@ -356,8 +366,9 @@ class Magic {
* @returns {ArrayBuffer}
*/
async _runRecipe(recipeConfig, input=this.inputBuffer) {
input = input instanceof ArrayBuffer ? input : input.buffer;
const dish = new Dish();
dish.set(input.buffer, Dish.ARRAY_BUFFER);
dish.set(input, Dish.ARRAY_BUFFER);
if (ENVIRONMENT_IS_WORKER()) self.loadRequiredModules(recipeConfig);

View File

@ -12,8 +12,8 @@
import OperationError from "../errors/OperationError";
import kbpgp from "kbpgp";
import promisifyDefault from "es6-promisify";
const promisify = promisifyDefault.promisify;
import * as es6promisify from "es6-promisify";
const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
/**
* Progress callback

View File

@ -0,0 +1,72 @@
/**
* Public key resources.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import { toHex, fromHex } from "./Hex";
/**
* Formats Distinguished Name (DN) strings.
*
* @param {string} dnStr
* @param {number} indent
* @returns {string}
*/
export function formatDnStr (dnStr, indent) {
const fields = dnStr.substr(1).replace(/([^\\])\//g, "$1$1/").split(/[^\\]\//);
let output = "",
maxKeyLen = 0,
key,
value,
i,
str;
for (i = 0; i < fields.length; i++) {
if (!fields[i].length) continue;
key = fields[i].split("=")[0];
maxKeyLen = key.length > maxKeyLen ? key.length : maxKeyLen;
}
for (i = 0; i < fields.length; i++) {
if (!fields[i].length) continue;
key = fields[i].split("=")[0];
value = fields[i].split("=")[1];
str = key.padEnd(maxKeyLen, " ") + " = " + value + "\n";
output += str.padStart(indent + str.length, " ");
}
return output.slice(0, -1);
}
/**
* Formats byte strings by adding line breaks and delimiters.
*
* @param {string} byteStr
* @param {number} length - Line width
* @param {number} indent
* @returns {string}
*/
export function formatByteStr (byteStr, length, indent) {
byteStr = toHex(fromHex(byteStr), ":");
length = length * 3;
let output = "";
for (let i = 0; i < byteStr.length; i += length) {
const str = byteStr.slice(i, i + length) + "\n";
if (i === 0) {
output += str;
} else {
output += str.padStart(indent + str.length, " ");
}
}
return output.slice(0, output.length-1);
}

View File

@ -0,0 +1,47 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import vkbeautify from "vkbeautify";
import Operation from "../Operation";
/**
* CSS Beautify operation
*/
class CSSBeautify extends Operation {
/**
* CSSBeautify constructor
*/
constructor() {
super();
this.name = "CSS Beautify";
this.module = "Code";
this.description = "Indents and prettifies Cascading Style Sheets (CSS) code.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Indent string",
"type": "binaryShortString",
"value": "\\t"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const indentStr = args[0];
return vkbeautify.css(input, indentStr);
}
}
export default CSSBeautify;

View File

@ -0,0 +1,47 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import vkbeautify from "vkbeautify";
import Operation from "../Operation";
/**
* CSS Minify operation
*/
class CSSMinify extends Operation {
/**
* CSSMinify constructor
*/
constructor() {
super();
this.name = "CSS Minify";
this.module = "Code";
this.description = "Compresses Cascading Style Sheets (CSS) code.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Preserve comments",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const preserveComments = args[0];
return vkbeautify.cssmin(input, preserveComments);
}
}
export default CSSMinify;

View File

@ -0,0 +1,90 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import xmldom from "xmldom";
import nwmatcher from "nwmatcher";
/**
* CSS selector operation
*/
class CSSSelector extends Operation {
/**
* CSSSelector constructor
*/
constructor() {
super();
this.name = "CSS selector";
this.module = "Code";
this.description = "Extract information from an HTML document with a CSS selector";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "CSS selector",
"type": "string",
"value": ""
},
{
"name": "Delimiter",
"type": "binaryShortString",
"value": "\\n"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [query, delimiter] = args,
parser = new xmldom.DOMParser();
let dom,
result;
if (!query.length || !input.length) {
return "";
}
try {
dom = parser.parseFromString(input);
} catch (err) {
throw new OperationError("Invalid input HTML.");
}
try {
const matcher = nwmatcher({document: dom});
result = matcher.select(query, dom);
} catch (err) {
throw new OperationError("Invalid CSS Selector. Details:\n" + err.message);
}
const nodeToString = function(node) {
return node.toString();
/* xmldom does not return the outerHTML value.
switch (node.nodeType) {
case node.ELEMENT_NODE: return node.outerHTML;
case node.ATTRIBUTE_NODE: return node.value;
case node.TEXT_NODE: return node.wholeText;
case node.COMMENT_NODE: return node.data;
case node.DOCUMENT_NODE: return node.outerHTML;
default: throw new Error("Unknown Node Type: " + node.nodeType);
}*/
};
return result
.map(nodeToString)
.join(delimiter);
}
}
export default CSSSelector;

View File

@ -0,0 +1,120 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Utils from "../Utils";
import {fromHex} from "../lib/Hex";
/**
* Change IP format operation
*/
class ChangeIPFormat extends Operation {
/**
* ChangeIPFormat constructor
*/
constructor() {
super();
this.name = "Change IP format";
this.module = "JSBN";
this.description = "Convert an IP address from one format to another, e.g. <code>172.20.23.54</code> to <code>ac141736</code>";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Input format",
"type": "option",
"value": ["Hex", "Raw"]
},
{
"name": "Output format",
"type": "option",
"value": ["Hex", "Raw"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [inFormat, outFormat] = args,
lines = input.split("\n");
let output = "",
j = 0;
for (let i = 0; i < lines.length; i++) {
if (lines[i] === "") continue;
let baIp = [];
let octets;
let decimal;
if (inFormat === outFormat) {
output += lines[i] + "\n";
continue;
}
// Convert to byte array IP from input format
switch (inFormat) {
case "Dotted Decimal":
octets = lines[i].split(".");
for (j = 0; j < octets.length; j++) {
baIp.push(parseInt(octets[j], 10));
}
break;
case "Decimal":
decimal = lines[i].toString();
baIp.push(decimal >> 24 & 255);
baIp.push(decimal >> 16 & 255);
baIp.push(decimal >> 8 & 255);
baIp.push(decimal & 255);
break;
case "Hex":
baIp = fromHex(lines[i]);
break;
default:
throw new OperationError("Unsupported input IP format");
}
let ddIp;
let decIp;
let hexIp;
// Convert byte array IP to output format
switch (outFormat) {
case "Dotted Decimal":
ddIp = "";
for (j = 0; j < baIp.length; j++) {
ddIp += baIp[j] + ".";
}
output += ddIp.slice(0, ddIp.length-1) + "\n";
break;
case "Decimal":
decIp = ((baIp[0] << 24) | (baIp[1] << 16) | (baIp[2] << 8) | baIp[3]) >>> 0;
output += decIp.toString() + "\n";
break;
case "Hex":
hexIp = "";
for (j = 0; j < baIp.length; j++) {
hexIp += Utils.hex(baIp[j]);
}
output += hexIp + "\n";
break;
default:
throw new OperationError("Unsupported output IP format");
}
}
return output.slice(0, output.length-1);
}
}
export default ChangeIPFormat;

View File

@ -71,36 +71,39 @@ class Diff extends Operation {
let output = "",
diff;
// Node and Webpack load modules slightly differently
const jsdiff = JsDiff.default ? JsDiff.default : JsDiff;
if (!samples || samples.length !== 2) {
throw new OperationError("Incorrect number of samples, perhaps you need to modify the sample delimiter or add more samples?");
}
switch (diffBy) {
case "Character":
diff = JsDiff.diffChars(samples[0], samples[1]);
diff = jsdiff.diffChars(samples[0], samples[1]);
break;
case "Word":
if (ignoreWhitespace) {
diff = JsDiff.diffWords(samples[0], samples[1]);
diff = jsdiff.diffWords(samples[0], samples[1]);
} else {
diff = JsDiff.diffWordsWithSpace(samples[0], samples[1]);
diff = jsdiff.diffWordsWithSpace(samples[0], samples[1]);
}
break;
case "Line":
if (ignoreWhitespace) {
diff = JsDiff.diffTrimmedLines(samples[0], samples[1]);
diff = jsdiff.diffTrimmedLines(samples[0], samples[1]);
} else {
diff = JsDiff.diffLines(samples[0], samples[1]);
diff = jsdiff.diffLines(samples[0], samples[1]);
}
break;
case "Sentence":
diff = JsDiff.diffSentences(samples[0], samples[1]);
diff = jsdiff.diffSentences(samples[0], samples[1]);
break;
case "CSS":
diff = JsDiff.diffCss(samples[0], samples[1]);
diff = jsdiff.diffCss(samples[0], samples[1]);
break;
case "JSON":
diff = JsDiff.diffJson(samples[0], samples[1]);
diff = jsdiff.diffJson(samples[0], samples[1]);
break;
default:
throw new OperationError("Invalid 'Diff by' option.");

View File

@ -0,0 +1,62 @@
/**
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import ExifParser from "exif-parser";
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
/**
* Extract EXIF operation
*/
class ExtractEXIF extends Operation {
/**
* ExtractEXIF constructor
*/
constructor() {
super();
this.name = "Extract EXIF";
this.module = "Image";
this.description = [
"Extracts EXIF data from an image.",
"<br><br>",
"EXIF data is metadata embedded in images (JPEG, JPG, TIFF) and audio files.",
"<br><br>",
"EXIF data from photos usually contains information about the image file itself as well as the device used to create it.",
].join("\n");
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
try {
const parser = ExifParser.create(input);
const result = parser.parse();
const lines = [];
for (const tagName in result.tags) {
const value = result.tags[tagName];
lines.push(`${tagName}: ${value}`);
}
const numTags = lines.length;
lines.unshift(`Found ${numTags} tags.\n`);
return lines.join("\n");
} catch (err) {
throw new OperationError(`Could not extract EXIF data from image: ${err}`);
}
}
}
export default ExtractEXIF;

View File

@ -0,0 +1,86 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Find / Replace operation
*/
class FindReplace extends Operation {
/**
* FindReplace constructor
*/
constructor() {
super();
this.name = "Find / Replace";
this.module = "Regex";
this.description = "Replaces all occurrences of the first string with the second.<br><br>Includes support for regular expressions (regex), simple strings and extended strings (which support \\n, \\r, \\t, \\b, \\f and escaped hex bytes using \\x notation, e.g. \\x00 for a null byte).";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Find",
"type": "toggleString",
"value": "",
"toggleValues": ["Regex", "Extended (\\n, \\t, \\x...)", "Simple string"]
},
{
"name": "Replace",
"type": "binaryString",
"value": ""
},
{
"name": "Global match",
"type": "boolean",
"value": true
},
{
"name": "Case insensitive",
"type": "boolean",
"value": false
},
{
"name": "Multiline matching",
"type": "boolean",
"value": true
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [{option: type}, replace, g, i, m] = args;
let find = args[0].string,
modifiers = "";
if (g) modifiers += "g";
if (i) modifiers += "i";
if (m) modifiers += "m";
if (type === "Regex") {
find = new RegExp(find, modifiers);
return input.replace(find, replace);
}
if (type.indexOf("Extended") === 0) {
find = Utils.parseEscapedChars(find);
}
find = new RegExp(Utils.escapeRegex(find), modifiers);
return input.replace(find, replace);
}
}
export default FindReplace;

View File

@ -9,8 +9,8 @@
import Operation from "../Operation";
import kbpgp from "kbpgp";
import { getSubkeySize, ASP } from "../lib/PGP";
import promisifyDefault from "es6-promisify";
const promisify = promisifyDefault.promisify;
import * as es6promisify from "es6-promisify";
const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
/**
* Generate PGP Key Pair operation

View File

@ -0,0 +1,136 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import {IP_DELIM_OPTIONS} from "../lib/Delim";
import {ipv6ToStr, genIpv6Mask, IPV4_REGEX, strToIpv6, ipv4ToStr, IPV6_REGEX, strToIpv4} from "../lib/IP";
/**
* Group IP addresses operation
*/
class GroupIPAddresses extends Operation {
/**
* GroupIPAddresses constructor
*/
constructor() {
super();
this.name = "Group IP addresses";
this.module = "JSBN";
this.description = "Groups a list of IP addresses into subnets. Supports both IPv4 and IPv6 addresses.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Delimiter",
"type": "option",
"value": IP_DELIM_OPTIONS
},
{
"name": "Subnet (CIDR)",
"type": "number",
"value": 24
},
{
"name": "Only show the subnets",
"type": "boolean",
"value": false,
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const delim = Utils.charRep(args[0]),
cidr = args[1],
onlySubnets = args[2],
ipv4Mask = cidr < 32 ? ~(0xFFFFFFFF >>> cidr) : 0xFFFFFFFF,
ipv6Mask = genIpv6Mask(cidr),
ips = input.split(delim),
ipv4Networks = {},
ipv6Networks = {};
let match = null,
output = "",
ip = null,
network = null,
networkStr = "",
i;
if (cidr < 0 || cidr > 127) {
throw new OperationError("CIDR must be less than 32 for IPv4 or 128 for IPv6");
}
// Parse all IPs and add to network dictionary
for (i = 0; i < ips.length; i++) {
if ((match = IPV4_REGEX.exec(ips[i]))) {
ip = strToIpv4(match[1]) >>> 0;
network = ip & ipv4Mask;
if (ipv4Networks.hasOwnProperty(network)) {
ipv4Networks[network].push(ip);
} else {
ipv4Networks[network] = [ip];
}
} else if ((match = IPV6_REGEX.exec(ips[i]))) {
ip = strToIpv6(match[1]);
network = [];
networkStr = "";
for (let j = 0; j < 8; j++) {
network.push(ip[j] & ipv6Mask[j]);
}
networkStr = ipv6ToStr(network, true);
if (ipv6Networks.hasOwnProperty(networkStr)) {
ipv6Networks[networkStr].push(ip);
} else {
ipv6Networks[networkStr] = [ip];
}
}
}
// Sort IPv4 network dictionaries and print
for (network in ipv4Networks) {
ipv4Networks[network] = ipv4Networks[network].sort();
output += ipv4ToStr(network) + "/" + cidr + "\n";
if (!onlySubnets) {
for (i = 0; i < ipv4Networks[network].length; i++) {
output += " " + ipv4ToStr(ipv4Networks[network][i]) + "\n";
}
output += "\n";
}
}
// Sort IPv6 network dictionaries and print
for (networkStr in ipv6Networks) {
//ipv6Networks[networkStr] = ipv6Networks[networkStr].sort(); TODO
output += networkStr + "/" + cidr + "\n";
if (!onlySubnets) {
for (i = 0; i < ipv6Networks[networkStr].length; i++) {
output += " " + ipv6ToStr(ipv6Networks[networkStr][i], true) + "\n";
}
output += "\n";
}
}
return output;
}
}
export default GroupIPAddresses;

View File

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import r from "jsrsasign";
import Operation from "../Operation";
/**
* Hex to Object Identifier operation
*/
class HexToObjectIdentifier extends Operation {
/**
* HexToObjectIdentifier constructor
*/
constructor() {
super();
this.name = "Hex to Object Identifier";
this.module = "PublicKey";
this.description = "Converts a hexadecimal string into an object identifier (OID).";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return r.KJUR.asn1.ASN1Util.oidHexToInt(input.replace(/\s/g, ""));
}
}
export default HexToObjectIdentifier;

View File

@ -0,0 +1,46 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import r from "jsrsasign";
import Operation from "../Operation";
/**
* Hex to PEM operation
*/
class HexToPEM extends Operation {
/**
* HexToPEM constructor
*/
constructor() {
super();
this.name = "Hex to PEM";
this.module = "PublicKey";
this.description = "Converts a hexadecimal DER (Distinguished Encoding Rules) string into PEM (Privacy Enhanced Mail) format.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Header string",
"type": "string",
"value": "CERTIFICATE"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return r.KJUR.asn1.ASN1Util.getPEMStringFromHex(input.replace(/\s/g, ""), args[0]);
}
}
export default HexToPEM;

View File

@ -0,0 +1,68 @@
/**
* @author Matt C (matt@artemisbot.uk)
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import jpath from "jsonpath";
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
/**
* JPath expression operation
*/
class JPathExpression extends Operation {
/**
* JPathExpression constructor
*/
constructor() {
super();
this.name = "JPath expression";
this.module = "Code";
this.description = "Extract information from a JSON object with a JPath query.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Query",
"type": "string",
"value": ""
},
{
"name": "Result delimiter",
"type": "binaryShortString",
"value": "\\n"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [query, delimiter] = args;
let results,
obj;
try {
obj = JSON.parse(input);
} catch (err) {
throw new OperationError(`Invalid input JSON: ${err.message}`);
}
try {
results = jpath.query(obj, query);
} catch (err) {
throw new OperationError(`Invalid JPath expression: ${err.message}`);
}
return results.map(result => JSON.stringify(result)).join(delimiter);
}
}
export default JPathExpression;

View File

@ -0,0 +1,48 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import vkbeautify from "vkbeautify";
import Operation from "../Operation";
/**
* JSON Beautify operation
*/
class JSONBeautify extends Operation {
/**
* JSONBeautify constructor
*/
constructor() {
super();
this.name = "JSON Beautify";
this.module = "Code";
this.description = "Indents and prettifies JavaScript Object Notation (JSON) code.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Indent string",
"type": "binaryShortString",
"value": "\\t"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const indentStr = args[0];
if (!input) return "";
return vkbeautify.json(input, indentStr);
}
}
export default JSONBeautify;

View File

@ -0,0 +1,41 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import vkbeautify from "vkbeautify";
import Operation from "../Operation";
/**
* JSON Minify operation
*/
class JSONMinify extends Operation {
/**
* JSONMinify constructor
*/
constructor() {
super();
this.name = "JSON Minify";
this.module = "Code";
this.description = "Compresses JavaScript Object Notation (JSON) code.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!input) return "";
return vkbeautify.jsonmin(input);
}
}
export default JSONMinify;

View File

@ -25,7 +25,8 @@ class Magic extends Operation {
this.module = "Default";
this.description = "The Magic operation attempts to detect various properties of the input data and suggests which operations could help to make more sense of it.<br><br><b>Options</b><br><u>Depth:</u> If an operation appears to match the data, it will be run and the result will be analysed further. This argument controls the maximum number of levels of recursion.<br><br><u>Intensive mode:</u> When this is turned on, various operations like XOR, bit rotates, and character encodings are brute-forced to attempt to detect valid data underneath. To improve performance, only the first 100 bytes of the data is brute-forced.<br><br><u>Extensive language support:</u> At each stage, the relative byte frequencies of the data will be compared to average frequencies for a number of languages. The default set consists of ~40 of the most commonly used languages on the Internet. The extensive list consists of 284 languages and can result in many languages matching the data if their byte frequencies are similar.";
this.inputType = "ArrayBuffer";
this.outputType = "html";
this.outputType = "JSON";
this.presentType = "html";
this.args = [
{
"name": "Depth",
@ -56,10 +57,25 @@ class Magic extends Operation {
const ings = state.opList[state.progress].ingValues,
[depth, intensive, extLang] = ings,
dish = state.dish,
currentRecipeConfig = state.opList.map(op => op.config),
magic = new MagicLib(await dish.get(Dish.ARRAY_BUFFER)),
options = await magic.speculativeExecution(depth, extLang, intensive);
// Record the current state for use when presenting
this.state = state;
dish.set(options, Dish.JSON);
return state;
}
/**
* Displays Magic results in HTML for web apps.
*
* @param {JSON} options
* @returns {html}
*/
present(options) {
const currentRecipeConfig = this.state.opList.map(op => op.config);
let output = `<table
class='table table-hover table-condensed table-bordered'
style='table-layout: fixed;'>
@ -84,9 +100,9 @@ class Magic extends Operation {
options.forEach(option => {
// Construct recipe URL
// Replace this Magic op with the generated recipe
const recipeConfig = currentRecipeConfig.slice(0, state.progress)
const recipeConfig = currentRecipeConfig.slice(0, this.state.progress)
.concat(option.recipe)
.concat(currentRecipeConfig.slice(state.progress + 1)),
.concat(currentRecipeConfig.slice(this.state.progress + 1)),
recipeURL = "recipe=" + Utils.encodeURIFragment(Utils.generatePrettyRecipe(recipeConfig));
let language = "",
@ -131,8 +147,8 @@ class Magic extends Operation {
if (!options.length) {
output = "Nothing of interest could be detected about the input data.\nHave you tried modifying the operation arguments?";
}
dish.set(output, Dish.HTML);
return state;
return output;
}
}

View File

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import r from "jsrsasign";
import Operation from "../Operation";
/**
* Object Identifier to Hex operation
*/
class ObjectIdentifierToHex extends Operation {
/**
* ObjectIdentifierToHex constructor
*/
constructor() {
super();
this.name = "Object Identifier to Hex";
this.module = "PublicKey";
this.description = "Converts an object identifier (OID) into a hexadecimal string.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return r.KJUR.asn1.ASN1Util.oidIntToHex(input);
}
}
export default ObjectIdentifierToHex;

View File

@ -0,0 +1,50 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import r from "jsrsasign";
import Operation from "../Operation";
/**
* PEM to Hex operation
*/
class PEMToHex extends Operation {
/**
* PEMToHex constructor
*/
constructor() {
super();
this.name = "PEM to Hex";
this.module = "PublicKey";
this.description = "Converts PEM (Privacy Enhanced Mail) format to a hexadecimal DER (Distinguished Encoding Rules) string.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (input.indexOf("-----BEGIN") < 0) {
// Add header so that the KEYUTIL function works
input = "-----BEGIN CERTIFICATE-----" + input;
}
if (input.indexOf("-----END") < 0) {
// Add footer so that the KEYUTIL function works
input = input + "-----END CERTIFICATE-----";
}
const cert = new r.X509();
cert.readCertPEM(input);
return cert.hex;
}
}
export default PEMToHex;

View File

@ -8,8 +8,8 @@ import Operation from "../Operation";
import kbpgp from "kbpgp";
import { ASP, importPrivateKey } from "../lib/PGP";
import OperationError from "../errors/OperationError";
import promisifyDefault from "es6-promisify";
const promisify = promisifyDefault.promisify;
import * as es6promisify from "es6-promisify";
const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
/**
* PGP Decrypt operation

View File

@ -8,8 +8,8 @@ import Operation from "../Operation";
import kbpgp from "kbpgp";
import { ASP, importPrivateKey, importPublicKey } from "../lib/PGP";
import OperationError from "../errors/OperationError";
import promisifyDefault from "es6-promisify";
const promisify = promisifyDefault.promisify;
import * as es6promisify from "es6-promisify";
const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
/**
* PGP Decrypt and Verify operation

View File

@ -6,10 +6,10 @@
import Operation from "../Operation";
import kbpgp from "kbpgp";
import { ASP } from "../lib/PGP";
import { ASP, importPublicKey } from "../lib/PGP";
import OperationError from "../errors/OperationError";
import promisifyDefault from "es6-promisify";
const promisify = promisifyDefault.promisify;
import * as es6promisify from "es6-promisify";
const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
/**
* PGP Encrypt operation
@ -54,18 +54,11 @@ class PGPEncrypt extends Operation {
async run(input, args) {
const plaintextMessage = input,
plainPubKey = args[0];
let key,
encryptedMessage;
let encryptedMessage;
if (!plainPubKey) throw new OperationError("Enter the public key of the recipient.");
try {
key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
armored: plainPubKey,
});
} catch (err) {
throw new OperationError(`Could not import public key: ${err}`);
}
const key = await importPublicKey(plainPubKey);
try {
encryptedMessage = await promisify(kbpgp.box)({

View File

@ -8,8 +8,8 @@ import Operation from "../Operation";
import kbpgp from "kbpgp";
import { ASP, importPrivateKey, importPublicKey } from "../lib/PGP";
import OperationError from "../errors/OperationError";
import promisifyDefault from "es6-promisify";
const promisify = promisifyDefault.promisify;
import * as es6promisify from "es6-promisify";
const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
/**
* PGP Encrypt and Sign operation

View File

@ -0,0 +1,54 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import r from "jsrsasign";
import Operation from "../Operation";
/**
* Parse ASN.1 hex string operation
*/
class ParseASN1HexString extends Operation {
/**
* ParseASN1HexString constructor
*/
constructor() {
super();
this.name = "Parse ASN.1 hex string";
this.module = "PublicKey";
this.description = "Abstract Syntax Notation One (ASN.1) is a standard and notation that describes rules and structures for representing, encoding, transmitting, and decoding data in telecommunications and computer networking.<br><br>This operation parses arbitrary ASN.1 data and presents the resulting tree.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Starting index",
"type": "number",
"value": 0
},
{
"name": "Truncate octet strings longer than",
"type": "number",
"value": 32
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [index, truncateLen] = args;
return r.ASN1HEX.dump(input.replace(/\s/g, ""), {
"ommitLongOctet": truncateLen
}, index);
}
}
export default ParseASN1HexString;

View File

@ -0,0 +1,81 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import {ipv4CidrRange, ipv4HyphenatedRange, ipv6CidrRange, ipv6HyphenatedRange} from "../lib/IP";
/**
* Parse IP range operation
*/
class ParseIPRange extends Operation {
/**
* ParseIPRange constructor
*/
constructor() {
super();
this.name = "Parse IP range";
this.module = "JSBN";
this.description = "Given a CIDR range (e.g. <code>10.0.0.0/24</code>) or a hyphenated range (e.g. <code>10.0.0.0 - 10.0.1.0</code>), this operation provides network information and enumerates all IP addresses in the range.<br><br>IPv6 is supported but will not be enumerated.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Include network info",
"type": "boolean",
"value": true
},
{
"name": "Enumerate IP addresses",
"type": "boolean",
"value": true
},
{
"name": "Allow large queries",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [
includeNetworkInfo,
enumerateAddresses,
allowLargeList
] = args;
// Check what type of input we are looking at
const ipv4CidrRegex = /^\s*((?:\d{1,3}\.){3}\d{1,3})\/(\d\d?)\s*$/,
ipv4RangeRegex = /^\s*((?:\d{1,3}\.){3}\d{1,3})\s*-\s*((?:\d{1,3}\.){3}\d{1,3})\s*$/,
ipv6CidrRegex = /^\s*(((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\4)::|:\b|(?![\dA-F])))|(?!\3\4)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4}))\/(\d\d?\d?)\s*$/i,
ipv6RangeRegex = /^\s*(((?=.*::)(?!.*::[^-]+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\4)::|:\b|(?![\dA-F])))|(?!\3\4)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4}))\s*-\s*(((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\17)::|:\b|(?![\dA-F])))|(?!\16\17)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4}))\s*$/i;
let match;
if ((match = ipv4CidrRegex.exec(input))) {
return ipv4CidrRange(match, includeNetworkInfo, enumerateAddresses, allowLargeList);
} else if ((match = ipv4RangeRegex.exec(input))) {
return ipv4HyphenatedRange(match, includeNetworkInfo, enumerateAddresses, allowLargeList);
} else if ((match = ipv6CidrRegex.exec(input))) {
return ipv6CidrRange(match, includeNetworkInfo);
} else if ((match = ipv6RangeRegex.exec(input))) {
return ipv6HyphenatedRange(match, includeNetworkInfo);
} else {
throw new OperationError("Invalid input.\n\nEnter either a CIDR range (e.g. 10.0.0.0/24) or a hyphenated range (e.g. 10.0.0.0 - 10.0.1.0). IPv6 also supported.");
}
}
}
export default ParseIPRange;

View File

@ -0,0 +1,129 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import {fromHex, toHex} from "../lib/Hex";
import {ipv4ToStr, protocolLookup} from "../lib/IP";
import TCPIPChecksum from "./TCPIPChecksum";
/**
* Parse IPv4 header operation
*/
class ParseIPv4Header extends Operation {
/**
* ParseIPv4Header constructor
*/
constructor() {
super();
this.name = "Parse IPv4 header";
this.module = "JSBN";
this.description = "Given an IPv4 header, this operations parses and displays each field in an easily readable format.";
this.inputType = "string";
this.outputType = "html";
this.args = [
{
"name": "Input format",
"type": "option",
"value": ["Hex", "Raw"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
run(input, args) {
const format = args[0];
let output;
if (format === "Hex") {
input = fromHex(input);
} else if (format === "Raw") {
input = Utils.strToByteArray(input);
} else {
throw new OperationError("Unrecognised input format.");
}
let ihl = input[0] & 0x0f;
const dscp = (input[1] >>> 2) & 0x3f,
ecn = input[1] & 0x03,
length = input[2] << 8 | input[3],
identification = input[4] << 8 | input[5],
flags = (input[6] >>> 5) & 0x07,
fragOffset = (input[6] & 0x1f) << 8 | input[7],
ttl = input[8],
protocol = input[9],
checksum = input[10] << 8 | input[11],
srcIP = input[12] << 24 | input[13] << 16 | input[14] << 8 | input[15],
dstIP = input[16] << 24 | input[17] << 16 | input[18] << 8 | input[19],
checksumHeader = input.slice(0, 10).concat([0, 0]).concat(input.slice(12, 20));
let version = (input[0] >>> 4) & 0x0f,
options = [];
// Version
if (version !== 4) {
version = version + " (Error: for IPv4 headers, this should always be set to 4)";
}
// IHL
if (ihl < 5) {
ihl = ihl + " (Error: this should always be at least 5)";
} else if (ihl > 5) {
// sort out options...
const optionsLen = ihl * 4 - 20;
options = input.slice(20, optionsLen + 20);
}
// Protocol
const protocolInfo = protocolLookup[protocol] || {keyword: "", protocol: ""};
// Checksum
const correctChecksum = (new TCPIPChecksum).run(checksumHeader),
givenChecksum = Utils.hex(checksum);
let checksumResult;
if (correctChecksum === givenChecksum) {
checksumResult = givenChecksum + " (correct)";
} else {
checksumResult = givenChecksum + " (incorrect, should be " + correctChecksum + ")";
}
output = `<table class='table table-hover table-condensed table-bordered table-nonfluid'><tr><th>Field</th><th>Value</th></tr>
<tr><td>Version</td><td>${version}</td></tr>
<tr><td>Internet Header Length (IHL)</td><td>${ihl} (${ihl * 4} bytes)</td></tr>
<tr><td>Differentiated Services Code Point (DSCP)</td><td>${dscp}</td></tr>
<tr><td>Explicit Congestion Notification (ECN)</td><td>${ecn}</td></tr>
<tr><td>Total length</td><td>${length} bytes
IP header: ${ihl * 4} bytes
Data: ${length - ihl * 4} bytes</td></tr>
<tr><td>Identification</td><td>0x${Utils.hex(identification)} (${identification})</td></tr>
<tr><td>Flags</td><td>0x${Utils.hex(flags, 2)}
Reserved bit:${flags >> 2} (must be 0)
Don't fragment:${flags >> 1 & 1}
More fragments:${flags & 1}</td></tr>
<tr><td>Fragment offset</td><td>${fragOffset}</td></tr>
<tr><td>Time-To-Live</td><td>${ttl}</td></tr>
<tr><td>Protocol</td><td>${protocol}, ${protocolInfo.protocol} (${protocolInfo.keyword})</td></tr>
<tr><td>Header checksum</td><td>${checksumResult}</td></tr>
<tr><td>Source IP address</td><td>${ipv4ToStr(srcIP)}</td></tr>
<tr><td>Destination IP address</td><td>${ipv4ToStr(dstIP)}</td></tr>`;
if (ihl > 5) {
output += `<tr><td>Options</td><td>${toHex(options)}</td></tr>`;
}
return output + "</table>";
}
}
export default ParseIPv4Header;

View File

@ -0,0 +1,192 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import {strToIpv6, ipv6ToStr, ipv4ToStr, IPV6_REGEX} from "../lib/IP";
import BigInteger from "jsbn";
/**
* Parse IPv6 address operation
*/
class ParseIPv6Address extends Operation {
/**
* ParseIPv6Address constructor
*/
constructor() {
super();
this.name = "Parse IPv6 address";
this.module = "JSBN";
this.description = "Displays the longhand and shorthand versions of a valid IPv6 address.<br><br>Recognises all reserved ranges and parses encapsulated or tunnelled addresses including Teredo and 6to4.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let match,
output = "";
if ((match = IPV6_REGEX.exec(input))) {
const ipv6 = strToIpv6(match[1]),
longhand = ipv6ToStr(ipv6),
shorthand = ipv6ToStr(ipv6, true);
output += "Longhand: " + longhand + "\nShorthand: " + shorthand + "\n";
// Detect reserved addresses
if (shorthand === "::") {
// Unspecified address
output += "\nUnspecified address corresponding to 0.0.0.0/32 in IPv4.";
output += "\nUnspecified address range: ::/128";
} else if (shorthand === "::1") {
// Loopback address
output += "\nLoopback address to the local host corresponding to 127.0.0.1/8 in IPv4.";
output += "\nLoopback addresses range: ::1/128";
} else if (ipv6[0] === 0 && ipv6[1] === 0 && ipv6[2] === 0 &&
ipv6[3] === 0 && ipv6[4] === 0 && ipv6[5] === 0xffff) {
// IPv4-mapped IPv6 address
output += "\nIPv4-mapped IPv6 address detected. IPv6 clients will be handled natively by default, and IPv4 clients appear as IPv6 clients at their IPv4-mapped IPv6 address.";
output += "\nMapped IPv4 address: " + ipv4ToStr((ipv6[6] << 16) + ipv6[7]);
output += "\nIPv4-mapped IPv6 addresses range: ::ffff:0:0/96";
} else if (ipv6[0] === 0 && ipv6[1] === 0 && ipv6[2] === 0 &&
ipv6[3] === 0 && ipv6[4] === 0xffff && ipv6[5] === 0) {
// IPv4-translated address
output += "\nIPv4-translated address detected. Used by Stateless IP/ICMP Translation (SIIT). See RFCs 6145 and 6052 for more details.";
output += "\nTranslated IPv4 address: " + ipv4ToStr((ipv6[6] << 16) + ipv6[7]);
output += "\nIPv4-translated addresses range: ::ffff:0:0:0/96";
} else if (ipv6[0] === 0x100) {
// Discard prefix per RFC 6666
output += "\nDiscard prefix detected. This is used when forwarding traffic to a sinkhole router to mitigate the effects of a denial-of-service attack. See RFC 6666 for more details.";
output += "\nDiscard range: 100::/64";
} else if (ipv6[0] === 0x64 && ipv6[1] === 0xff9b && ipv6[2] === 0 &&
ipv6[3] === 0 && ipv6[4] === 0 && ipv6[5] === 0) {
// IPv4/IPv6 translation per RFC 6052
output += "\n'Well-Known' prefix for IPv4/IPv6 translation detected. See RFC 6052 for more details.";
output += "\nTranslated IPv4 address: " + ipv4ToStr((ipv6[6] << 16) + ipv6[7]);
output += "\n'Well-Known' prefix range: 64:ff9b::/96";
} else if (ipv6[0] === 0x2001 && ipv6[1] === 0) {
// Teredo tunneling
output += "\nTeredo tunneling IPv6 address detected\n";
const serverIpv4 = (ipv6[2] << 16) + ipv6[3],
udpPort = (~ipv6[5]) & 0xffff,
clientIpv4 = ~((ipv6[6] << 16) + ipv6[7]),
flagCone = (ipv6[4] >>> 15) & 1,
flagR = (ipv6[4] >>> 14) & 1,
flagRandom1 = (ipv6[4] >>> 10) & 15,
flagUg = (ipv6[4] >>> 8) & 3,
flagRandom2 = ipv6[4] & 255;
output += "\nServer IPv4 address: " + ipv4ToStr(serverIpv4) +
"\nClient IPv4 address: " + ipv4ToStr(clientIpv4) +
"\nClient UDP port: " + udpPort +
"\nFlags:" +
"\n\tCone: " + flagCone;
if (flagCone) {
output += " (Client is behind a cone NAT)";
} else {
output += " (Client is not behind a cone NAT)";
}
output += "\n\tR: " + flagR;
if (flagR) {
output += " Error: This flag should be set to 0. See RFC 5991 and RFC 4380.";
}
output += "\n\tRandom1: " + Utils.bin(flagRandom1, 4) +
"\n\tUG: " + Utils.bin(flagUg, 2);
if (flagUg) {
output += " Error: This flag should be set to 00. See RFC 4380.";
}
output += "\n\tRandom2: " + Utils.bin(flagRandom2, 8);
if (!flagR && !flagUg && flagRandom1 && flagRandom2) {
output += "\n\nThis is a valid Teredo address which complies with RFC 4380 and RFC 5991.";
} else if (!flagR && !flagUg) {
output += "\n\nThis is a valid Teredo address which complies with RFC 4380, however it does not comply with RFC 5991 (Teredo Security Updates) as there are no randomised bits in the flag field.";
} else {
output += "\n\nThis is an invalid Teredo address.";
}
output += "\n\nTeredo prefix range: 2001::/32";
} else if (ipv6[0] === 0x2001 && ipv6[1] === 0x2 && ipv6[2] === 0) {
// Benchmarking
output += "\nAssigned to the Benchmarking Methodology Working Group (BMWG) for benchmarking IPv6. Corresponds to 198.18.0.0/15 for benchmarking IPv4. See RFC 5180 for more details.";
output += "\nBMWG range: 2001:2::/48";
} else if (ipv6[0] === 0x2001 && ipv6[1] >= 0x10 && ipv6[1] <= 0x1f) {
// ORCHIDv1
output += "\nDeprecated, previously ORCHIDv1 (Overlay Routable Cryptographic Hash Identifiers).\nORCHIDv1 range: 2001:10::/28\nORCHIDv2 now uses 2001:20::/28.";
} else if (ipv6[0] === 0x2001 && ipv6[1] >= 0x20 && ipv6[1] <= 0x2f) {
// ORCHIDv2
output += "\nORCHIDv2 (Overlay Routable Cryptographic Hash Identifiers).\nThese are non-routed IPv6 addresses used for Cryptographic Hash Identifiers.";
output += "\nORCHIDv2 range: 2001:20::/28";
} else if (ipv6[0] === 0x2001 && ipv6[1] === 0xdb8) {
// Documentation
output += "\nThis is a documentation IPv6 address. This range should be used whenever an example IPv6 address is given or to model networking scenarios. Corresponds to 192.0.2.0/24, 198.51.100.0/24, and 203.0.113.0/24 in IPv4.";
output += "\nDocumentation range: 2001:db8::/32";
} else if (ipv6[0] === 0x2002) {
// 6to4
output += "\n6to4 transition IPv6 address detected. See RFC 3056 for more details." +
"\n6to4 prefix range: 2002::/16";
const v4Addr = ipv4ToStr((ipv6[1] << 16) + ipv6[2]),
slaId = ipv6[3],
interfaceIdStr = ipv6[4].toString(16) + ipv6[5].toString(16) + ipv6[6].toString(16) + ipv6[7].toString(16),
interfaceId = new BigInteger(interfaceIdStr, 16);
output += "\n\nEncapsulated IPv4 address: " + v4Addr +
"\nSLA ID: " + slaId +
"\nInterface ID (base 16): " + interfaceIdStr +
"\nInterface ID (base 10): " + interfaceId.toString();
} else if (ipv6[0] >= 0xfc00 && ipv6[0] <= 0xfdff) {
// Unique local address
output += "\nThis is a unique local address comparable to the IPv4 private addresses 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16. See RFC 4193 for more details.";
output += "\nUnique local addresses range: fc00::/7";
} else if (ipv6[0] >= 0xfe80 && ipv6[0] <= 0xfebf) {
// Link-local address
output += "\nThis is a link-local address comparable to the auto-configuration addresses 169.254.0.0/16 in IPv4.";
output += "\nLink-local addresses range: fe80::/10";
} else if (ipv6[0] >= 0xff00) {
// Multicast
output += "\nThis is a reserved multicast address.";
output += "\nMulticast addresses range: ff00::/8";
}
// Detect possible EUI-64 addresses
if ((ipv6[5] & 0xff === 0xff) && (ipv6[6] >>> 8 === 0xfe)) {
output += "\n\nThis IPv6 address contains a modified EUI-64 address, identified by the presence of FF:FE in the 12th and 13th octets.";
const intIdent = Utils.hex(ipv6[4] >>> 8) + ":" + Utils.hex(ipv6[4] & 0xff) + ":" +
Utils.hex(ipv6[5] >>> 8) + ":" + Utils.hex(ipv6[5] & 0xff) + ":" +
Utils.hex(ipv6[6] >>> 8) + ":" + Utils.hex(ipv6[6] & 0xff) + ":" +
Utils.hex(ipv6[7] >>> 8) + ":" + Utils.hex(ipv6[7] & 0xff),
mac = Utils.hex((ipv6[4] >>> 8) ^ 2) + ":" + Utils.hex(ipv6[4] & 0xff) + ":" +
Utils.hex(ipv6[5] >>> 8) + ":" + Utils.hex(ipv6[6] & 0xff) + ":" +
Utils.hex(ipv6[7] >>> 8) + ":" + Utils.hex(ipv6[7] & 0xff);
output += "\nInterface identifier: " + intIdent +
"\nMAC address: " + mac;
}
} else {
throw new OperationError("Invalid IPv6 address");
}
return output;
}
}
export default ParseIPv6Address;

View File

@ -0,0 +1,216 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import r from "jsrsasign";
import { fromBase64 } from "../lib/Base64";
import { toHex } from "../lib/Hex";
import { formatByteStr, formatDnStr } from "../lib/PublicKey";
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Parse X.509 certificate operation
*/
class ParseX509Certificate extends Operation {
/**
* ParseX509Certificate constructor
*/
constructor() {
super();
this.name = "Parse X.509 certificate";
this.module = "PublicKey";
this.description = "X.509 is an ITU-T standard for a public key infrastructure (PKI) and Privilege Management Infrastructure (PMI). It is commonly involved with SSL/TLS security.<br><br>This operation displays the contents of a certificate in a human readable format, similar to the openssl command line tool.<br><br>Tags: X509, server hello, handshake";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Input format",
"type": "option",
"value": ["PEM", "DER Hex", "Base64", "Raw"]
}
];
this.patterns = [
{
"match": "^-+BEGIN CERTIFICATE-+\\r?\\n[\\da-z+/\\n\\r]+-+END CERTIFICATE-+\\r?\\n?$",
"flags": "i",
"args": [
"PEM"
]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!input.length) {
return "No input";
}
const cert = new r.X509(),
inputFormat = args[0];
switch (inputFormat) {
case "DER Hex":
input = input.replace(/\s/g, "");
cert.readCertHex(input);
break;
case "PEM":
cert.readCertPEM(input);
break;
case "Base64":
cert.readCertHex(toHex(fromBase64(input, null, "byteArray"), ""));
break;
case "Raw":
cert.readCertHex(toHex(Utils.strToByteArray(input), ""));
break;
default:
throw "Undefined input format";
}
const sn = cert.getSerialNumberHex(),
issuer = cert.getIssuerString(),
subject = cert.getSubjectString(),
pk = cert.getPublicKey(),
pkFields = [],
sig = cert.getSignatureValueHex();
let pkStr = "",
sigStr = "",
extensions = "";
// Public Key fields
pkFields.push({
key: "Algorithm",
value: pk.type
});
if (pk.type === "EC") { // ECDSA
pkFields.push({
key: "Curve Name",
value: pk.curveName
});
pkFields.push({
key: "Length",
value: (((new r.BigInteger(pk.pubKeyHex, 16)).bitLength()-3) /2) + " bits"
});
pkFields.push({
key: "pub",
value: formatByteStr(pk.pubKeyHex, 16, 18)
});
} else if (pk.type === "DSA") { // DSA
pkFields.push({
key: "pub",
value: formatByteStr(pk.y.toString(16), 16, 18)
});
pkFields.push({
key: "P",
value: formatByteStr(pk.p.toString(16), 16, 18)
});
pkFields.push({
key: "Q",
value: formatByteStr(pk.q.toString(16), 16, 18)
});
pkFields.push({
key: "G",
value: formatByteStr(pk.g.toString(16), 16, 18)
});
} else if (pk.e) { // RSA
pkFields.push({
key: "Length",
value: pk.n.bitLength() + " bits"
});
pkFields.push({
key: "Modulus",
value: formatByteStr(pk.n.toString(16), 16, 18)
});
pkFields.push({
key: "Exponent",
value: pk.e + " (0x" + pk.e.toString(16) + ")"
});
} else {
pkFields.push({
key: "Error",
value: "Unknown Public Key type"
});
}
// Format Public Key fields
for (let i = 0; i < pkFields.length; i++) {
pkStr += ` ${pkFields[i].key}:${(pkFields[i].value + "\n").padStart(
18 - (pkFields[i].key.length + 3) + pkFields[i].value.length + 1,
" "
)}`;
}
// Signature fields
let breakoutSig = false;
try {
breakoutSig = r.ASN1HEX.dump(sig).indexOf("SEQUENCE") === 0;
} catch (err) {
// Error processing signature, output without further breakout
}
if (breakoutSig) { // DSA or ECDSA
sigStr = ` r: ${formatByteStr(r.ASN1HEX.getV(sig, 4), 16, 18)}
s: ${formatByteStr(r.ASN1HEX.getV(sig, 48), 16, 18)}`;
} else { // RSA or unknown
sigStr = ` Signature: ${formatByteStr(sig, 16, 18)}`;
}
// Extensions
try {
extensions = cert.getInfo().split("X509v3 Extensions:\n")[1].split("signature")[0];
} catch (err) {}
const issuerStr = formatDnStr(issuer, 2),
nbDate = formatDate(cert.getNotBefore()),
naDate = formatDate(cert.getNotAfter()),
subjectStr = formatDnStr(subject, 2);
return `Version: ${cert.version} (0x${Utils.hex(cert.version - 1)})
Serial number: ${new r.BigInteger(sn, 16).toString()} (0x${sn})
Algorithm ID: ${cert.getSignatureAlgorithmField()}
Validity
Not Before: ${nbDate} (dd-mm-yy hh:mm:ss) (${cert.getNotBefore()})
Not After: ${naDate} (dd-mm-yy hh:mm:ss) (${cert.getNotAfter()})
Issuer
${issuerStr}
Subject
${subjectStr}
Public Key
${pkStr.slice(0, -1)}
Certificate Signature
Algorithm: ${cert.getSignatureAlgorithmName()}
${sigStr}
Extensions
${extensions}`;
}
}
/**
* Formats dates.
*
* @param {string} dateStr
* @returns {string}
*/
function formatDate (dateStr) {
return dateStr[4] + dateStr[5] + "/" +
dateStr[2] + dateStr[3] + "/" +
dateStr[0] + dateStr[1] + " " +
dateStr[6] + dateStr[7] + ":" +
dateStr[8] + dateStr[9] + ":" +
dateStr[10] + dateStr[11];
}
export default ParseX509Certificate;

View File

@ -0,0 +1,262 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import XRegExp from "xregexp";
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
/**
* Regular expression operation
*/
class RegularExpression extends Operation {
/**
* RegularExpression constructor
*/
constructor() {
super();
this.name = "Regular expression";
this.module = "Regex";
this.description = "Define your own regular expression (regex) to search the input data with, optionally choosing from a list of pre-defined patterns.<br><br>Supports extended regex syntax including the 'dot matches all' flag, named capture groups, full unicode coverage (including <code>\\p{}</code> categories and scripts as well as astral codes) and recursive matching.";
this.inputType = "string";
this.outputType = "html";
this.args = [
{
"name": "Built in regexes",
"type": "populateOption",
"value": [
{
name: "User defined",
value: ""
},
{
name: "IPv4 address",
value: "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?"
},
{
name: "IPv6 address",
value: "((?=.*::)(?!.*::.+::)(::)?([\\dA-Fa-f]{1,4}:(:|\\b)|){5}|([\\dA-Fa-f]{1,4}:){6})((([\\dA-Fa-f]{1,4}((?!\\3)::|:\\b|(?![\\dA-Fa-f])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})"
},
{
name: "Email address",
value: "\\b(\\w[-.\\w]*)@([-\\w]+(?:\\.[-\\w]+)*)\\.([A-Za-z]{2,4})\\b"
},
{
name: "URL",
value: "([A-Za-z]+://)([-\\w]+(?:\\.\\w[-\\w]*)+)(:\\d+)?(/[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]*(?:[.!,?]+[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]+)*)?"
},
{
name: "Domain",
value: "\\b((?=[a-z0-9-]{1,63}\\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,63}\\b"
},
{
name: "Windows file path",
value: "([A-Za-z]):\\\\((?:[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)~]{0,61}\\\\?)*[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)]{0,61})(\\.[A-Za-z\\d]{1,6})?"
},
{
name: "UNIX file path",
value: "(?:/[A-Za-z\\d.][A-Za-z\\d\\-.]{0,61})+"
},
{
name: "MAC address",
value: "[A-Fa-f\\d]{2}(?:[:-][A-Fa-f\\d]{2}){5}"
},
{
name: "Date (yyyy-mm-dd)",
value: "((?:19|20)\\d\\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])"
},
{
name: "Date (dd/mm/yyyy)",
value: "(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.]((?:19|20)\\d\\d)"
},
{
name: "Date (mm/dd/yyyy)",
value: "(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.]((?:19|20)\\d\\d)"
},
{
name: "Strings",
value: "[A-Za-z\\d/\\-:.,_$%\\x27\"()<>= !\\[\\]{}@]{4,}"
},
],
"target": 1
},
{
"name": "Regex",
"type": "text",
"value": ""
},
{
"name": "Case insensitive",
"type": "boolean",
"value": true
},
{
"name": "^ and $ match at newlines",
"type": "boolean",
"value": true
},
{
"name": "Dot matches all",
"type": "boolean",
"value": false
},
{
"name": "Unicode support",
"type": "boolean",
"value": false
},
{
"name": "Astral support",
"type": "boolean",
"value": false
},
{
"name": "Display total",
"type": "boolean",
"value": false
},
{
"name": "Output format",
"type": "option",
"value": ["Highlight matches", "List matches", "List capture groups", "List matches with capture groups"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
run(input, args) {
const [,
userRegex,
i, m, s, u, a,
displayTotal,
outputFormat
] = args;
let modifiers = "g";
if (i) modifiers += "i";
if (m) modifiers += "m";
if (s) modifiers += "s";
if (u) modifiers += "u";
if (a) modifiers += "A";
if (userRegex && userRegex !== "^" && userRegex !== "$") {
try {
const regex = new XRegExp(userRegex, modifiers);
switch (outputFormat) {
case "Highlight matches":
return regexHighlight(input, regex, displayTotal);
case "List matches":
return Utils.escapeHtml(regexList(input, regex, displayTotal, true, false));
case "List capture groups":
return Utils.escapeHtml(regexList(input, regex, displayTotal, false, true));
case "List matches with capture groups":
return Utils.escapeHtml(regexList(input, regex, displayTotal, true, true));
default:
return "Error: Invalid output format";
}
} catch (err) {
throw new OperationError("Invalid regex. Details: " + err.message);
}
} else {
return Utils.escapeHtml(input);
}
}
}
/**
* Creates a string listing the matches within a string.
*
* @param {string} input
* @param {RegExp} regex
* @param {boolean} displayTotal
* @param {boolean} matches - Display full match
* @param {boolean} captureGroups - Display each of the capture groups separately
* @returns {string}
*/
function regexList (input, regex, displayTotal, matches, captureGroups) {
let output = "",
total = 0,
match;
while ((match = regex.exec(input))) {
// Moves pointer when an empty string is matched (prevents infinite loop)
if (match.index === regex.lastIndex) {
regex.lastIndex++;
}
total++;
if (matches) {
output += match[0] + "\n";
}
if (captureGroups) {
for (let i = 1; i < match.length; i++) {
if (matches) {
output += " Group " + i + ": ";
}
output += match[i] + "\n";
}
}
}
if (displayTotal)
output = "Total found: " + total + "\n\n" + output;
return output.slice(0, -1);
}
/**
* Adds HTML highlights to matches within a string.
*
* @private
* @param {string} input
* @param {RegExp} regex
* @param {boolean} displayTotal
* @returns {string}
*/
function regexHighlight (input, regex, displayTotal) {
let output = "",
m,
hl = 1,
i = 0,
total = 0;
while ((m = regex.exec(input))) {
// Moves pointer when an empty string is matched (prevents infinite loop)
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
// Add up to match
output += Utils.escapeHtml(input.slice(i, m.index));
// Add match with highlighting
output += "<span class='hl"+hl+"'>" + Utils.escapeHtml(m[0]) + "</span>";
// Switch highlight
hl = hl === 1 ? 2 : 1;
i = regex.lastIndex;
total++;
}
// Add all after final match
output += Utils.escapeHtml(input.slice(i, input.length));
if (displayTotal)
output = "Total found: " + total + "\n\n" + output;
return output;
}
export default RegularExpression;

View File

@ -0,0 +1,54 @@
/**
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import { removeEXIF } from "../vendor/remove-exif";
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
/**
* Remove EXIF operation
*/
class RemoveEXIF extends Operation {
/**
* RemoveEXIF constructor
*/
constructor() {
super();
this.name = "Remove EXIF";
this.module = "Image";
this.description = [
"Removes EXIF data from a JPEG image.",
"<br><br>",
"EXIF data embedded in photos usually contains information about the image file itself as well as the device used to create it.",
].join("\n");
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
// Do nothing if input is empty
if (input.length === 0) return input;
try {
return removeEXIF(input);
} catch (err) {
// Simply return input if no EXIF data is found
if (err === "Exif not found.") return input;
throw new OperationError(`Could not remove EXIF data from image: ${err}`);
}
}
}
export default RemoveEXIF;

View File

@ -0,0 +1,90 @@
/**
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import { fromBase64, toBase64 } from "../lib/Base64";
import { fromHex } from "../lib/Hex";
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Utils from "../Utils";
import Magic from "../lib/Magic";
/**
* Render Image operation
*/
class RenderImage extends Operation {
/**
* RenderImage constructor
*/
constructor() {
super();
this.name = "Render Image";
this.module = "Image";
this.description = "Displays the input as an image. Supports the following formats:<br><br><ul><li>jpg/jpeg</li><li>png</li><li>gif</li><li>webp</li><li>bmp</li><li>ico</li></ul>";
this.inputType = "string";
this.outputType = "html";
this.args = [
{
"name": "Input format",
"type": "option",
"value": ["Raw", "Base64", "Hex"]
}
];
this.patterns = [
{
"match": "^(?:\\xff\\xd8\\xff|\\x89\\x50\\x4e\\x47|\\x47\\x49\\x46|.{8}\\x57\\x45\\x42\\x50|\\x42\\x4d)",
"flags": "",
"args": ["Raw"],
"useful": true
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
run(input, args) {
const inputFormat = args[0];
let dataURI = "data:";
if (!input.length) return "";
// Convert input to raw bytes
switch (inputFormat) {
case "Hex":
input = fromHex(input);
break;
case "Base64":
// Don't trust the Base64 entered by the user.
// Unwrap it first, then re-encode later.
input = fromBase64(input, undefined, "byteArray");
break;
case "Raw":
default:
input = Utils.strToByteArray(input);
break;
}
// Determine file type
const type = Magic.magicFileType(input);
if (type && type.mime.indexOf("image") === 0) {
dataURI += type.mime + ";";
} else {
throw new OperationError("Invalid file type");
}
// Add image data to URI
dataURI += "base64," + toBase64(input);
return "<img src='" + dataURI + "'>";
}
}
export default RenderImage;

View File

@ -0,0 +1,47 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import vkbeautify from "vkbeautify";
import Operation from "../Operation";
/**
* SQL Beautify operation
*/
class SQLBeautify extends Operation {
/**
* SQLBeautify constructor
*/
constructor() {
super();
this.name = "SQL Beautify";
this.module = "Code";
this.description = "Indents and prettifies Structured Query Language (SQL) code.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Indent string",
"type": "binaryShortString",
"value": "\\t"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const indentStr = args[0];
return vkbeautify.sql(input, indentStr);
}
}
export default SQLBeautify;

View File

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import vkbeautify from "vkbeautify";
import Operation from "../Operation";
/**
* SQL Minify operation
*/
class SQLMinify extends Operation {
/**
* SQLMinify constructor
*/
constructor() {
super();
this.name = "SQL Minify";
this.module = "Code";
this.description = "Compresses Structured Query Language (SQL) code.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return vkbeautify.sqlmin(input);
}
}
export default SQLMinify;

View File

@ -0,0 +1,47 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import vkbeautify from "vkbeautify";
import Operation from "../Operation";
/**
* XML Beautify operation
*/
class XMLBeautify extends Operation {
/**
* XMLBeautify constructor
*/
constructor() {
super();
this.name = "XML Beautify";
this.module = "Code";
this.description = "Indents and prettifies eXtensible Markup Language (XML) code.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Indent string",
"type": "binaryShortString",
"value": "\\t"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const indentStr = args[0];
return vkbeautify.xml(input, indentStr);
}
}
export default XMLBeautify;

View File

@ -0,0 +1,47 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import vkbeautify from "vkbeautify";
import Operation from "../Operation";
/**
* XML Minify operation
*/
class XMLMinify extends Operation {
/**
* XMLMinify constructor
*/
constructor() {
super();
this.name = "XML Minify";
this.module = "Code";
this.description = "Compresses eXtensible Markup Language (XML) code.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Preserve comments",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const preserveComments = args[0];
return vkbeautify.xmlmin(input, preserveComments);
}
}
export default XMLMinify;

View File

@ -0,0 +1,73 @@
/**
* @author Mikescher (https://github.com/Mikescher | https://mikescher.com)
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import xmldom from "xmldom";
import xpath from "xpath";
/**
* XPath expression operation
*/
class XPathExpression extends Operation {
/**
* XPathExpression constructor
*/
constructor() {
super();
this.name = "XPath expression";
this.module = "Code";
this.description = "Extract information from an XML document with an XPath query";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "XPath",
"type": "string",
"value": ""
},
{
"name": "Result delimiter",
"type": "binaryShortString",
"value": "\\n"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [query, delimiter] = args;
let doc;
try {
doc = new xmldom.DOMParser().parseFromString(input, "application/xml");
} catch (err) {
throw new OperationError("Invalid input XML.");
}
let nodes;
try {
nodes = xpath.select(query, doc);
} catch (err) {
throw new OperationError(`Invalid XPath. Details:\n${err.message}.`);
}
const nodeToString = function(node) {
return node.toString();
};
return nodes.map(nodeToString).join(delimiter);
}
}
export default XPathExpression;

View File

@ -1,259 +0,0 @@
/**
* @author d98762625 [d98762625@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Utils from "../Utils.js";
import BigNumber from "bignumber.js";
/**
* Math operations on numbers.
*
* @author bwhitn [brian.m.whitney@outlook.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Arithmetic = {
/**
* @constant
* @default
*/
DELIM_OPTIONS: ["Line feed", "Space", "Comma", "Semi-colon", "Colon", "CRLF"],
/**
* Splits a string based on a delimiter and calculates the sum of numbers.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runSum: function(input, args) {
const val = Arithmetic._sum(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and subtracts all the numbers.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runSub: function(input, args) {
let val = Arithmetic._sub(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and multiplies the numbers.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runMulti: function(input, args) {
let val = Arithmetic._multi(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and divides the numbers.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runDiv: function(input, args) {
let val = Arithmetic._div(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and computes the mean (average).
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runMean: function(input, args) {
let val = Arithmetic._mean(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and finds the median.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runMedian: function(input, args) {
let val = Arithmetic._median(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* splits a string based on a delimiter and computes the standard deviation.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runStdDev: function(input, args) {
let val = Arithmetic._stdDev(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Converts a string array to a number array.
*
* @private
* @param {string[]} input
* @param {string} delim
* @returns {BigNumber[]}
*/
_createNumArray: function(input, delim) {
delim = Utils.charRep(delim || "Space");
let splitNumbers = input.split(delim),
numbers = [],
num;
for (let i = 0; i < splitNumbers.length; i++) {
try {
num = BigNumber(splitNumbers[i].trim());
if (!num.isNaN()) {
numbers.push(num);
}
} catch (err) {
// This line is not a valid number
}
}
return numbers;
},
/**
* Adds an array of numbers and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_sum: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.plus(curr));
}
},
/**
* Subtracts an array of numbers and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_sub: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.minus(curr));
}
},
/**
* Multiplies an array of numbers and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_multi: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.times(curr));
}
},
/**
* Divides an array of numbers and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_div: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.div(curr));
}
},
/**
* Computes mean of a number array and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_mean: function(data) {
if (data.length > 0) {
return Arithmetic._sum(data).div(data.length);
}
},
/**
* Computes median of a number array and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_median: function (data) {
if ((data.length % 2) === 0 && data.length > 0) {
let first, second;
data.sort(function(a, b){
return a.minus(b);
});
first = data[Math.floor(data.length / 2)];
second = data[Math.floor(data.length / 2) - 1];
return Arithmetic._mean([first, second]);
} else {
return data[Math.floor(data.length / 2)];
}
},
/**
* Computes standard deviation of a number array and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_stdDev: function (data) {
if (data.length > 0) {
let avg = Arithmetic._mean(data);
let devSum = new BigNumber(0);
for (let i = 0; i < data.length; i++) {
devSum = devSum.plus(data[i].minus(avg).pow(2));
}
return devSum.div(data.length).sqrt();
}
},
};
export default Arithmetic;

View File

@ -1,215 +0,0 @@
import Utils from "../Utils.js";
import BigNumber from "bignumber.js";
/**
* Binary-Coded Decimal operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const BCD = {
/**
* @constant
* @default
*/
ENCODING_SCHEME: [
"8 4 2 1",
"7 4 2 1",
"4 2 2 1",
"2 4 2 1",
"8 4 -2 -1",
"Excess-3",
"IBM 8 4 2 1",
],
/**
* Lookup table for the binary value of each digit representation.
*
* I wrote a very nice algorithm to generate 8 4 2 1 encoding programatically,
* but unfortunately it's much easier (if less elegant) to use lookup tables
* when supporting multiple encoding schemes.
*
* "Practicality beats purity" - PEP 20
*
* In some schemes it is possible to represent the same value in multiple ways.
* For instance, in 4 2 2 1 encoding, 0100 and 0010 both represent 2. Support
* has not yet been added for this.
*
* @constant
*/
ENCODING_LOOKUP: {
"8 4 2 1": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"7 4 2 1": [0, 1, 2, 3, 4, 5, 6, 8, 9, 10],
"4 2 2 1": [0, 1, 4, 5, 8, 9, 12, 13, 14, 15],
"2 4 2 1": [0, 1, 2, 3, 4, 11, 12, 13, 14, 15],
"8 4 -2 -1": [0, 7, 6, 5, 4, 11, 10, 9, 8, 15],
"Excess-3": [3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
"IBM 8 4 2 1": [10, 1, 2, 3, 4, 5, 6, 7, 8, 9],
},
/**
* @default
* @constant
*/
FORMAT: ["Nibbles", "Bytes", "Raw"],
/**
* To BCD operation.
*
* @param {BigNumber} input
* @param {Object[]} args
* @returns {string}
*/
runToBCD: function(input, args) {
if (input.isNaN())
return "Invalid input";
if (!input.integerValue(BigNumber.ROUND_DOWN).isEqualTo(input))
return "Fractional values are not supported by BCD";
const encoding = BCD.ENCODING_LOOKUP[args[0]],
packed = args[1],
signed = args[2],
outputFormat = args[3];
// Split input number up into separate digits
const digits = input.toFixed().split("");
if (digits[0] === "-" || digits[0] === "+") {
digits.shift();
}
let nibbles = [];
digits.forEach(d => {
const n = parseInt(d, 10);
nibbles.push(encoding[n]);
});
if (signed) {
if (packed && digits.length % 2 === 0) {
// If there are an even number of digits, we add a leading 0 so
// that the sign nibble doesn't sit in its own byte, leading to
// ambiguity around whether the number ends with a 0 or not.
nibbles.unshift(encoding[0]);
}
nibbles.push(input > 0 ? 12 : 13);
// 12 ("C") for + (credit)
// 13 ("D") for - (debit)
}
let bytes = [];
if (packed) {
let encoded = 0,
little = false;
nibbles.forEach(n => {
encoded ^= little ? n : (n << 4);
if (little) {
bytes.push(encoded);
encoded = 0;
}
little = !little;
});
if (little) bytes.push(encoded);
} else {
bytes = nibbles;
// Add null high nibbles
nibbles = nibbles.map(n => {
return [0, n];
}).reduce((a, b) => {
return a.concat(b);
});
}
// Output
switch (outputFormat) {
case "Nibbles":
return nibbles.map(n => {
return n.toString(2).padStart(4, "0");
}).join(" ");
case "Bytes":
return bytes.map(b => {
return b.toString(2).padStart(8, "0");
}).join(" ");
case "Raw":
default:
return Utils.byteArrayToChars(bytes);
}
},
/**
* From BCD operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runFromBCD: function(input, args) {
const encoding = BCD.ENCODING_LOOKUP[args[0]],
packed = args[1],
signed = args[2],
inputFormat = args[3];
let nibbles = [],
output = "",
byteArray;
// Normalise the input
switch (inputFormat) {
case "Nibbles":
case "Bytes":
input = input.replace(/\s/g, "");
for (let i = 0; i < input.length; i += 4) {
nibbles.push(parseInt(input.substr(i, 4), 2));
}
break;
case "Raw":
default:
byteArray = Utils.strToByteArray(input);
byteArray.forEach(b => {
nibbles.push(b >>> 4);
nibbles.push(b & 15);
});
break;
}
if (!packed) {
// Discard each high nibble
for (let i = 0; i < nibbles.length; i++) {
nibbles.splice(i, 1);
}
}
if (signed) {
const sign = nibbles.pop();
if (sign === 13 ||
sign === 11) {
// Negative
output += "-";
}
}
nibbles.forEach(n => {
if (isNaN(n)) throw "Invalid input";
let val = encoding.indexOf(n);
if (val < 0) throw `Value ${Utils.bin(n, 4)} not in encoding scheme`;
output += val.toString();
});
return new BigNumber(output);
},
};
export default BCD;

View File

@ -1,59 +0,0 @@
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) {
throw 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,68 +0,0 @@
import BigNumber from "bignumber.js";
/**
* Numerical base operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Base = {
/**
* @constant
* @default
*/
DEFAULT_RADIX: 36,
/**
* To Base operation.
*
* @param {BigNumber} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
if (!input) {
throw ("Error: Input must be a number");
}
const radix = args[0] || Base.DEFAULT_RADIX;
if (radix < 2 || radix > 36) {
throw "Error: Radix argument must be between 2 and 36";
}
return input.toString(radix);
},
/**
* From Base operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runFrom: function(input, args) {
const radix = args[0] || Base.DEFAULT_RADIX;
if (radix < 2 || radix > 36) {
throw "Error: Radix argument must be between 2 and 36";
}
let number = input.replace(/\s/g, "").split("."),
result = new BigNumber(number[0], radix) || 0;
if (number.length === 1) return result;
// Fractional part
for (let i = 0; i < number[1].length; i++) {
const digit = new BigNumber(number[1][i], radix);
result += digit.div(Math.pow(radix, i+1));
}
return result;
},
};
export default Base;

View File

@ -1,137 +0,0 @@
import Utils from "../Utils.js";
/**
* Base58 operations.
*
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const Base58 = {
/**
* @constant
* @default
*/
ALPHABET_OPTIONS: [
{
name: "Bitcoin",
value: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
},
{
name: "Ripple",
value: "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz",
},
],
/**
* @constant
* @default
*/
REMOVE_NON_ALPH_CHARS: true,
/**
* To Base58 operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
let alphabet = args[0] || Base58.ALPHABET_OPTIONS[0].value,
result = [0];
alphabet = Utils.expandAlphRange(alphabet).join("");
if (alphabet.length !== 58 ||
[].unique.call(alphabet).length !== 58) {
throw ("Error: alphabet must be of length 58");
}
if (input.length === 0) return "";
input.forEach(function(b) {
let carry = (result[0] << 8) + b;
result[0] = carry % 58;
carry = (carry / 58) | 0;
for (let i = 1; i < result.length; i++) {
carry += result[i] << 8;
result[i] = carry % 58;
carry = (carry / 58) | 0;
}
while (carry > 0) {
result.push(carry % 58);
carry = (carry / 58) | 0;
}
});
result = result.map(function(b) {
return alphabet[b];
}).reverse().join("");
while (result.length < input.length) {
result = alphabet[0] + result;
}
return result;
},
/**
* From Base58 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFrom: function(input, args) {
let alphabet = args[0] || Base58.ALPHABET_OPTIONS[0].value,
removeNonAlphaChars = args[1] === undefined ? true : args[1],
result = [0];
alphabet = Utils.expandAlphRange(alphabet).join("");
if (alphabet.length !== 58 ||
[].unique.call(alphabet).length !== 58) {
throw ("Alphabet must be of length 58");
}
if (input.length === 0) return [];
[].forEach.call(input, function(c, charIndex) {
const index = alphabet.indexOf(c);
if (index === -1) {
if (removeNonAlphaChars) {
return;
} else {
throw ("Char '" + c + "' at position " + charIndex + " not in alphabet");
}
}
let carry = result[0] * 58 + index;
result[0] = carry & 0xFF;
carry = carry >> 8;
for (let i = 1; i < result.length; i++) {
carry += result[i] * 58;
result[i] = carry & 0xFF;
carry = carry >> 8;
}
while (carry > 0) {
result.push(carry & 0xFF);
carry = carry >> 8;
}
});
return result.reverse();
},
};
export default Base58;

View File

@ -1,355 +0,0 @@
import Utils from "../Utils.js";
import {toHex, fromHex} from "../lib/Hex";
/**
* Byte representation operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const ByteRepr = {
/**
* @constant
* @default
*/
DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF"],
/**
* @constant
* @default
*/
TO_HEX_DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"],
/**
* @constant
* @default
*/
FROM_HEX_DELIM_OPTIONS: ["Auto", "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 Octal operation.
*
* @author Matt C [matt@artemisbot.uk]
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToOct: function(input, args) {
const delim = Utils.charRep(args[0] || "Space");
return input.map(val => val.toString(8)).join(delim);
},
/**
* From Octal operation.
*
* @author Matt C [matt@artemisbot.uk]
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromOct: function(input, args) {
const delim = Utils.charRep(args[0] || "Space");
if (input.length === 0) return [];
return input.split(delim).map(val => parseInt(val, 8));
},
/**
* @constant
* @default
*/
CHARCODE_BASE: 16,
/**
* To Charcode operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runToCharcode: function(input, args) {
let delim = Utils.charRep(args[0] || "Space"),
base = args[1],
output = "",
padding = 2,
ordinal;
if (base < 2 || base > 36) {
throw "Error: Base argument must be between 2 and 36";
}
const charcode = Utils.strToCharcode(input);
for (let i = 0; i < charcode.length; i++) {
ordinal = charcode[i];
if (base === 16) {
if (ordinal < 256) padding = 2;
else if (ordinal < 65536) padding = 4;
else if (ordinal < 16777216) padding = 6;
else if (ordinal < 4294967296) padding = 8;
else padding = 2;
if (padding > 2 && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
output += Utils.hex(ordinal, padding) + delim;
} else {
if (ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
output += ordinal.toString(base) + delim;
}
}
return output.slice(0, -delim.length);
},
/**
* From Charcode operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromCharcode: function(input, args) {
let delim = Utils.charRep(args[0] || "Space"),
base = args[1],
bites = input.split(delim),
i = 0;
if (base < 2 || base > 36) {
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
// too long to be a single character
if (bites.length === 1 && input.length > 17) {
bites = [];
for (i = 0; i < input.length; i += 2) {
bites.push(input.slice(i, i+2));
}
}
let latin1 = "";
for (i = 0; i < bites.length; i++) {
latin1 += Utils.chr(parseInt(bites[i], base));
}
return Utils.strToByteArray(latin1);
},
/**
* To Decimal operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToDecimal: function(input, args) {
const delim = Utils.charRep(args[0]);
return input.join(delim);
},
/**
* From Decimal operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromDecimal: function(input, args) {
const delim = Utils.charRep(args[0]);
let byteStr = input.split(delim), output = [];
if (byteStr[byteStr.length-1] === "")
byteStr = byteStr.slice(0, byteStr.length-1);
for (let i = 0; i < byteStr.length; i++) {
output[i] = parseInt(byteStr[i], 10);
}
return output;
},
/**
* To Binary operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToBinary: function(input, args) {
let delim = Utils.charRep(args[0] || "Space"),
output = "",
padding = 8;
for (let i = 0; i < input.length; i++) {
output += input[i].toString(2).padStart(padding, "0") + delim;
}
if (delim.length) {
return output.slice(0, -delim.length);
} else {
return output;
}
},
/**
* From Binary operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromBinary: function(input, args) {
const delimRegex = Utils.regexRep(args[0] || "Space");
input = input.replace(delimRegex, "");
const output = [];
const byteLen = 8;
for (let i = 0; i < input.length; i += byteLen) {
output.push(parseInt(input.substr(i, byteLen), 2));
}
return output;
},
/**
* Highlight to binary
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightToBinary: function(pos, args) {
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;
},
/**
* Highlight from binary
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightFromBinary: function(pos, args) {
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;
},
/**
* @constant
* @default
*/
HEX_CONTENT_CONVERT_WHICH: ["Only special chars", "Only special chars including spaces", "All chars"],
/**
* @constant
* @default
*/
HEX_CONTENT_SPACES_BETWEEN_BYTES: false,
/**
* To Hex Content operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToHexContent: function(input, args) {
const convert = args[0];
const spaces = args[1];
if (convert === "All chars") {
let result = "|" + toHex(input) + "|";
if (!spaces) result = result.replace(/ /g, "");
return result;
}
let output = "",
inHex = false,
convertSpaces = convert === "Only special chars including spaces",
b;
for (let i = 0; i < input.length; i++) {
b = input[i];
if ((b === 32 && convertSpaces) || (b < 48 && b !== 32) || (b > 57 && b < 65) || (b > 90 && b < 97) || b > 122) {
if (!inHex) {
output += "|";
inHex = true;
} else if (spaces) output += " ";
output += toHex([b]);
} else {
if (inHex) {
output += "|";
inHex = false;
}
output += Utils.chr(input[i]);
}
}
if (inHex) output += "|";
return output;
},
/**
* From Hex Content operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromHexContent: function(input, args) {
const regex = /\|([a-f\d ]{2,})\|/gi;
let output = [], m, i = 0;
while ((m = regex.exec(input))) {
// Add up to match
for (; i < m.index;)
output.push(Utils.ord(input[i++]));
// Add match
const bytes = fromHex(m[1]);
if (bytes) {
for (let a = 0; a < bytes.length;)
output.push(bytes[a++]);
} else {
// Not valid hex, print as normal
for (; i < regex.lastIndex;)
output.push(Utils.ord(input[i++]));
}
i = regex.lastIndex;
}
// Add all after final match
for (; i < input.length;)
output.push(Utils.ord(input[i++]));
return output;
},
};
export default ByteRepr;

View File

@ -1,97 +0,0 @@
import cptable from "../vendor/js-codepage/cptable.js";
/**
* Character encoding operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const CharEnc = {
/**
* @constant
* @default
*/
IO_FORMAT: {
"UTF-8 (65001)": 65001,
"UTF-7 (65000)": 65000,
"UTF16LE (1200)": 1200,
"UTF16BE (1201)": 1201,
"UTF16 (1201)": 1201,
"IBM EBCDIC International (500)": 500,
"IBM EBCDIC US-Canada (37)": 37,
"Windows-874 Thai (874)": 874,
"Japanese Shift-JIS (932)": 932,
"Simplified Chinese GBK (936)": 936,
"Korean (949)": 949,
"Traditional Chinese Big5 (950)": 950,
"Windows-1250 Central European (1250)": 1250,
"Windows-1251 Cyrillic (1251)": 1251,
"Windows-1252 Latin (1252)": 1252,
"Windows-1253 Greek (1253)": 1253,
"Windows-1254 Turkish (1254)": 1254,
"Windows-1255 Hebrew (1255)": 1255,
"Windows-1256 Arabic (1256)": 1256,
"Windows-1257 Baltic (1257)": 1257,
"Windows-1258 Vietnam (1258)": 1258,
"US-ASCII (20127)": 20127,
"Simplified Chinese GB2312 (20936)": 20936,
"KOI8-R Russian Cyrillic (20866)": 20866,
"KOI8-U Ukrainian Cyrillic (21866)": 21866,
"ISO-8859-1 Latin 1 Western European (28591)": 28591,
"ISO-8859-2 Latin 2 Central European (28592)": 28592,
"ISO-8859-3 Latin 3 South European (28593)": 28593,
"ISO-8859-4 Latin 4 North European (28594)": 28594,
"ISO-8859-5 Latin/Cyrillic (28595)": 28595,
"ISO-8859-6 Latin/Arabic (28596)": 28596,
"ISO-8859-7 Latin/Greek (28597)": 28597,
"ISO-8859-8 Latin/Hebrew (28598)": 28598,
"ISO-8859-9 Latin 5 Turkish (28599)": 28599,
"ISO-8859-10 Latin 6 Nordic (28600)": 28600,
"ISO-8859-11 Latin/Thai (28601)": 28601,
"ISO-8859-13 Latin 7 Baltic Rim (28603)": 28603,
"ISO-8859-14 Latin 8 Celtic (28604)": 28604,
"ISO-8859-15 Latin 9 (28605)": 28605,
"ISO-8859-16 Latin 10 (28606)": 28606,
"ISO-2022 JIS Japanese (50222)": 50222,
"EUC Japanese (51932)": 51932,
"EUC Korean (51949)": 51949,
"Simplified Chinese GB18030 (54936)": 54936,
},
/**
* Encode text operation.
* @author tlwr [toby@toby.codes]
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runEncode: function(input, args) {
const format = CharEnc.IO_FORMAT[args[0]];
let encoded = cptable.utils.encode(format, input);
encoded = Array.from(encoded);
return encoded;
},
/**
* Decode text operation.
* @author tlwr [toby@toby.codes]
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runDecode: function(input, args) {
const format = CharEnc.IO_FORMAT[args[0]];
let decoded = cptable.utils.decode(format, input);
return decoded;
},
};
export default CharEnc;

View File

@ -1,179 +0,0 @@
import * as CRC from "js-crc";
import Utils from "../Utils.js";
/**
* Checksum operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Checksum = {
/**
* Fletcher-8 Checksum operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runFletcher8: function(input, args) {
let a = 0,
b = 0;
for (let i = 0; i < input.length; i++) {
a = (a + input[i]) % 0xf;
b = (b + a) % 0xf;
}
return Utils.hex(((b << 4) | a) >>> 0, 2);
},
/**
* Fletcher-16 Checksum operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runFletcher16: function(input, args) {
let a = 0,
b = 0;
for (let i = 0; i < input.length; i++) {
a = (a + input[i]) % 0xff;
b = (b + a) % 0xff;
}
return Utils.hex(((b << 8) | a) >>> 0, 4);
},
/**
* Fletcher-32 Checksum operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runFletcher32: function(input, args) {
let a = 0,
b = 0;
for (let i = 0; i < input.length; i++) {
a = (a + input[i]) % 0xffff;
b = (b + a) % 0xffff;
}
return Utils.hex(((b << 16) | a) >>> 0, 8);
},
/**
* Fletcher-64 Checksum operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runFletcher64: function(input, args) {
let a = 0,
b = 0;
for (let i = 0; i < input.length; i++) {
a = (a + input[i]) % 0xffffffff;
b = (b + a) % 0xffffffff;
}
return Utils.hex(b >>> 0, 8) + Utils.hex(a >>> 0, 8);
},
/**
* Adler-32 Checksum operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runAdler32: function(input, args) {
let MOD_ADLER = 65521,
a = 1,
b = 0;
for (let i = 0; i < input.length; i++) {
a += input[i];
b += a;
}
a %= MOD_ADLER;
b %= MOD_ADLER;
return Utils.hex(((b << 16) | a) >>> 0, 8);
},
/**
* CRC-32 Checksum operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runCRC32: function(input, args) {
return CRC.crc32(input);
},
/**
* CRC-16 Checksum operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runCRC16: function(input, args) {
return CRC.crc16(input);
},
/**
* TCP/IP Checksum operation.
*
* @author GCHQ Contributor [1]
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*
* @example
* // returns '3f2c'
* Checksum.runTcpIp([0x45,0x00,0x00,0x87,0xa3,0x1b,0x40,0x00,0x40,0x06,
* 0x00,0x00,0xac,0x11,0x00,0x04,0xac,0x11,0x00,0x03])
*
* // returns 'a249'
* Checksum.runTcpIp([0x45,0x00,0x01,0x11,0x3f,0x74,0x40,0x00,0x40,0x06,
* 0x00,0x00,0xac,0x11,0x00,0x03,0xac,0x11,0x00,0x04])
*/
runTCPIP: function(input, args) {
let csum = 0;
for (let i = 0; i < input.length; i++) {
if (i % 2 === 0) {
csum += (input[i] << 8);
} else {
csum += input[i];
}
}
csum = (csum >> 16) + (csum & 0xffff);
return Utils.hex(0xffff - csum);
},
};
export default Checksum;

View File

@ -1,540 +0,0 @@
import {camelCase, kebabCase, snakeCase} from "lodash";
import vkbeautify from "vkbeautify";
import {DOMParser} from "xmldom";
import xpath from "xpath";
import jpath from "jsonpath";
import nwmatcher from "nwmatcher";
import hljs from "highlight.js";
/**
* Code operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Code = {
/**
* @constant
* @default
*/
LANGUAGES: ["auto detect"].concat(hljs.listLanguages()),
/**
* Syntax highlighter operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runSyntaxHighlight: function(input, args) {
const language = args[0];
if (language === "auto detect") {
return hljs.highlightAuto(input).value;
}
return hljs.highlight(language, input, true).value;
},
/**
* @constant
* @default
*/
BEAUTIFY_INDENT: "\\t",
/**
* XML Beautify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runXmlBeautify: function(input, args) {
const indentStr = args[0];
return vkbeautify.xml(input, indentStr);
},
/**
* JSON Beautify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runJsonBeautify: function(input, args) {
const indentStr = args[0];
if (!input) return "";
return vkbeautify.json(input, indentStr);
},
/**
* CSS Beautify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runCssBeautify: function(input, args) {
const indentStr = args[0];
return vkbeautify.css(input, indentStr);
},
/**
* SQL Beautify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSqlBeautify: function(input, args) {
const indentStr = args[0];
return vkbeautify.sql(input, indentStr);
},
/**
* @constant
* @default
*/
PRESERVE_COMMENTS: false,
/**
* XML Minify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runXmlMinify: function(input, args) {
const preserveComments = args[0];
return vkbeautify.xmlmin(input, preserveComments);
},
/**
* JSON Minify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runJsonMinify: function(input, args) {
if (!input) return "";
return vkbeautify.jsonmin(input);
},
/**
* CSS Minify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runCssMinify: function(input, args) {
const preserveComments = args[0];
return vkbeautify.cssmin(input, preserveComments);
},
/**
* SQL Minify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSqlMinify: function(input, args) {
return vkbeautify.sqlmin(input);
},
/**
* Generic Code Beautify operation.
*
* Yeeeaaah...
*
* I'm not proud of this code, but seriously, try writing a generic lexer and parser that
* correctly generates an AST for multiple different languages. I have tried, and I can tell
* you it's pretty much impossible.
*
* This basically works. That'll have to be good enough. It's not meant to produce working code,
* just slightly more readable code.
*
* Things that don't work:
* - For loop formatting
* - Do-While loop formatting
* - Switch/Case indentation
* - Bit shift operators
*
* @author n1474335 [n1474335@gmail.com]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runGenericBeautify: function(input, args) {
let code = input,
t = 0,
preservedTokens = [],
m;
// Remove strings
const sstrings = /'([^'\\]|\\.)*'/g;
while ((m = sstrings.exec(code))) {
code = preserveToken(code, m, t++);
sstrings.lastIndex = m.index;
}
const dstrings = /"([^"\\]|\\.)*"/g;
while ((m = dstrings.exec(code))) {
code = preserveToken(code, m, t++);
dstrings.lastIndex = m.index;
}
// Remove comments
const scomments = /\/\/[^\n\r]*/g;
while ((m = scomments.exec(code))) {
code = preserveToken(code, m, t++);
scomments.lastIndex = m.index;
}
const mcomments = /\/\*[\s\S]*?\*\//gm;
while ((m = mcomments.exec(code))) {
code = preserveToken(code, m, t++);
mcomments.lastIndex = m.index;
}
const hcomments = /(^|\n)#[^\n\r#]+/g;
while ((m = hcomments.exec(code))) {
code = preserveToken(code, m, t++);
hcomments.lastIndex = m.index;
}
// Remove regexes
const regexes = /\/.*?[^\\]\/[gim]{0,3}/gi;
while ((m = regexes.exec(code))) {
code = preserveToken(code, m, t++);
regexes.lastIndex = m.index;
}
code = code
// Create newlines after ;
.replace(/;/g, ";\n")
// Create newlines after { and around }
.replace(/{/g, "{\n")
.replace(/}/g, "\n}\n")
// Remove carriage returns
.replace(/\r/g, "")
// Remove all indentation
.replace(/^\s+/g, "")
.replace(/\n\s+/g, "\n")
// Remove trailing spaces
.replace(/\s*$/g, "")
.replace(/\n{/g, "{");
// Indent
let i = 0,
level = 0,
indent;
while (i < code.length) {
switch (code[i]) {
case "{":
level++;
break;
case "\n":
if (i+1 >= code.length) break;
if (code[i+1] === "}") level--;
indent = (level >= 0) ? Array(level*4+1).join(" ") : "";
code = code.substring(0, i+1) + indent + code.substring(i+1);
if (level > 0) i += level*4;
break;
}
i++;
}
code = code
// Add strategic spaces
.replace(/\s*([!<>=+-/*]?)=\s*/g, " $1= ")
.replace(/\s*<([=]?)\s*/g, " <$1 ")
.replace(/\s*>([=]?)\s*/g, " >$1 ")
.replace(/([^+])\+([^+=])/g, "$1 + $2")
.replace(/([^-])-([^-=])/g, "$1 - $2")
.replace(/([^*])\*([^*=])/g, "$1 * $2")
.replace(/([^/])\/([^/=])/g, "$1 / $2")
.replace(/\s*,\s*/g, ", ")
.replace(/\s*{/g, " {")
.replace(/}\n/g, "}\n\n")
// Hacky horribleness
.replace(/(if|for|while|with|elif|elseif)\s*\(([^\n]*)\)\s*\n([^{])/gim, "$1 ($2)\n $3")
.replace(/(if|for|while|with|elif|elseif)\s*\(([^\n]*)\)([^{])/gim, "$1 ($2) $3")
.replace(/else\s*\n([^{])/gim, "else\n $1")
.replace(/else\s+([^{])/gim, "else $1")
// Remove strategic spaces
.replace(/\s+;/g, ";")
.replace(/\{\s+\}/g, "{}")
.replace(/\[\s+\]/g, "[]")
.replace(/}\s*(else|catch|except|finally|elif|elseif|else if)/gi, "} $1");
// Replace preserved tokens
const ptokens = /###preservedToken(\d+)###/g;
while ((m = ptokens.exec(code))) {
const ti = parseInt(m[1], 10);
code = code.substring(0, m.index) + preservedTokens[ti] + code.substring(m.index + m[0].length);
ptokens.lastIndex = m.index;
}
return code;
/**
* Replaces a matched token with a placeholder value.
*/
function preserveToken(str, match, t) {
preservedTokens[t] = match[0];
return str.substring(0, match.index) +
"###preservedToken" + t + "###" +
str.substring(match.index + match[0].length);
}
},
/**
* @constant
* @default
*/
XPATH_INITIAL: "",
/**
* @constant
* @default
*/
XPATH_DELIMITER: "\\n",
/**
* XPath expression operation.
*
* @author Mikescher (https://github.com/Mikescher | https://mikescher.com)
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runXpath: function(input, args) {
let query = args[0],
delimiter = args[1];
let doc;
try {
doc = new DOMParser().parseFromString(input, "application/xml");
} catch (err) {
return "Invalid input XML.";
}
let nodes;
try {
nodes = xpath.select(query, doc);
} catch (err) {
return "Invalid XPath. Details:\n" + err.message;
}
const nodeToString = function(node) {
return node.toString();
};
return nodes.map(nodeToString).join(delimiter);
},
/**
* @constant
* @default
*/
JPATH_INITIAL: "",
/**
* @constant
* @default
*/
JPATH_DELIMITER: "\\n",
/**
* JPath expression operation.
*
* @author Matt C (matt@artemisbot.uk)
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runJpath: function(input, args) {
let query = args[0],
delimiter = args[1],
results,
obj;
try {
obj = JSON.parse(input);
} catch (err) {
return "Invalid input JSON: " + err.message;
}
try {
results = jpath.query(obj, query);
} catch (err) {
return "Invalid JPath expression: " + err.message;
}
return results.map(result => JSON.stringify(result)).join(delimiter);
},
/**
* @constant
* @default
*/
CSS_SELECTOR_INITIAL: "",
/**
* @constant
* @default
*/
CSS_QUERY_DELIMITER: "\\n",
/**
* CSS selector operation.
*
* @author Mikescher (https://github.com/Mikescher | https://mikescher.com)
* @author n1474335 [n1474335@gmail.com]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runCSSQuery: function(input, args) {
let query = args[0],
delimiter = args[1],
parser = new DOMParser(),
dom,
result;
if (!query.length || !input.length) {
return "";
}
try {
dom = parser.parseFromString(input);
} catch (err) {
return "Invalid input HTML.";
}
try {
const matcher = nwmatcher({document: dom});
result = matcher.select(query, dom);
} catch (err) {
return "Invalid CSS Selector. Details:\n" + err.message;
}
const nodeToString = function(node) {
return node.toString();
/* xmldom does not return the outerHTML value.
switch (node.nodeType) {
case node.ELEMENT_NODE: return node.outerHTML;
case node.ATTRIBUTE_NODE: return node.value;
case node.TEXT_NODE: return node.wholeText;
case node.COMMENT_NODE: return node.data;
case node.DOCUMENT_NODE: return node.outerHTML;
default: throw new Error("Unknown Node Type: " + node.nodeType);
}*/
};
return result
.map(nodeToString)
.join(delimiter);
},
/**
* This tries to rename variable names in a code snippet according to a function.
*
* @param {string} input
* @param {function} replacer - this function will be fed the token which should be renamed.
* @returns {string}
*/
_replaceVariableNames(input, replacer) {
const tokenRegex = /\\"|"(?:\\"|[^"])*"|(\b[a-z0-9\-_]+\b)/ig;
return input.replace(tokenRegex, (...args) => {
let match = args[0],
quotes = args[1];
if (!quotes) {
return match;
} else {
return replacer(match);
}
});
},
/**
* To Snake Case operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runToSnakeCase(input, args) {
const smart = args[0];
if (smart) {
return Code._replaceVariableNames(input, snakeCase);
} else {
return snakeCase(input);
}
},
/**
* To Camel Case operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runToCamelCase(input, args) {
const smart = args[0];
if (smart) {
return Code._replaceVariableNames(input, camelCase);
} else {
return camelCase(input);
}
},
/**
* To Kebab Case operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runToKebabCase(input, args) {
const smart = args[0];
if (smart) {
return Code._replaceVariableNames(input, kebabCase);
} else {
return kebabCase(input);
}
},
};
export default Code;

View File

@ -1,243 +0,0 @@
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,413 +0,0 @@
/**
* Unit conversion operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Convert = {
/**
* @constant
* @default
*/
DISTANCE_UNITS: [
"[Metric]", "Nanometres (nm)", "Micrometres (µm)", "Millimetres (mm)", "Centimetres (cm)", "Metres (m)", "Kilometers (km)", "[/Metric]",
"[Imperial]", "Thou (th)", "Inches (in)", "Feet (ft)", "Yards (yd)", "Chains (ch)", "Furlongs (fur)", "Miles (mi)", "Leagues (lea)", "[/Imperial]",
"[Maritime]", "Fathoms (ftm)", "Cables", "Nautical miles", "[/Maritime]",
"[Comparisons]", "Cars (4m)", "Buses (8.4m)", "American football fields (91m)", "Football pitches (105m)", "[/Comparisons]",
"[Astronomical]", "Earth-to-Moons", "Earth's equators", "Astronomical units (au)", "Light-years (ly)", "Parsecs (pc)", "[/Astronomical]",
],
/**
* @constant
* @default
*/
DISTANCE_FACTOR: { // Multiples of a metre
"Nanometres (nm)": 1e-9,
"Micrometres (µm)": 1e-6,
"Millimetres (mm)": 1e-3,
"Centimetres (cm)": 1e-2,
"Metres (m)": 1,
"Kilometers (km)": 1e3,
"Thou (th)": 0.0000254,
"Inches (in)": 0.0254,
"Feet (ft)": 0.3048,
"Yards (yd)": 0.9144,
"Chains (ch)": 20.1168,
"Furlongs (fur)": 201.168,
"Miles (mi)": 1609.344,
"Leagues (lea)": 4828.032,
"Fathoms (ftm)": 1.853184,
"Cables": 185.3184,
"Nautical miles": 1853.184,
"Cars (4m)": 4,
"Buses (8.4m)": 8.4,
"American football fields (91m)": 91,
"Football pitches (105m)": 105,
"Earth-to-Moons": 380000000,
"Earth's equators": 40075016.686,
"Astronomical units (au)": 149597870700,
"Light-years (ly)": 9460730472580800,
"Parsecs (pc)": 3.0856776e16
},
/**
* Convert distance operation.
*
* @param {BigNumber} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runDistance: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input.times(Convert.DISTANCE_FACTOR[inputUnits]);
return input.div(Convert.DISTANCE_FACTOR[outputUnits]);
},
/**
* @constant
* @default
*/
DATA_UNITS: [
"Bits (b)", "Nibbles", "Octets", "Bytes (B)",
"[Binary bits (2^n)]", "Kibibits (Kib)", "Mebibits (Mib)", "Gibibits (Gib)", "Tebibits (Tib)", "Pebibits (Pib)", "Exbibits (Eib)", "Zebibits (Zib)", "Yobibits (Yib)", "[/Binary bits (2^n)]",
"[Decimal bits (10^n)]", "Decabits", "Hectobits", "Kilobits (kb)", "Megabits (Mb)", "Gigabits (Gb)", "Terabits (Tb)", "Petabits (Pb)", "Exabits (Eb)", "Zettabits (Zb)", "Yottabits (Yb)", "[/Decimal bits (10^n)]",
"[Binary bytes (8 x 2^n)]", "Kibibytes (KiB)", "Mebibytes (MiB)", "Gibibytes (GiB)", "Tebibytes (TiB)", "Pebibytes (PiB)", "Exbibytes (EiB)", "Zebibytes (ZiB)", "Yobibytes (YiB)", "[/Binary bytes (8 x 2^n)]",
"[Decimal bytes (8 x 10^n)]", "Kilobytes (KB)", "Megabytes (MB)", "Gigabytes (GB)", "Terabytes (TB)", "Petabytes (PB)", "Exabytes (EB)", "Zettabytes (ZB)", "Yottabytes (YB)", "[/Decimal bytes (8 x 10^n)]"
],
/**
* @constant
* @default
*/
DATA_FACTOR: { // Multiples of a bit
"Bits (b)": 1,
"Nibbles": 4,
"Octets": 8,
"Bytes (B)": 8,
// Binary bits (2^n)
"Kibibits (Kib)": 1024,
"Mebibits (Mib)": 1048576,
"Gibibits (Gib)": 1073741824,
"Tebibits (Tib)": 1099511627776,
"Pebibits (Pib)": 1125899906842624,
"Exbibits (Eib)": 1152921504606846976,
"Zebibits (Zib)": 1180591620717411303424,
"Yobibits (Yib)": 1208925819614629174706176,
// Decimal bits (10^n)
"Decabits": 10,
"Hectobits": 100,
"Kilobits (Kb)": 1e3,
"Megabits (Mb)": 1e6,
"Gigabits (Gb)": 1e9,
"Terabits (Tb)": 1e12,
"Petabits (Pb)": 1e15,
"Exabits (Eb)": 1e18,
"Zettabits (Zb)": 1e21,
"Yottabits (Yb)": 1e24,
// Binary bytes (8 x 2^n)
"Kibibytes (KiB)": 8192,
"Mebibytes (MiB)": 8388608,
"Gibibytes (GiB)": 8589934592,
"Tebibytes (TiB)": 8796093022208,
"Pebibytes (PiB)": 9007199254740992,
"Exbibytes (EiB)": 9223372036854775808,
"Zebibytes (ZiB)": 9444732965739290427392,
"Yobibytes (YiB)": 9671406556917033397649408,
// Decimal bytes (8 x 10^n)
"Kilobytes (KB)": 8e3,
"Megabytes (MB)": 8e6,
"Gigabytes (GB)": 8e9,
"Terabytes (TB)": 8e12,
"Petabytes (PB)": 8e15,
"Exabytes (EB)": 8e18,
"Zettabytes (ZB)": 8e21,
"Yottabytes (YB)": 8e24,
},
/**
* Convert data units operation.
*
* @param {BigNumber} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runDataSize: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input.times(Convert.DATA_FACTOR[inputUnits]);
return input.div(Convert.DATA_FACTOR[outputUnits]);
},
/**
* @constant
* @default
*/
AREA_UNITS: [
"[Metric]", "Square metre (sq m)", "Square kilometre (sq km)", "Centiare (ca)", "Deciare (da)", "Are (a)", "Decare (daa)", "Hectare (ha)", "[/Metric]",
"[Imperial]", "Square inch (sq in)", "Square foot (sq ft)", "Square yard (sq yd)", "Square mile (sq mi)", "Perch (sq per)", "Rood (ro)", "International acre (ac)", "[/Imperial]",
"[US customary units]", "US survey acre (ac)", "US survey square mile (sq mi)", "US survey township", "[/US customary units]",
"[Nuclear physics]", "Yoctobarn (yb)", "Zeptobarn (zb)", "Attobarn (ab)", "Femtobarn (fb)", "Picobarn (pb)", "Nanobarn (nb)", "Microbarn (μb)", "Millibarn (mb)", "Barn (b)", "Kilobarn (kb)", "Megabarn (Mb)", "Outhouse", "Shed", "Planck area", "[/Nuclear physics]",
"[Comparisons]", "Washington D.C.", "Isle of Wight", "Wales", "Texas", "[/Comparisons]",
],
/**
* @constant
* @default
*/
AREA_FACTOR: { // Multiples of a square metre
// Metric
"Square metre (sq m)": 1,
"Square kilometre (sq km)": 1e6,
"Centiare (ca)": 1,
"Deciare (da)": 10,
"Are (a)": 100,
"Decare (daa)": 1e3,
"Hectare (ha)": 1e4,
// Imperial
"Square inch (sq in)": 0.00064516,
"Square foot (sq ft)": 0.09290304,
"Square yard (sq yd)": 0.83612736,
"Square mile (sq mi)": 2589988.110336,
"Perch (sq per)": 42.21,
"Rood (ro)": 1011,
"International acre (ac)": 4046.8564224,
// US customary units
"US survey acre (ac)": 4046.87261,
"US survey square mile (sq mi)": 2589998.470305239,
"US survey township": 93239944.9309886,
// Nuclear physics
"Yoctobarn (yb)": 1e-52,
"Zeptobarn (zb)": 1e-49,
"Attobarn (ab)": 1e-46,
"Femtobarn (fb)": 1e-43,
"Picobarn (pb)": 1e-40,
"Nanobarn (nb)": 1e-37,
"Microbarn (μb)": 1e-34,
"Millibarn (mb)": 1e-31,
"Barn (b)": 1e-28,
"Kilobarn (kb)": 1e-25,
"Megabarn (Mb)": 1e-22,
"Planck area": 2.6e-70,
"Shed": 1e-52,
"Outhouse": 1e-34,
// Comparisons
"Washington D.C.": 176119191.502848,
"Isle of Wight": 380000000,
"Wales": 20779000000,
"Texas": 696241000000,
},
/**
* Convert area operation.
*
* @param {BigNumber} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runArea: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input.times(Convert.AREA_FACTOR[inputUnits]);
return input.div(Convert.AREA_FACTOR[outputUnits]);
},
/**
* @constant
* @default
*/
MASS_UNITS: [
"[Metric]", "Yoctogram (yg)", "Zeptogram (zg)", "Attogram (ag)", "Femtogram (fg)", "Picogram (pg)", "Nanogram (ng)", "Microgram (μg)", "Milligram (mg)", "Centigram (cg)", "Decigram (dg)", "Gram (g)", "Decagram (dag)", "Hectogram (hg)", "Kilogram (kg)", "Megagram (Mg)", "Tonne (t)", "Gigagram (Gg)", "Teragram (Tg)", "Petagram (Pg)", "Exagram (Eg)", "Zettagram (Zg)", "Yottagram (Yg)", "[/Metric]",
"[Imperial Avoirdupois]", "Grain (gr)", "Dram (dr)", "Ounce (oz)", "Pound (lb)", "Nail", "Stone (st)", "Quarter (gr)", "Tod", "US hundredweight (cwt)", "Imperial hundredweight (cwt)", "US ton (t)", "Imperial ton (t)", "[/Imperial Avoirdupois]",
"[Imperial Troy]", "Grain (gr)", "Pennyweight (dwt)", "Troy dram (dr t)", "Troy ounce (oz t)", "Troy pound (lb t)", "Mark", "[/Imperial Troy]",
"[Archaic]", "Wey", "Wool wey", "Suffolk wey", "Wool sack", "Coal sack", "Load", "Last", "Flax or feather last", "Gunpowder last", "Picul", "Rice last", "[/Archaic]",
"[Comparisons]", "Big Ben (14 tonnes)", "Blue whale (180 tonnes)", "International Space Station (417 tonnes)", "Space Shuttle (2,041 tonnes)", "RMS Titanic (52,000 tonnes)", "Great Pyramid of Giza (6,000,000 tonnes)", "Earth's oceans (1.4 yottagrams)", "[/Comparisons]",
"[Astronomical]", "A teaspoon of neutron star (5,500 million tonnes)", "Lunar mass (ML)", "Earth mass (M⊕)", "Jupiter mass (MJ)", "Solar mass (M☉)", "Sagittarius A* (7.5 x 10^36 kgs-ish)", "Milky Way galaxy (1.2 x 10^42 kgs)", "The observable universe (1.45 x 10^53 kgs)", "[/Astronomical]",
],
/**
* @constant
* @default
*/
MASS_FACTOR: { // Multiples of a gram
// Metric
"Yoctogram (yg)": 1e-24,
"Zeptogram (zg)": 1e-21,
"Attogram (ag)": 1e-18,
"Femtogram (fg)": 1e-15,
"Picogram (pg)": 1e-12,
"Nanogram (ng)": 1e-9,
"Microgram (μg)": 1e-6,
"Milligram (mg)": 1e-3,
"Centigram (cg)": 1e-2,
"Decigram (dg)": 1e-1,
"Gram (g)": 1,
"Decagram (dag)": 10,
"Hectogram (hg)": 100,
"Kilogram (kg)": 1000,
"Megagram (Mg)": 1e6,
"Tonne (t)": 1e6,
"Gigagram (Gg)": 1e9,
"Teragram (Tg)": 1e12,
"Petagram (Pg)": 1e15,
"Exagram (Eg)": 1e18,
"Zettagram (Zg)": 1e21,
"Yottagram (Yg)": 1e24,
// Imperial Avoirdupois
"Grain (gr)": 64.79891e-3,
"Dram (dr)": 1.7718451953125,
"Ounce (oz)": 28.349523125,
"Pound (lb)": 453.59237,
"Nail": 3175.14659,
"Stone (st)": 6.35029318e3,
"Quarter (gr)": 12700.58636,
"Tod": 12700.58636,
"US hundredweight (cwt)": 45.359237e3,
"Imperial hundredweight (cwt)": 50.80234544e3,
"US ton (t)": 907.18474e3,
"Imperial ton (t)": 1016.0469088e3,
// Imperial Troy
"Pennyweight (dwt)": 1.55517384,
"Troy dram (dr t)": 3.8879346,
"Troy ounce (oz t)": 31.1034768,
"Troy pound (lb t)": 373.2417216,
"Mark": 248.8278144,
// Archaic
"Wey": 76.5e3,
"Wool wey": 101.7e3,
"Suffolk wey": 161.5e3,
"Wool sack": 153000,
"Coal sack": 50.80234544e3,
"Load": 918000,
"Last": 1836000,
"Flax or feather last": 770e3,
"Gunpowder last": 1090e3,
"Picul": 60.478982e3,
"Rice last": 1200e3,
// Comparisons
"Big Ben (14 tonnes)": 14e6,
"Blue whale (180 tonnes)": 180e6,
"International Space Station (417 tonnes)": 417e6,
"Space Shuttle (2,041 tonnes)": 2041e6,
"RMS Titanic (52,000 tonnes)": 52000e6,
"Great Pyramid of Giza (6,000,000 tonnes)": 6e12,
"Earth's oceans (1.4 yottagrams)": 1.4e24,
// Astronomical
"A teaspoon of neutron star (5,500 million tonnes)": 5.5e15,
"Lunar mass (ML)": 7.342e25,
"Earth mass (M⊕)": 5.97219e27,
"Jupiter mass (MJ)": 1.8981411476999997e30,
"Solar mass (M☉)": 1.98855e33,
"Sagittarius A* (7.5 x 10^36 kgs-ish)": 7.5e39,
"Milky Way galaxy (1.2 x 10^42 kgs)": 1.2e45,
"The observable universe (1.45 x 10^53 kgs)": 1.45e56,
},
/**
* Convert mass operation.
*
* @param {BigNumber} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runMass: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input.times(Convert.MASS_FACTOR[inputUnits]);
return input.div(Convert.MASS_FACTOR[outputUnits]);
},
/**
* @constant
* @default
*/
SPEED_UNITS: [
"[Metric]", "Metres per second (m/s)", "Kilometres per hour (km/h)", "[/Metric]",
"[Imperial]", "Miles per hour (mph)", "Knots (kn)", "[/Imperial]",
"[Comparisons]", "Human hair growth rate", "Bamboo growth rate", "World's fastest snail", "Usain Bolt's top speed", "Jet airliner cruising speed", "Concorde", "SR-71 Blackbird", "Space Shuttle", "International Space Station", "[/Comparisons]",
"[Scientific]", "Sound in standard atmosphere", "Sound in water", "Lunar escape velocity", "Earth escape velocity", "Earth's solar orbit", "Solar system's Milky Way orbit", "Milky Way relative to the cosmic microwave background", "Solar escape velocity", "Neutron star escape velocity (0.3c)", "Light in a diamond (0.4136c)", "Signal in an optical fibre (0.667c)", "Light (c)", "[/Scientific]",
],
/**
* @constant
* @default
*/
SPEED_FACTOR: { // Multiples of m/s
// Metric
"Metres per second (m/s)": 1,
"Kilometres per hour (km/h)": 0.2778,
// Imperial
"Miles per hour (mph)": 0.44704,
"Knots (kn)": 0.5144,
// Comparisons
"Human hair growth rate": 4.8e-9,
"Bamboo growth rate": 1.4e-5,
"World's fastest snail": 0.00275,
"Usain Bolt's top speed": 12.42,
"Jet airliner cruising speed": 250,
"Concorde": 603,
"SR-71 Blackbird": 981,
"Space Shuttle": 1400,
"International Space Station": 7700,
// Scientific
"Sound in standard atmosphere": 340.3,
"Sound in water": 1500,
"Lunar escape velocity": 2375,
"Earth escape velocity": 11200,
"Earth's solar orbit": 29800,
"Solar system's Milky Way orbit": 200000,
"Milky Way relative to the cosmic microwave background": 552000,
"Solar escape velocity": 617700,
"Neutron star escape velocity (0.3c)": 100000000,
"Light in a diamond (0.4136c)": 124000000,
"Signal in an optical fibre (0.667c)": 200000000,
"Light (c)": 299792458,
},
/**
* Convert speed operation.
*
* @param {BigNumber} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runSpeed: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input.times(Convert.SPEED_FACTOR[inputUnits]);
return input.div(Convert.SPEED_FACTOR[outputUnits]);
},
};
export default Convert;

View File

@ -1,484 +0,0 @@
import moment from "moment-timezone";
/**
* Date and time operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const DateTime = {
/**
* @constant
* @default
*/
UNITS: ["Seconds (s)", "Milliseconds (ms)", "Microseconds (μs)", "Nanoseconds (ns)"],
/**
* From UNIX Timestamp operation.
*
* @param {number} input
* @param {Object[]} args
* @returns {string}
*/
runFromUnixTimestamp: function(input, args) {
let units = args[0],
d;
input = parseFloat(input);
if (units === "Seconds (s)") {
d = moment.unix(input);
return d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss") + " UTC";
} else if (units === "Milliseconds (ms)") {
d = moment(input);
return d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss.SSS") + " UTC";
} else if (units === "Microseconds (μs)") {
d = moment(input / 1000);
return d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss.SSS") + " UTC";
} else if (units === "Nanoseconds (ns)") {
d = moment(input / 1000000);
return d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss.SSS") + " UTC";
} else {
throw "Unrecognised unit";
}
},
/**
* @constant
* @default
*/
TREAT_AS_UTC: true,
/**
* To UNIX Timestamp operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runToUnixTimestamp: function(input, args) {
const units = args[0],
treatAsUTC = args[1],
showDateTime = args[2],
d = treatAsUTC ? moment.utc(input) : moment(input);
let result = "";
if (units === "Seconds (s)") {
result = d.unix();
} else if (units === "Milliseconds (ms)") {
result = d.valueOf();
} else if (units === "Microseconds (μs)") {
result = d.valueOf() * 1000;
} else if (units === "Nanoseconds (ns)") {
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();
},
/**
* @constant
* @default
*/
DATETIME_FORMATS: [
{
name: "Standard date and time",
value: "DD/MM/YYYY HH:mm:ss"
},
{
name: "American-style date and time",
value: "MM/DD/YYYY HH:mm:ss"
},
{
name: "International date and time",
value: "YYYY-MM-DD HH:mm:ss"
},
{
name: "Verbose date and time",
value: "dddd Do MMMM YYYY HH:mm:ss Z z"
},
{
name: "UNIX timestamp (seconds)",
value: "X"
},
{
name: "UNIX timestamp offset (milliseconds)",
value: "x"
},
{
name: "Automatic",
value: ""
},
],
/**
* @constant
* @default
*/
INPUT_FORMAT_STRING: "DD/MM/YYYY HH:mm:ss",
/**
* @constant
* @default
*/
OUTPUT_FORMAT_STRING: "dddd Do MMMM YYYY HH:mm:ss Z z",
/**
* @constant
* @default
*/
TIMEZONES: ["UTC"].concat(moment.tz.names()),
/**
* Translate DateTime Format operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runTranslateFormat: function(input, args) {
let inputFormat = args[1],
inputTimezone = args[2],
outputFormat = args[3],
outputTimezone = args[4],
date;
try {
date = moment.tz(input, inputFormat, inputTimezone);
if (!date || date.format() === "Invalid date") throw Error;
} catch (err) {
return "Invalid format.\n\n" + DateTime.FORMAT_EXAMPLES;
}
return date.tz(outputTimezone).format(outputFormat);
},
/**
* Parse DateTime operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runParse: function(input, args) {
let inputFormat = args[1],
inputTimezone = args[2],
date,
output = "";
try {
date = moment.tz(input, inputFormat, inputTimezone);
if (!date || date.format() === "Invalid date") throw Error;
} catch (err) {
return "Invalid format.\n\n" + DateTime.FORMAT_EXAMPLES;
}
output += "Date: " + date.format("dddd Do MMMM YYYY") +
"\nTime: " + date.format("HH:mm:ss") +
"\nPeriod: " + date.format("A") +
"\nTimezone: " + date.format("z") +
"\nUTC offset: " + date.format("ZZ") +
"\n\nDaylight Saving Time: " + date.isDST() +
"\nLeap year: " + date.isLeapYear() +
"\nDays in this month: " + date.daysInMonth() +
"\n\nDay of year: " + date.dayOfYear() +
"\nWeek number: " + date.weekYear() +
"\nQuarter: " + date.quarter();
return output;
},
/**
* Sleep operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
runSleep: async function(input, args) {
const ms = args[0];
await new Promise(r => setTimeout(r, ms));
return input;
},
/**
* @constant
*/
FORMAT_EXAMPLES: `Format string tokens:
<table class="table table-striped table-hover table-condensed table-bordered" style="font-family: sans-serif">
<thead>
<tr>
<th>Category</th>
<th>Token</th>
<th>Output</th>
</tr>
</thead>
<tbody>
<tr>
<td><b>Month</b></td>
<td>M</td>
<td>1 2 ... 11 12</td>
</tr>
<tr>
<td></td>
<td>Mo</td>
<td>1st 2nd ... 11th 12th</td>
</tr>
<tr>
<td></td>
<td>MM</td>
<td>01 02 ... 11 12</td>
</tr>
<tr>
<td></td>
<td>MMM</td>
<td>Jan Feb ... Nov Dec</td>
</tr>
<tr>
<td></td>
<td>MMMM</td>
<td>January February ... November December</td>
</tr>
<tr>
<td><b>Quarter</b></td>
<td>Q</td>
<td>1 2 3 4</td>
</tr>
<tr>
<td><b>Day of Month</b></td>
<td>D</td>
<td>1 2 ... 30 31</td>
</tr>
<tr>
<td></td>
<td>Do</td>
<td>1st 2nd ... 30th 31st</td>
</tr>
<tr>
<td></td>
<td>DD</td>
<td>01 02 ... 30 31</td>
</tr>
<tr>
<td><b>Day of Year</b></td>
<td>DDD</td>
<td>1 2 ... 364 365</td>
</tr>
<tr>
<td></td>
<td>DDDo</td>
<td>1st 2nd ... 364th 365th</td>
</tr>
<tr>
<td></td>
<td>DDDD</td>
<td>001 002 ... 364 365</td>
</tr>
<tr>
<td><b>Day of Week</b></td>
<td>d</td>
<td>0 1 ... 5 6</td>
</tr>
<tr>
<td></td>
<td>do</td>
<td>0th 1st ... 5th 6th</td>
</tr>
<tr>
<td></td>
<td>dd</td>
<td>Su Mo ... Fr Sa</td>
</tr>
<tr>
<td></td>
<td>ddd</td>
<td>Sun Mon ... Fri Sat</td>
</tr>
<tr>
<td></td>
<td>dddd</td>
<td>Sunday Monday ... Friday Saturday</td>
</tr>
<tr>
<td><b>Day of Week (Locale)</b></td>
<td>e</td>
<td>0 1 ... 5 6</td>
</tr>
<tr>
<td><b>Day of Week (ISO)</b></td>
<td>E</td>
<td>1 2 ... 6 7</td>
</tr>
<tr>
<td><b>Week of Year</b></td>
<td>w</td>
<td>1 2 ... 52 53</td>
</tr>
<tr>
<td></td>
<td>wo</td>
<td>1st 2nd ... 52nd 53rd</td>
</tr>
<tr>
<td></td>
<td>ww</td>
<td>01 02 ... 52 53</td>
</tr>
<tr>
<td><b>Week of Year (ISO)</b></td>
<td>W</td>
<td>1 2 ... 52 53</td>
</tr>
<tr>
<td></td>
<td>Wo</td>
<td>1st 2nd ... 52nd 53rd</td>
</tr>
<tr>
<td></td>
<td>WW</td>
<td>01 02 ... 52 53</td>
</tr>
<tr>
<td><b>Year</b></td>
<td>YY</td>
<td>70 71 ... 29 30</td>
</tr>
<tr>
<td></td>
<td>YYYY</td>
<td>1970 1971 ... 2029 2030</td>
</tr>
<tr>
<td><b>Week Year</b></td>
<td>gg</td>
<td>70 71 ... 29 30</td>
</tr>
<tr>
<td></td>
<td>gggg</td>
<td>1970 1971 ... 2029 2030</td>
</tr>
<tr>
<td><b>Week Year (ISO)</b></td>
<td>GG</td>
<td>70 71 ... 29 30</td>
</tr>
<tr>
<td></td>
<td>GGGG</td>
<td>1970 1971 ... 2029 2030</td>
</tr>
<tr>
<td><b>AM/PM</b></td>
<td>A</td>
<td>AM PM</td>
</tr>
<tr>
<td></td>
<td>a</td>
<td>am pm</td>
</tr>
<tr>
<td><b>Hour</b></td>
<td>H</td>
<td>0 1 ... 22 23</td>
</tr>
<tr>
<td></td>
<td>HH</td>
<td>00 01 ... 22 23</td>
</tr>
<tr>
<td></td>
<td>h</td>
<td>1 2 ... 11 12</td>
</tr>
<tr>
<td></td>
<td>hh</td>
<td>01 02 ... 11 12</td>
</tr>
<tr>
<td><b>Minute</b></td>
<td>m</td>
<td>0 1 ... 58 59</td>
</tr>
<tr>
<td></td>
<td>mm</td>
<td>00 01 ... 58 59</td>
</tr>
<tr>
<td><b>Second</b></td>
<td>s</td>
<td>0 1 ... 58 59</td>
</tr>
<tr>
<td></td>
<td>ss</td>
<td>00 01 ... 58 59</td>
</tr>
<tr>
<td><b>Fractional Second</b></td>
<td>S</td>
<td>0 1 ... 8 9</td>
</tr>
<tr>
<td></td>
<td>SS</td>
<td>00 01 ... 98 99</td>
</tr>
<tr>
<td></td>
<td>SSS</td>
<td>000 001 ... 998 999</td>
</tr>
<tr>
<td></td>
<td>SSSS ... SSSSSSSSS</td>
<td>000[0..] 001[0..] ... 998[0..] 999[0..]</td>
</tr>
<tr>
<td><b>Timezone</b></td>
<td>z or zz</td>
<td>EST CST ... MST PST</td>
</tr>
<tr>
<td></td>
<td>Z</td>
<td>-07:00 -06:00 ... +06:00 +07:00</td>
</tr>
<tr>
<td></td>
<td>ZZ</td>
<td>-0700 -0600 ... +0600 +0700</td>
</tr>
<tr>
<td><b>Unix Timestamp</b></td>
<td>X</td>
<td>1360013296</td>
</tr>
<tr>
<td><b>Unix Millisecond Timestamp</b></td>
<td>x</td>
<td>1360013296123</td>
</tr>
</tbody>
</table>`,
};
export default DateTime;

View File

@ -1,94 +0,0 @@
import Utils from "../Utils.js";
import * as JsDiff from "diff";
/**
* Diff operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Diff = {
/**
* @constant
* @default
*/
DIFF_SAMPLE_DELIMITER: "\\n\\n",
/**
* @constant
* @default
*/
DIFF_BY: ["Character", "Word", "Line", "Sentence", "CSS", "JSON"],
/**
* Diff operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runDiff: function(input, args) {
let sampleDelim = args[0],
diffBy = args[1],
showAdded = args[2],
showRemoved = args[3],
ignoreWhitespace = args[4],
samples = input.split(sampleDelim),
output = "",
diff;
if (!samples || samples.length !== 2) {
return "Incorrect number of samples, perhaps you need to modify the sample delimiter or add more samples?";
}
switch (diffBy) {
case "Character":
diff = JsDiff.diffChars(samples[0], samples[1]);
break;
case "Word":
if (ignoreWhitespace) {
diff = JsDiff.diffWords(samples[0], samples[1]);
} else {
diff = JsDiff.diffWordsWithSpace(samples[0], samples[1]);
}
break;
case "Line":
if (ignoreWhitespace) {
diff = JsDiff.diffTrimmedLines(samples[0], samples[1]);
} else {
diff = JsDiff.diffLines(samples[0], samples[1]);
}
break;
case "Sentence":
diff = JsDiff.diffSentences(samples[0], samples[1]);
break;
case "CSS":
diff = JsDiff.diffCss(samples[0], samples[1]);
break;
case "JSON":
diff = JsDiff.diffJson(samples[0], samples[1]);
break;
default:
return "Invalid 'Diff by' option.";
}
for (let i = 0; i < diff.length; i++) {
if (diff[i].added) {
if (showAdded) output += "<span class='hl5'>" + Utils.escapeHtml(diff[i].value) + "</span>";
} else if (diff[i].removed) {
if (showRemoved) output += "<span class='hl3'>" + Utils.escapeHtml(diff[i].value) + "</span>";
} else {
output += Utils.escapeHtml(diff[i].value);
}
}
return output;
},
};
export default Diff;

View File

@ -1,100 +0,0 @@
import Utils from "../Utils.js";
import {toHex, fromHex} from "../lib/Hex";
/**
* Endian operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Endian = {
/**
* @constant
* @default
*/
DATA_FORMAT: ["Hex", "Raw"],
/**
* @constant
* @default
*/
WORD_LENGTH: 4,
/**
* @constant
* @default
*/
PAD_INCOMPLETE_WORDS: true,
/**
* Swap endianness operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSwapEndianness: function(input, args) {
let dataFormat = args[0],
wordLength = args[1],
padIncompleteWords = args[2],
data = [],
result = [],
words = [],
i = 0,
j = 0;
if (wordLength <= 0) {
return "Word length must be greater than 0";
}
// Convert input to raw data based on specified data format
switch (dataFormat) {
case "Hex":
data = fromHex(input);
break;
case "Raw":
data = Utils.strToByteArray(input);
break;
default:
data = input;
}
// Split up into words
for (i = 0; i < data.length; i += wordLength) {
const word = data.slice(i, i + wordLength);
// Pad word if too short
if (padIncompleteWords && word.length < wordLength){
for (j = word.length; j < wordLength; j++) {
word.push(0);
}
}
words.push(word);
}
// Swap endianness and flatten
for (i = 0; i < words.length; i++) {
j = words[i].length;
while (j--) {
result.push(words[i][j]);
}
}
// Convert data back to specified data format
switch (dataFormat) {
case "Hex":
return toHex(result);
case "Raw":
return Utils.byteArrayToUtf8(result);
default:
return result;
}
},
};
export default Endian;

View File

@ -1,195 +0,0 @@
import Utils from "../Utils.js";
/**
* Entropy operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Entropy = {
/**
* @constant
* @default
*/
CHUNK_SIZE: 1000,
/**
* Entropy operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {html}
*/
runEntropy: function(input, args) {
let chunkSize = args[0],
output = "",
entropy = Entropy._calcEntropy(input);
output += "Shannon entropy: " + entropy + "\n" +
"<br><canvas id='chart-area'></canvas><br>\n" +
"- 0 represents no randomness (i.e. all the bytes in the data have the same value) whereas 8, the maximum, represents a completely random string.\n" +
"- Standard English text usually falls somewhere between 3.5 and 5.\n" +
"- Properly encrypted or compressed data of a reasonable length should have an entropy of over 7.5.\n\n" +
"The following results show the entropy of chunks of the input data. Chunks with particularly high entropy could suggest encrypted or compressed sections.\n\n" +
"<br><script>\
var canvas = document.getElementById('chart-area'),\
parentRect = canvas.parentNode.getBoundingClientRect(),\
entropy = " + entropy + ",\
height = parentRect.height * 0.25;\
\
canvas.width = parentRect.width * 0.95;\
canvas.height = height > 150 ? 150 : height;\
\
CanvasComponents.drawScaleBar(canvas, entropy, 8, [\
{\
label: 'English text',\
min: 3.5,\
max: 5\
},{\
label: 'Encrypted/compressed',\
min: 7.5,\
max: 8\
}\
]);\
</script>";
let chunkEntropy = 0;
if (chunkSize !== 0) {
for (let i = 0; i < input.length; i += chunkSize) {
chunkEntropy = Entropy._calcEntropy(input.slice(i, i+chunkSize));
output += "Bytes " + i + " to " + (i+chunkSize) + ": " + chunkEntropy + "\n";
}
} else {
output += "Chunk size cannot be 0.";
}
return output;
},
/**
* @constant
* @default
*/
FREQ_ZEROS: false,
/**
* Frequency distribution operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {html}
*/
runFreqDistrib: function (input, args) {
const data = new Uint8Array(input);
if (!data.length) return "No data";
let distrib = new Array(256).fill(0),
percentages = new Array(256),
len = data.length,
showZeroes = args[0],
i;
// Count bytes
for (i = 0; i < len; i++) {
distrib[data[i]]++;
}
// Calculate percentages
let repr = 0;
for (i = 0; i < 256; i++) {
if (distrib[i] > 0) repr++;
percentages[i] = distrib[i] / len * 100;
}
// Print
let output = "<canvas id='chart-area'></canvas><br>" +
"Total data length: " + len +
"\nNumber of bytes represented: " + repr +
"\nNumber of bytes not represented: " + (256-repr) +
"\n\nByte Percentage\n" +
"<script>\
var canvas = document.getElementById('chart-area'),\
parentRect = canvas.parentNode.getBoundingClientRect(),\
scores = " + JSON.stringify(percentages) + ";\
\
canvas.width = parentRect.width * 0.95;\
canvas.height = parentRect.height * 0.9;\
\
CanvasComponents.drawBarChart(canvas, scores, 'Byte', 'Frequency %', 16, 6);\
</script>";
for (i = 0; i < 256; i++) {
if (distrib[i] || showZeroes) {
output += " " + Utils.hex(i, 2) + " (" +
(percentages[i].toFixed(2).replace(".00", "") + "%)").padEnd(8, " ") +
Array(Math.ceil(percentages[i])+1).join("|") + "\n";
}
}
return output;
},
/**
* Chi Square operation.
*
* @param {ArrayBuffer} data
* @param {Object[]} args
* @returns {number}
*/
runChiSq: function(input, args) {
const data = new Uint8Array(input);
let distArray = new Array(256).fill(0),
total = 0;
for (let i = 0; i < data.length; i++) {
distArray[data[i]]++;
}
for (let i = 0; i < distArray.length; i++) {
if (distArray[i] > 0) {
total += Math.pow(distArray[i] - data.length / 256, 2) / (data.length / 256);
}
}
return total;
},
/**
* Calculates the Shannon entropy for a given chunk of data.
*
* @private
* @param {byteArray} data
* @returns {number}
*/
_calcEntropy: function(data) {
let prob = [],
uniques = data.unique(),
str = Utils.byteArrayToChars(data),
i;
for (i = 0; i < uniques.length; i++) {
prob.push(str.count(Utils.chr(uniques[i])) / data.length);
}
let entropy = 0,
p;
for (i = 0; i < prob.length; i++) {
p = prob[i];
entropy += p * Math.log(p) / Math.log(2);
}
return -entropy;
},
};
export default Entropy;

View File

@ -1,542 +0,0 @@
import Utils from "../Utils.js";
/**
* File type operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const FileType = {
/**
* Detect File Type operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runDetect: function(input, args) {
const data = new Uint8Array(input),
type = FileType.magicType(data);
if (!type) {
return "Unknown file type. Have you tried checking the entropy of this data to determine whether it might be encrypted or compressed?";
} else {
let output = "File extension: " + type.ext + "\n" +
"MIME type: " + type.mime;
if (type.desc && type.desc.length) {
output += "\nDescription: " + type.desc;
}
return output;
}
},
/**
* @constant
* @default
*/
IGNORE_COMMON_BYTE_SEQUENCES: true,
/**
* Scan for Embedded Files operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runScanForEmbeddedFiles: function(input, args) {
let output = "Scanning data for 'magic bytes' which may indicate embedded files. The following results may be false positives and should not be treat as reliable. Any suffiently long file is likely to contain these magic bytes coincidentally.\n",
type,
numFound = 0,
numCommonFound = 0;
const ignoreCommon = args[0],
commonExts = ["ico", "ttf", ""],
data = new Uint8Array(input);
for (let i = 0; i < data.length; i++) {
type = FileType.magicType(data.slice(i));
if (type) {
if (ignoreCommon && commonExts.indexOf(type.ext) > -1) {
numCommonFound++;
continue;
}
numFound++;
output += "\nOffset " + i + " (0x" + Utils.hex(i) + "):\n" +
" File extension: " + type.ext + "\n" +
" MIME type: " + type.mime + "\n";
if (type.desc && type.desc.length) {
output += " Description: " + type.desc + "\n";
}
}
}
if (numFound === 0) {
output += "\nNo embedded files were found.";
}
if (numCommonFound > 0) {
output += "\n\n" + numCommonFound;
output += numCommonFound === 1 ?
" file type was detected that has a common byte sequence. This is likely to be a false positive." :
" file types were detected that have common byte sequences. These are likely to be false positives.";
output += " Run this operation with the 'Ignore common byte sequences' option unchecked to see details.";
}
return output;
},
/**
* Given a buffer, detects magic byte sequences at specific positions and returns the
* extension and mime type.
*
* @param {Uint8Array} buf
* @returns {Object} type
* @returns {string} type.ext - File extension
* @returns {string} type.mime - Mime type
* @returns {string} [type.desc] - Description
*/
magicType: function (buf) {
if (!(buf && buf.length > 1)) {
return null;
}
if (buf[0] === 0xFF && buf[1] === 0xD8 && buf[2] === 0xFF) {
return {
ext: "jpg",
mime: "image/jpeg"
};
}
if (buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4E && buf[3] === 0x47) {
return {
ext: "png",
mime: "image/png"
};
}
if (buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46) {
return {
ext: "gif",
mime: "image/gif"
};
}
if (buf[8] === 0x57 && buf[9] === 0x45 && buf[10] === 0x42 && buf[11] === 0x50) {
return {
ext: "webp",
mime: "image/webp"
};
}
// needs to be before `tif` check
if (((buf[0] === 0x49 && buf[1] === 0x49 && buf[2] === 0x2A && buf[3] === 0x0) || (buf[0] === 0x4D && buf[1] === 0x4D && buf[2] === 0x0 && buf[3] === 0x2A)) && buf[8] === 0x43 && buf[9] === 0x52) {
return {
ext: "cr2",
mime: "image/x-canon-cr2"
};
}
if ((buf[0] === 0x49 && buf[1] === 0x49 && buf[2] === 0x2A && buf[3] === 0x0) || (buf[0] === 0x4D && buf[1] === 0x4D && buf[2] === 0x0 && buf[3] === 0x2A)) {
return {
ext: "tif",
mime: "image/tiff"
};
}
if (buf[0] === 0x42 && buf[1] === 0x4D) {
return {
ext: "bmp",
mime: "image/bmp"
};
}
if (buf[0] === 0x49 && buf[1] === 0x49 && buf[2] === 0xBC) {
return {
ext: "jxr",
mime: "image/vnd.ms-photo"
};
}
if (buf[0] === 0x38 && buf[1] === 0x42 && buf[2] === 0x50 && buf[3] === 0x53) {
return {
ext: "psd",
mime: "image/vnd.adobe.photoshop"
};
}
// needs to be before `zip` check
if (buf[0] === 0x50 && buf[1] === 0x4B && buf[2] === 0x3 && buf[3] === 0x4 && buf[30] === 0x6D && buf[31] === 0x69 && buf[32] === 0x6D && buf[33] === 0x65 && buf[34] === 0x74 && buf[35] === 0x79 && buf[36] === 0x70 && buf[37] === 0x65 && buf[38] === 0x61 && buf[39] === 0x70 && buf[40] === 0x70 && buf[41] === 0x6C && buf[42] === 0x69 && buf[43] === 0x63 && buf[44] === 0x61 && buf[45] === 0x74 && buf[46] === 0x69 && buf[47] === 0x6F && buf[48] === 0x6E && buf[49] === 0x2F && buf[50] === 0x65 && buf[51] === 0x70 && buf[52] === 0x75 && buf[53] === 0x62 && buf[54] === 0x2B && buf[55] === 0x7A && buf[56] === 0x69 && buf[57] === 0x70) {
return {
ext: "epub",
mime: "application/epub+zip"
};
}
if (buf[0] === 0x50 && buf[1] === 0x4B && (buf[2] === 0x3 || buf[2] === 0x5 || buf[2] === 0x7) && (buf[3] === 0x4 || buf[3] === 0x6 || buf[3] === 0x8)) {
return {
ext: "zip",
mime: "application/zip"
};
}
if (buf[257] === 0x75 && buf[258] === 0x73 && buf[259] === 0x74 && buf[260] === 0x61 && buf[261] === 0x72) {
return {
ext: "tar",
mime: "application/x-tar"
};
}
if (buf[0] === 0x52 && buf[1] === 0x61 && buf[2] === 0x72 && buf[3] === 0x21 && buf[4] === 0x1A && buf[5] === 0x7 && (buf[6] === 0x0 || buf[6] === 0x1)) {
return {
ext: "rar",
mime: "application/x-rar-compressed"
};
}
if (buf[0] === 0x1F && buf[1] === 0x8B && buf[2] === 0x8) {
return {
ext: "gz",
mime: "application/gzip"
};
}
if (buf[0] === 0x42 && buf[1] === 0x5A && buf[2] === 0x68) {
return {
ext: "bz2",
mime: "application/x-bzip2"
};
}
if (buf[0] === 0x37 && buf[1] === 0x7A && buf[2] === 0xBC && buf[3] === 0xAF && buf[4] === 0x27 && buf[5] === 0x1C) {
return {
ext: "7z",
mime: "application/x-7z-compressed"
};
}
if (buf[0] === 0x78 && buf[1] === 0x01) {
return {
ext: "dmg, zlib",
mime: "application/x-apple-diskimage, application/x-deflate"
};
}
if ((buf[0] === 0x0 && buf[1] === 0x0 && buf[2] === 0x0 && (buf[3] === 0x18 || buf[3] === 0x20) && buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70) || (buf[0] === 0x33 && buf[1] === 0x67 && buf[2] === 0x70 && buf[3] === 0x35) || (buf[0] === 0x0 && buf[1] === 0x0 && buf[2] === 0x0 && buf[3] === 0x1C && buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70 && buf[8] === 0x6D && buf[9] === 0x70 && buf[10] === 0x34 && buf[11] === 0x32 && buf[16] === 0x6D && buf[17] === 0x70 && buf[18] === 0x34 && buf[19] === 0x31 && buf[20] === 0x6D && buf[21] === 0x70 && buf[22] === 0x34 && buf[23] === 0x32 && buf[24] === 0x69 && buf[25] === 0x73 && buf[26] === 0x6F && buf[27] === 0x6D)) {
return {
ext: "mp4",
mime: "video/mp4"
};
}
if ((buf[0] === 0x0 && buf[1] === 0x0 && buf[2] === 0x0 && buf[3] === 0x1C && buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70 && buf[8] === 0x4D && buf[9] === 0x34 && buf[10] === 0x56)) {
return {
ext: "m4v",
mime: "video/x-m4v"
};
}
if (buf[0] === 0x4D && buf[1] === 0x54 && buf[2] === 0x68 && buf[3] === 0x64) {
return {
ext: "mid",
mime: "audio/midi"
};
}
// needs to be before the `webm` check
if (buf[31] === 0x6D && buf[32] === 0x61 && buf[33] === 0x74 && buf[34] === 0x72 && buf[35] === 0x6f && buf[36] === 0x73 && buf[37] === 0x6B && buf[38] === 0x61) {
return {
ext: "mkv",
mime: "video/x-matroska"
};
}
if (buf[0] === 0x1A && buf[1] === 0x45 && buf[2] === 0xDF && buf[3] === 0xA3) {
return {
ext: "webm",
mime: "video/webm"
};
}
if (buf[0] === 0x0 && buf[1] === 0x0 && buf[2] === 0x0 && buf[3] === 0x14 && buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70) {
return {
ext: "mov",
mime: "video/quicktime"
};
}
if (buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 && buf[8] === 0x41 && buf[9] === 0x56 && buf[10] === 0x49) {
return {
ext: "avi",
mime: "video/x-msvideo"
};
}
if (buf[0] === 0x30 && buf[1] === 0x26 && buf[2] === 0xB2 && buf[3] === 0x75 && buf[4] === 0x8E && buf[5] === 0x66 && buf[6] === 0xCF && buf[7] === 0x11 && buf[8] === 0xA6 && buf[9] === 0xD9) {
return {
ext: "wmv",
mime: "video/x-ms-wmv"
};
}
if (buf[0] === 0x0 && buf[1] === 0x0 && buf[2] === 0x1 && buf[3].toString(16)[0] === "b") {
return {
ext: "mpg",
mime: "video/mpeg"
};
}
if ((buf[0] === 0x49 && buf[1] === 0x44 && buf[2] === 0x33) || (buf[0] === 0xFF && buf[1] === 0xfb)) {
return {
ext: "mp3",
mime: "audio/mpeg"
};
}
if ((buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70 && buf[8] === 0x4D && buf[9] === 0x34 && buf[10] === 0x41) || (buf[0] === 0x4D && buf[1] === 0x34 && buf[2] === 0x41 && buf[3] === 0x20)) {
return {
ext: "m4a",
mime: "audio/m4a"
};
}
if (buf[0] === 0x4F && buf[1] === 0x67 && buf[2] === 0x67 && buf[3] === 0x53) {
return {
ext: "ogg",
mime: "audio/ogg"
};
}
if (buf[0] === 0x66 && buf[1] === 0x4C && buf[2] === 0x61 && buf[3] === 0x43) {
return {
ext: "flac",
mime: "audio/x-flac"
};
}
if (buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 && buf[8] === 0x57 && buf[9] === 0x41 && buf[10] === 0x56 && buf[11] === 0x45) {
return {
ext: "wav",
mime: "audio/x-wav"
};
}
if (buf[0] === 0x23 && buf[1] === 0x21 && buf[2] === 0x41 && buf[3] === 0x4D && buf[4] === 0x52 && buf[5] === 0x0A) {
return {
ext: "amr",
mime: "audio/amr"
};
}
if (buf[0] === 0x25 && buf[1] === 0x50 && buf[2] === 0x44 && buf[3] === 0x46) {
return {
ext: "pdf",
mime: "application/pdf"
};
}
if (buf[0] === 0x4D && buf[1] === 0x5A) {
return {
ext: "exe",
mime: "application/x-msdownload"
};
}
if ((buf[0] === 0x43 || buf[0] === 0x46) && buf[1] === 0x57 && buf[2] === 0x53) {
return {
ext: "swf",
mime: "application/x-shockwave-flash"
};
}
if (buf[0] === 0x7B && buf[1] === 0x5C && buf[2] === 0x72 && buf[3] === 0x74 && buf[4] === 0x66) {
return {
ext: "rtf",
mime: "application/rtf"
};
}
if (buf[0] === 0x77 && buf[1] === 0x4F && buf[2] === 0x46 && buf[3] === 0x46 && buf[4] === 0x00 && buf[5] === 0x01 && buf[6] === 0x00 && buf[7] === 0x00) {
return {
ext: "woff",
mime: "application/font-woff"
};
}
if (buf[0] === 0x77 && buf[1] === 0x4F && buf[2] === 0x46 && buf[3] === 0x32 && buf[4] === 0x00 && buf[5] === 0x01 && buf[6] === 0x00 && buf[7] === 0x00) {
return {
ext: "woff2",
mime: "application/font-woff"
};
}
if (buf[34] === 0x4C && buf[35] === 0x50 && ((buf[8] === 0x02 && buf[9] === 0x00 && buf[10] === 0x01) || (buf[8] === 0x01 && buf[9] === 0x00 && buf[10] === 0x00) || (buf[8] === 0x02 && buf[9] === 0x00 && buf[10] === 0x02))) {
return {
ext: "eot",
mime: "application/octet-stream"
};
}
if (buf[0] === 0x00 && buf[1] === 0x01 && buf[2] === 0x00 && buf[3] === 0x00 && buf[4] === 0x00) {
return {
ext: "ttf",
mime: "application/font-sfnt"
};
}
if (buf[0] === 0x4F && buf[1] === 0x54 && buf[2] === 0x54 && buf[3] === 0x4F && buf[4] === 0x00) {
return {
ext: "otf",
mime: "application/font-sfnt"
};
}
if (buf[0] === 0x00 && buf[1] === 0x00 && buf[2] === 0x01 && buf[3] === 0x00) {
return {
ext: "ico",
mime: "image/x-icon"
};
}
if (buf[0] === 0x46 && buf[1] === 0x4C && buf[2] === 0x56 && buf[3] === 0x01) {
return {
ext: "flv",
mime: "video/x-flv"
};
}
if (buf[0] === 0x25 && buf[1] === 0x21) {
return {
ext: "ps",
mime: "application/postscript"
};
}
if (buf[0] === 0xFD && buf[1] === 0x37 && buf[2] === 0x7A && buf[3] === 0x58 && buf[4] === 0x5A && buf[5] === 0x00) {
return {
ext: "xz",
mime: "application/x-xz"
};
}
if (buf[0] === 0x53 && buf[1] === 0x51 && buf[2] === 0x4C && buf[3] === 0x69) {
return {
ext: "sqlite",
mime: "application/x-sqlite3"
};
}
/**
*
* Added by n1474335 [n1474335@gmail.com] from here on
*
*/
if ((buf[0] === 0x1F && buf[1] === 0x9D) || (buf[0] === 0x1F && buf[1] === 0xA0)) {
return {
ext: "z, tar.z",
mime: "application/x-gtar"
};
}
if (buf[0] === 0x7F && buf[1] === 0x45 && buf[2] === 0x4C && buf[3] === 0x46) {
return {
ext: "none, axf, bin, elf, o, prx, puff, so",
mime: "application/x-executable",
desc: "Executable and Linkable Format file. No standard file extension."
};
}
if (buf[0] === 0xCA && buf[1] === 0xFE && buf[2] === 0xBA && buf[3] === 0xBE) {
return {
ext: "class",
mime: "application/java-vm"
};
}
if (buf[0] === 0xEF && buf[1] === 0xBB && buf[2] === 0xBF) {
return {
ext: "txt",
mime: "text/plain",
desc: "UTF-8 encoded Unicode byte order mark detected, commonly but not exclusively seen in text files."
};
}
// Must be before Little-endian UTF-16 BOM
if (buf[0] === 0xFF && buf[1] === 0xFE && buf[2] === 0x00 && buf[3] === 0x00) {
return {
ext: "UTF32LE",
mime: "charset/utf32le",
desc: "Little-endian UTF-32 encoded Unicode byte order mark detected."
};
}
if (buf[0] === 0xFF && buf[1] === 0xFE) {
return {
ext: "UTF16LE",
mime: "charset/utf16le",
desc: "Little-endian UTF-16 encoded Unicode byte order mark detected."
};
}
if ((buf[0x8001] === 0x43 && buf[0x8002] === 0x44 && buf[0x8003] === 0x30 && buf[0x8004] === 0x30 && buf[0x8005] === 0x31) ||
(buf[0x8801] === 0x43 && buf[0x8802] === 0x44 && buf[0x8803] === 0x30 && buf[0x8804] === 0x30 && buf[0x8805] === 0x31) ||
(buf[0x9001] === 0x43 && buf[0x9002] === 0x44 && buf[0x9003] === 0x30 && buf[0x9004] === 0x30 && buf[0x9005] === 0x31)) {
return {
ext: "iso",
mime: "application/octet-stream",
desc: "ISO 9660 CD/DVD image file"
};
}
if (buf[0] === 0xD0 && buf[1] === 0xCF && buf[2] === 0x11 && buf[3] === 0xE0 && buf[4] === 0xA1 && buf[5] === 0xB1 && buf[6] === 0x1A && buf[7] === 0xE1) {
return {
ext: "doc, xls, ppt",
mime: "application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint",
desc: "Microsoft Office documents"
};
}
if (buf[0] === 0x64 && buf[1] === 0x65 && buf[2] === 0x78 && buf[3] === 0x0A && buf[4] === 0x30 && buf[5] === 0x33 && buf[6] === 0x35 && buf[7] === 0x00) {
return {
ext: "dex",
mime: "application/octet-stream",
desc: "Dalvik Executable (Android)"
};
}
if (buf[0] === 0x4B && buf[1] === 0x44 && buf[2] === 0x4D) {
return {
ext: "vmdk",
mime: "application/vmdk, application/x-virtualbox-vmdk"
};
}
if (buf[0] === 0x43 && buf[1] === 0x72 && buf[2] === 0x32 && buf[3] === 0x34) {
return {
ext: "crx",
mime: "application/crx",
desc: "Google Chrome extension or packaged app"
};
}
if (buf[0] === 0x78 && (buf[1] === 0x01 || buf[1] === 0x9C || buf[1] === 0xDA || buf[1] === 0x5e)) {
return {
ext: "zlib",
mime: "application/x-deflate"
};
}
return null;
},
};
export default FileType;

View File

@ -1,103 +0,0 @@
import BigNumber from "bignumber.js";
/**
* Windows Filetime operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const Filetime = {
/**
* @constant
* @default
*/
UNITS: ["Seconds (s)", "Milliseconds (ms)", "Microseconds (μs)", "Nanoseconds (ns)"],
/**
* @constant
* @default
*/
FILETIME_FORMATS: ["Decimal", "Hex"],
/**
* Windows Filetime to Unix Timestamp operation.
*
* @author bwhitn [brian.m.whitney@outlook.com]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runFromFiletimeToUnix: function(input, args) {
let units = args[0],
format = args[1];
if (!input) return "";
if (format === "Hex") {
input = new BigNumber(input, 16);
} else {
input = new BigNumber(input);
}
input = input.minus(new BigNumber("116444736000000000"));
if (units === "Seconds (s)"){
input = input.dividedBy(new BigNumber("10000000"));
} else if (units === "Milliseconds (ms)") {
input = input.dividedBy(new BigNumber("10000"));
} else if (units === "Microseconds (μs)") {
input = input.dividedBy(new BigNumber("10"));
} else if (units === "Nanoseconds (ns)") {
input = input.multipliedBy(new BigNumber("100"));
} else {
throw "Unrecognised unit";
}
return input.toFixed();
},
/**
* Unix Timestamp to Windows Filetime operation.
*
* @author bwhitn [brian.m.whitney@outlook.com]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runToFiletimeFromUnix: function(input, args) {
let units = args[0],
format = args[1];
if (!input) return "";
input = new BigNumber(input);
if (units === "Seconds (s)"){
input = input.multipliedBy(new BigNumber("10000000"));
} else if (units === "Milliseconds (ms)") {
input = input.multipliedBy(new BigNumber("10000"));
} else if (units === "Microseconds (μs)") {
input = input.multiplyiedBy(new BigNumber("10"));
} else if (units === "Nanoseconds (ns)") {
input = input.dividedBy(new BigNumber("100"));
} else {
throw "Unrecognised unit";
}
input = input.plus(new BigNumber("116444736000000000"));
if (format === "Hex"){
return input.toString(16);
} else {
return input.toFixed();
}
},
};
export default Filetime;

View File

@ -1,857 +0,0 @@
import Utils from "../Utils.js";
/**
* HTML operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const HTML = {
/**
* @constant
* @default
*/
CONVERT_ALL: false,
/**
* @constant
* @default
*/
CONVERT_OPTIONS: ["Named entities where possible", "Numeric entities", "Hex entities"],
/**
* To HTML Entity operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runToEntity: function(input, args) {
let convertAll = args[0],
numeric = args[1] === "Numeric entities",
hexa = args[1] === "Hex entities";
const charcodes = Utils.strToCharcode(input);
let output = "";
for (let i = 0; i < charcodes.length; i++) {
if (convertAll && numeric) {
output += "&#" + charcodes[i] + ";";
} else if (convertAll && hexa) {
output += "&#x" + Utils.hex(charcodes[i]) + ";";
} else if (convertAll) {
output += HTML._byteToEntity[charcodes[i]] || "&#" + charcodes[i] + ";";
} else if (numeric) {
if (charcodes[i] > 255 || HTML._byteToEntity.hasOwnProperty(charcodes[i])) {
output += "&#" + charcodes[i] + ";";
} else {
output += Utils.chr(charcodes[i]);
}
} else if (hexa) {
if (charcodes[i] > 255 || HTML._byteToEntity.hasOwnProperty(charcodes[i])) {
output += "&#x" + Utils.hex(charcodes[i]) + ";";
} else {
output += Utils.chr(charcodes[i]);
}
} else {
output += HTML._byteToEntity[charcodes[i]] || (
charcodes[i] > 255 ?
"&#" + charcodes[i] + ";" :
Utils.chr(charcodes[i])
);
}
}
return output;
},
/**
* From HTML Entity operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runFromEntity: function(input, args) {
let regex = /&(#?x?[a-zA-Z0-9]{1,8});/g,
output = "",
m,
i = 0;
while ((m = regex.exec(input))) {
// Add up to match
for (; i < m.index;)
output += input[i++];
// Add match
const bite = HTML._entityToByte[m[1]];
if (bite) {
output += Utils.chr(bite);
} else if (!bite && m[1][0] === "#" && m[1].length > 1 && /^#\d{1,6}$/.test(m[1])) {
// Numeric entity (e.g. &#10;)
const num = m[1].slice(1, m[1].length);
output += Utils.chr(parseInt(num, 10));
} else if (!bite && m[1][0] === "#" && m[1].length > 3 && /^#x[\dA-F]{2,8}$/i.test(m[1])) {
// Hex entity (e.g. &#x3A;)
const hex = m[1].slice(2, m[1].length);
output += Utils.chr(parseInt(hex, 16));
} else {
// Not a valid entity, print as normal
for (; i < regex.lastIndex;)
output += input[i++];
}
i = regex.lastIndex;
}
// Add all after final match
for (; i < input.length;)
output += input[i++];
return output;
},
/**
* @constant
* @default
*/
REMOVE_INDENTATION: true,
/**
* @constant
* @default
*/
REMOVE_LINE_BREAKS: true,
/**
* Strip HTML tags operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runStripTags: function(input, args) {
let removeIndentation = args[0],
removeLineBreaks = args[1];
input = Utils.stripHtmlTags(input);
if (removeIndentation) {
input = input.replace(/\n[ \f\t]+/g, "\n");
}
if (removeLineBreaks) {
input = input
.replace(/^\s*\n/, "") // first line
.replace(/(\n\s*){2,}/g, "\n"); // all others
}
return input;
},
/**
* Parse colour code operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runParseColourCode: function(input, args) {
let m = null,
r = 0, g = 0, b = 0, a = 1;
// Read in the input
if ((m = input.match(/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/i))) {
// Hex - #d9edf7
r = parseInt(m[1], 16);
g = parseInt(m[2], 16);
b = parseInt(m[3], 16);
} else if ((m = input.match(/rgba?\((\d{1,3}(?:\.\d+)?),\s?(\d{1,3}(?:\.\d+)?),\s?(\d{1,3}(?:\.\d+)?)(?:,\s?(\d(?:\.\d+)?))?\)/i))) {
// RGB or RGBA - rgb(217,237,247) or rgba(217,237,247,1)
r = parseFloat(m[1]);
g = parseFloat(m[2]);
b = parseFloat(m[3]);
a = m[4] ? parseFloat(m[4]) : 1;
} else if ((m = input.match(/hsla?\((\d{1,3}(?:\.\d+)?),\s?(\d{1,3}(?:\.\d+)?)%,\s?(\d{1,3}(?:\.\d+)?)%(?:,\s?(\d(?:\.\d+)?))?\)/i))) {
// HSL or HSLA - hsl(200, 65%, 91%) or hsla(200, 65%, 91%, 1)
let h_ = parseFloat(m[1]) / 360,
s_ = parseFloat(m[2]) / 100,
l_ = parseFloat(m[3]) / 100,
rgb_ = HTML._hslToRgb(h_, s_, l_);
r = rgb_[0];
g = rgb_[1];
b = rgb_[2];
a = m[4] ? parseFloat(m[4]) : 1;
} else if ((m = input.match(/cmyk\((\d(?:\.\d+)?),\s?(\d(?:\.\d+)?),\s?(\d(?:\.\d+)?),\s?(\d(?:\.\d+)?)\)/i))) {
// CMYK - cmyk(0.12, 0.04, 0.00, 0.03)
let c_ = parseFloat(m[1]),
m_ = parseFloat(m[2]),
y_ = parseFloat(m[3]),
k_ = parseFloat(m[4]);
r = Math.round(255 * (1 - c_) * (1 - k_));
g = Math.round(255 * (1 - m_) * (1 - k_));
b = Math.round(255 * (1 - y_) * (1 - k_));
}
let hsl_ = HTML._rgbToHsl(r, g, b),
h = Math.round(hsl_[0] * 360),
s = Math.round(hsl_[1] * 100),
l = Math.round(hsl_[2] * 100),
k = 1 - Math.max(r/255, g/255, b/255),
c = (1 - r/255 - k) / (1 - k),
y = (1 - b/255 - k) / (1 - k);
m = (1 - g/255 - k) / (1 - k);
c = isNaN(c) ? "0" : c.toFixed(2);
m = isNaN(m) ? "0" : m.toFixed(2);
y = isNaN(y) ? "0" : y.toFixed(2);
k = k.toFixed(2);
let hex = "#" +
Math.round(r).toString(16).padStart(2, "0") +
Math.round(g).toString(16).padStart(2, "0") +
Math.round(b).toString(16).padStart(2, "0"),
rgb = "rgb(" + r + ", " + g + ", " + b + ")",
rgba = "rgba(" + r + ", " + g + ", " + b + ", " + a + ")",
hsl = "hsl(" + h + ", " + s + "%, " + l + "%)",
hsla = "hsla(" + h + ", " + s + "%, " + l + "%, " + a + ")",
cmyk = "cmyk(" + c + ", " + m + ", " + y + ", " + k + ")";
// Generate output
return `<div id="colorpicker" style="display: inline-block"></div>
Hex: ${hex}
RGB: ${rgb}
RGBA: ${rgba}
HSL: ${hsl}
HSLA: ${hsla}
CMYK: ${cmyk}
<script>
$('#colorpicker').colorpicker({
format: 'rgba',
color: '${rgba}',
container: true,
inline: true,
}).on('changeColor', function(e) {
var color = e.color.toRGB();
document.getElementById('input-text').value = 'rgba(' +
color.r + ', ' + color.g + ', ' + color.b + ', ' + color.a + ')';
window.app.autoBake();
});
</script>`;
},
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_colorSpace.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @private
* @author Mohsen (http://stackoverflow.com/a/9493060)
*
* @param {number} h - The hue
* @param {number} s - The saturation
* @param {number} l - The lightness
* @return {Array} The RGB representation
*/
_hslToRgb: function(h, s, l){
let r, g, b;
if (s === 0){
r = g = b = l; // achromatic
} else {
const hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
},
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_colorSpace.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* @private
* @author Mohsen (http://stackoverflow.com/a/9493060)
*
* @param {number} r - The red color value
* @param {number} g - The green color value
* @param {number} b - The blue color value
* @return {Array} The HSL representation
*/
_rgbToHsl: function(r, g, b) {
r /= 255; g /= 255; b /= 255;
let max = Math.max(r, g, b),
min = Math.min(r, g, b),
h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0; // achromatic
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
},
/**
* Lookup table to translate byte values to their HTML entity codes.
*
* @private
* @constant
*/
_byteToEntity: {
34: "&quot;",
38: "&amp;",
39: "&apos;",
60: "&lt;",
62: "&gt;",
160: "&nbsp;",
161: "&iexcl;",
162: "&cent;",
163: "&pound;",
164: "&curren;",
165: "&yen;",
166: "&brvbar;",
167: "&sect;",
168: "&uml;",
169: "&copy;",
170: "&ordf;",
171: "&laquo;",
172: "&not;",
173: "&shy;",
174: "&reg;",
175: "&macr;",
176: "&deg;",
177: "&plusmn;",
178: "&sup2;",
179: "&sup3;",
180: "&acute;",
181: "&micro;",
182: "&para;",
183: "&middot;",
184: "&cedil;",
185: "&sup1;",
186: "&ordm;",
187: "&raquo;",
188: "&frac14;",
189: "&frac12;",
190: "&frac34;",
191: "&iquest;",
192: "&Agrave;",
193: "&Aacute;",
194: "&Acirc;",
195: "&Atilde;",
196: "&Auml;",
197: "&Aring;",
198: "&AElig;",
199: "&Ccedil;",
200: "&Egrave;",
201: "&Eacute;",
202: "&Ecirc;",
203: "&Euml;",
204: "&Igrave;",
205: "&Iacute;",
206: "&Icirc;",
207: "&Iuml;",
208: "&ETH;",
209: "&Ntilde;",
210: "&Ograve;",
211: "&Oacute;",
212: "&Ocirc;",
213: "&Otilde;",
214: "&Ouml;",
215: "&times;",
216: "&Oslash;",
217: "&Ugrave;",
218: "&Uacute;",
219: "&Ucirc;",
220: "&Uuml;",
221: "&Yacute;",
222: "&THORN;",
223: "&szlig;",
224: "&agrave;",
225: "&aacute;",
226: "&acirc;",
227: "&atilde;",
228: "&auml;",
229: "&aring;",
230: "&aelig;",
231: "&ccedil;",
232: "&egrave;",
233: "&eacute;",
234: "&ecirc;",
235: "&euml;",
236: "&igrave;",
237: "&iacute;",
238: "&icirc;",
239: "&iuml;",
240: "&eth;",
241: "&ntilde;",
242: "&ograve;",
243: "&oacute;",
244: "&ocirc;",
245: "&otilde;",
246: "&ouml;",
247: "&divide;",
248: "&oslash;",
249: "&ugrave;",
250: "&uacute;",
251: "&ucirc;",
252: "&uuml;",
253: "&yacute;",
254: "&thorn;",
255: "&yuml;",
338: "&OElig;",
339: "&oelig;",
352: "&Scaron;",
353: "&scaron;",
376: "&Yuml;",
402: "&fnof;",
710: "&circ;",
732: "&tilde;",
913: "&Alpha;",
914: "&Beta;",
915: "&Gamma;",
916: "&Delta;",
917: "&Epsilon;",
918: "&Zeta;",
919: "&Eta;",
920: "&Theta;",
921: "&Iota;",
922: "&Kappa;",
923: "&Lambda;",
924: "&Mu;",
925: "&Nu;",
926: "&Xi;",
927: "&Omicron;",
928: "&Pi;",
929: "&Rho;",
931: "&Sigma;",
932: "&Tau;",
933: "&Upsilon;",
934: "&Phi;",
935: "&Chi;",
936: "&Psi;",
937: "&Omega;",
945: "&alpha;",
946: "&beta;",
947: "&gamma;",
948: "&delta;",
949: "&epsilon;",
950: "&zeta;",
951: "&eta;",
952: "&theta;",
953: "&iota;",
954: "&kappa;",
955: "&lambda;",
956: "&mu;",
957: "&nu;",
958: "&xi;",
959: "&omicron;",
960: "&pi;",
961: "&rho;",
962: "&sigmaf;",
963: "&sigma;",
964: "&tau;",
965: "&upsilon;",
966: "&phi;",
967: "&chi;",
968: "&psi;",
969: "&omega;",
977: "&thetasym;",
978: "&upsih;",
982: "&piv;",
8194: "&ensp;",
8195: "&emsp;",
8201: "&thinsp;",
8204: "&zwnj;",
8205: "&zwj;",
8206: "&lrm;",
8207: "&rlm;",
8211: "&ndash;",
8212: "&mdash;",
8216: "&lsquo;",
8217: "&rsquo;",
8218: "&sbquo;",
8220: "&ldquo;",
8221: "&rdquo;",
8222: "&bdquo;",
8224: "&dagger;",
8225: "&Dagger;",
8226: "&bull;",
8230: "&hellip;",
8240: "&permil;",
8242: "&prime;",
8243: "&Prime;",
8249: "&lsaquo;",
8250: "&rsaquo;",
8254: "&oline;",
8260: "&frasl;",
8364: "&euro;",
8465: "&image;",
8472: "&weierp;",
8476: "&real;",
8482: "&trade;",
8501: "&alefsym;",
8592: "&larr;",
8593: "&uarr;",
8594: "&rarr;",
8595: "&darr;",
8596: "&harr;",
8629: "&crarr;",
8656: "&lArr;",
8657: "&uArr;",
8658: "&rArr;",
8659: "&dArr;",
8660: "&hArr;",
8704: "&forall;",
8706: "&part;",
8707: "&exist;",
8709: "&empty;",
8711: "&nabla;",
8712: "&isin;",
8713: "&notin;",
8715: "&ni;",
8719: "&prod;",
8721: "&sum;",
8722: "&minus;",
8727: "&lowast;",
8730: "&radic;",
8733: "&prop;",
8734: "&infin;",
8736: "&ang;",
8743: "&and;",
8744: "&or;",
8745: "&cap;",
8746: "&cup;",
8747: "&int;",
8756: "&there4;",
8764: "&sim;",
8773: "&cong;",
8776: "&asymp;",
8800: "&ne;",
8801: "&equiv;",
8804: "&le;",
8805: "&ge;",
8834: "&sub;",
8835: "&sup;",
8836: "&nsub;",
8838: "&sube;",
8839: "&supe;",
8853: "&oplus;",
8855: "&otimes;",
8869: "&perp;",
8901: "&sdot;",
8942: "&vellip;",
8968: "&lceil;",
8969: "&rceil;",
8970: "&lfloor;",
8971: "&rfloor;",
9001: "&lang;",
9002: "&rang;",
9674: "&loz;",
9824: "&spades;",
9827: "&clubs;",
9829: "&hearts;",
9830: "&diams;",
},
/**
* Lookup table to translate HTML entity codes to their byte values.
*
* @private
* @constant
*/
_entityToByte: {
"quot": 34,
"amp": 38,
"apos": 39,
"lt": 60,
"gt": 62,
"nbsp": 160,
"iexcl": 161,
"cent": 162,
"pound": 163,
"curren": 164,
"yen": 165,
"brvbar": 166,
"sect": 167,
"uml": 168,
"copy": 169,
"ordf": 170,
"laquo": 171,
"not": 172,
"shy": 173,
"reg": 174,
"macr": 175,
"deg": 176,
"plusmn": 177,
"sup2": 178,
"sup3": 179,
"acute": 180,
"micro": 181,
"para": 182,
"middot": 183,
"cedil": 184,
"sup1": 185,
"ordm": 186,
"raquo": 187,
"frac14": 188,
"frac12": 189,
"frac34": 190,
"iquest": 191,
"Agrave": 192,
"Aacute": 193,
"Acirc": 194,
"Atilde": 195,
"Auml": 196,
"Aring": 197,
"AElig": 198,
"Ccedil": 199,
"Egrave": 200,
"Eacute": 201,
"Ecirc": 202,
"Euml": 203,
"Igrave": 204,
"Iacute": 205,
"Icirc": 206,
"Iuml": 207,
"ETH": 208,
"Ntilde": 209,
"Ograve": 210,
"Oacute": 211,
"Ocirc": 212,
"Otilde": 213,
"Ouml": 214,
"times": 215,
"Oslash": 216,
"Ugrave": 217,
"Uacute": 218,
"Ucirc": 219,
"Uuml": 220,
"Yacute": 221,
"THORN": 222,
"szlig": 223,
"agrave": 224,
"aacute": 225,
"acirc": 226,
"atilde": 227,
"auml": 228,
"aring": 229,
"aelig": 230,
"ccedil": 231,
"egrave": 232,
"eacute": 233,
"ecirc": 234,
"euml": 235,
"igrave": 236,
"iacute": 237,
"icirc": 238,
"iuml": 239,
"eth": 240,
"ntilde": 241,
"ograve": 242,
"oacute": 243,
"ocirc": 244,
"otilde": 245,
"ouml": 246,
"divide": 247,
"oslash": 248,
"ugrave": 249,
"uacute": 250,
"ucirc": 251,
"uuml": 252,
"yacute": 253,
"thorn": 254,
"yuml": 255,
"OElig": 338,
"oelig": 339,
"Scaron": 352,
"scaron": 353,
"Yuml": 376,
"fnof": 402,
"circ": 710,
"tilde": 732,
"Alpha": 913,
"Beta": 914,
"Gamma": 915,
"Delta": 916,
"Epsilon": 917,
"Zeta": 918,
"Eta": 919,
"Theta": 920,
"Iota": 921,
"Kappa": 922,
"Lambda": 923,
"Mu": 924,
"Nu": 925,
"Xi": 926,
"Omicron": 927,
"Pi": 928,
"Rho": 929,
"Sigma": 931,
"Tau": 932,
"Upsilon": 933,
"Phi": 934,
"Chi": 935,
"Psi": 936,
"Omega": 937,
"alpha": 945,
"beta": 946,
"gamma": 947,
"delta": 948,
"epsilon": 949,
"zeta": 950,
"eta": 951,
"theta": 952,
"iota": 953,
"kappa": 954,
"lambda": 955,
"mu": 956,
"nu": 957,
"xi": 958,
"omicron": 959,
"pi": 960,
"rho": 961,
"sigmaf": 962,
"sigma": 963,
"tau": 964,
"upsilon": 965,
"phi": 966,
"chi": 967,
"psi": 968,
"omega": 969,
"thetasym": 977,
"upsih": 978,
"piv": 982,
"ensp": 8194,
"emsp": 8195,
"thinsp": 8201,
"zwnj": 8204,
"zwj": 8205,
"lrm": 8206,
"rlm": 8207,
"ndash": 8211,
"mdash": 8212,
"lsquo": 8216,
"rsquo": 8217,
"sbquo": 8218,
"ldquo": 8220,
"rdquo": 8221,
"bdquo": 8222,
"dagger": 8224,
"Dagger": 8225,
"bull": 8226,
"hellip": 8230,
"permil": 8240,
"prime": 8242,
"Prime": 8243,
"lsaquo": 8249,
"rsaquo": 8250,
"oline": 8254,
"frasl": 8260,
"euro": 8364,
"image": 8465,
"weierp": 8472,
"real": 8476,
"trade": 8482,
"alefsym": 8501,
"larr": 8592,
"uarr": 8593,
"rarr": 8594,
"darr": 8595,
"harr": 8596,
"crarr": 8629,
"lArr": 8656,
"uArr": 8657,
"rArr": 8658,
"dArr": 8659,
"hArr": 8660,
"forall": 8704,
"part": 8706,
"exist": 8707,
"empty": 8709,
"nabla": 8711,
"isin": 8712,
"notin": 8713,
"ni": 8715,
"prod": 8719,
"sum": 8721,
"minus": 8722,
"lowast": 8727,
"radic": 8730,
"prop": 8733,
"infin": 8734,
"ang": 8736,
"and": 8743,
"or": 8744,
"cap": 8745,
"cup": 8746,
"int": 8747,
"there4": 8756,
"sim": 8764,
"cong": 8773,
"asymp": 8776,
"ne": 8800,
"equiv": 8801,
"le": 8804,
"ge": 8805,
"sub": 8834,
"sup": 8835,
"nsub": 8836,
"sube": 8838,
"supe": 8839,
"oplus": 8853,
"otimes": 8855,
"perp": 8869,
"sdot": 8901,
"vellip": 8942,
"lceil": 8968,
"rceil": 8969,
"lfloor": 8970,
"rfloor": 8971,
"lang": 9001,
"rang": 9002,
"loz": 9674,
"spades": 9824,
"clubs": 9827,
"hearts": 9829,
"diams": 9830,
},
};
export default HTML;

View File

@ -1,158 +0,0 @@
import UAParser from "ua-parser-js";
/**
* HTTP operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const HTTP = {
/**
* @constant
* @default
*/
METHODS: [
"GET", "POST", "HEAD",
"PUT", "PATCH", "DELETE",
"CONNECT", "TRACE", "OPTIONS"
],
/**
* Strip HTTP headers operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runStripHeaders: function(input, args) {
let headerEnd = input.indexOf("\r\n\r\n");
headerEnd = (headerEnd < 0) ? input.indexOf("\n\n") + 2 : headerEnd + 4;
return (headerEnd < 2) ? input : input.slice(headerEnd, input.length);
},
/**
* Parse User Agent operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runParseUserAgent: function(input, args) {
const ua = UAParser(input);
return `Browser
Name: ${ua.browser.name || "unknown"}
Version: ${ua.browser.version || "unknown"}
Device
Model: ${ua.device.model || "unknown"}
Type: ${ua.device.type || "unknown"}
Vendor: ${ua.device.vendor || "unknown"}
Engine
Name: ${ua.engine.name || "unknown"}
Version: ${ua.engine.version || "unknown"}
OS
Name: ${ua.os.name || "unknown"}
Version: ${ua.os.version || "unknown"}
CPU
Architecture: ${ua.cpu.architecture || "unknown"}`;
},
/**
* @constant
* @default
*/
MODE: [
"Cross-Origin Resource Sharing",
"No CORS (limited to HEAD, GET or POST)",
],
/**
* Lookup table for HTTP modes
*
* @private
* @constant
*/
_modeLookup: {
"Cross-Origin Resource Sharing": "cors",
"No CORS (limited to HEAD, GET or POST)": "no-cors",
},
/**
* HTTP request operation.
*
* @author tlwr [toby@toby.codes]
* @author n1474335 [n1474335@gmail.com]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runHTTPRequest(input, args) {
const method = args[0],
url = args[1],
headersText = args[2],
mode = args[3],
showResponseMetadata = args[4];
if (url.length === 0) return "";
let headers = new Headers();
headersText.split(/\r?\n/).forEach(line => {
line = line.trim();
if (line.length === 0) return;
let split = line.split(":");
if (split.length !== 2) throw `Could not parse header in line: ${line}`;
headers.set(split[0].trim(), split[1].trim());
});
let config = {
method: method,
headers: headers,
mode: HTTP._modeLookup[mode],
cache: "no-cache",
};
if (method !== "GET" && method !== "HEAD") {
config.body = input;
}
return fetch(url, config)
.then(r => {
if (r.status === 0 && r.type === "opaque") {
return "Error: Null response. Try setting the connection mode to CORS.";
}
if (showResponseMetadata) {
let headers = "";
for (let pair of r.headers.entries()) {
headers += " " + pair[0] + ": " + pair[1] + "\n";
}
return r.text().then(b => {
return "####\n Status: " + r.status + " " + r.statusText +
"\n Exposed headers:\n" + headers + "####\n\n" + b;
});
}
return r.text();
})
.catch(e => {
return e.toString() +
"\n\nThis error could be caused by one of the following:\n" +
" - An invalid URL\n" +
" - Making a request to an insecure resource (HTTP) from a secure source (HTTPS)\n" +
" - Making a cross-origin request to a server which does not support CORS\n";
});
},
};
export default HTTP;

View File

@ -1,785 +0,0 @@
import Utils from "../Utils.js";
import CryptoApi from "babel-loader!crypto-api";
import MD6 from "node-md6";
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";
/**
* Hashing operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Hash = {
/**
* Generic hash function.
*
* @param {string} name
* @param {ArrayBuffer} input
* @param {Object} [options={}]
* @returns {string}
*/
runHash: function(name, input, options={}) {
const msg = Utils.arrayBufferToStr(input, false),
hasher = CryptoApi.getHasher(name, options);
hasher.update(msg);
return CryptoApi.encoder.toHex(hasher.finalize());
},
/**
* MD2 operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runMD2: function (input, args) {
return Hash.runHash("md2", input);
},
/**
* MD4 operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runMD4: function (input, args) {
return Hash.runHash("md4", input);
},
/**
* MD5 operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runMD5: function (input, args) {
return Hash.runHash("md5", input);
},
/**
* @constant
* @default
*/
MD6_SIZE: 256,
/**
* @constant
* @default
*/
MD6_LEVELS: 64,
/**
* MD6 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runMD6: function (input, args) {
const size = args[0],
levels = args[1],
key = args[2];
if (size < 0 || size > 512)
return "Size must be between 0 and 512";
if (levels < 0)
return "Levels must be greater than 0";
return MD6.getHashOfText(input, size, key, levels);
},
/**
* SHA0 operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runSHA0: function (input, args) {
return Hash.runHash("sha0", input);
},
/**
* SHA1 operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runSHA1: function (input, args) {
return Hash.runHash("sha1", input);
},
/**
* @constant
* @default
*/
SHA2_SIZE: ["512", "256", "384", "224", "512/256", "512/224"],
/**
* SHA2 operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runSHA2: function (input, args) {
const size = args[0];
return Hash.runHash("sha" + size, input);
},
/**
* @constant
* @default
*/
SHA3_SIZE: ["512", "384", "256", "224"],
/**
* SHA3 operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runSHA3: function (input, args) {
const size = parseInt(args[0], 10);
let algo;
switch (size) {
case 224:
algo = SHA3.sha3_224;
break;
case 384:
algo = SHA3.sha3_384;
break;
case 256:
algo = SHA3.sha3_256;
break;
case 512:
algo = SHA3.sha3_512;
break;
default:
return "Invalid size";
}
return algo(input);
},
/**
* @constant
* @default
*/
KECCAK_SIZE: ["512", "384", "256", "224"],
/**
* Keccak operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runKeccak: function (input, args) {
const size = parseInt(args[0], 10);
let algo;
switch (size) {
case 224:
algo = SHA3.keccak224;
break;
case 384:
algo = SHA3.keccak384;
break;
case 256:
algo = SHA3.keccak256;
break;
case 512:
algo = SHA3.keccak512;
break;
default:
return "Invalid size";
}
return algo(input);
},
/**
* @constant
* @default
*/
SHAKE_CAPACITY: ["256", "128"],
/**
* @constant
* @default
*/
SHAKE_SIZE: 512,
/**
* Shake operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runShake: function (input, args) {
const capacity = parseInt(args[0], 10),
size = args[1];
let algo;
if (size < 0)
return "Size must be greater than 0";
switch (capacity) {
case 128:
algo = SHA3.shake128;
break;
case 256:
algo = SHA3.shake256;
break;
default:
return "Invalid size";
}
return algo(input, size);
},
/**
* @constant
* @default
*/
RIPEMD_SIZE: ["320", "256", "160", "128"],
/**
* RIPEMD operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runRIPEMD: function (input, args) {
const size = args[0];
return Hash.runHash("ripemd" + size, input);
},
/**
* HAS-160 operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runHAS: function (input, args) {
return Hash.runHash("has160", input);
},
/**
* @constant
* @default
*/
WHIRLPOOL_VARIANT: ["Whirlpool", "Whirlpool-T", "Whirlpool-0"],
/**
* Whirlpool operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runWhirlpool: function (input, args) {
const variant = args[0].toLowerCase();
return Hash.runHash(variant, input);
},
/**
* @constant
* @default
*/
SNEFRU_ROUNDS: ["8", "4", "2"],
/**
* @constant
* @default
*/
SNEFRU_SIZE: ["256", "128"],
/**
* Snefru operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runSnefru: function (input, args) {
return Hash.runHash("snefru", input, {
rounds: args[0],
length: args[1]
});
},
/**
* CTPH operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runCTPH: function (input, args) {
return ctph.digest(input);
},
/**
* SSDEEP operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSSDEEP: function (input, args) {
return ssdeep.digest(input);
},
/**
* @constant
* @default
*/
DELIM_OPTIONS: ["Line feed", "CRLF", "Space", "Comma"],
/**
* Compare CTPH hashes operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {Number}
*/
runCompareCTPH: function (input, args) {
const samples = input.split(Utils.charRep(args[0]));
if (samples.length !== 2) throw "Incorrect number of samples.";
return ctph.similarity(samples[0], samples[1]);
},
/**
* Compare SSDEEP hashes operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {Number}
*/
runCompareSSDEEP: function (input, args) {
const samples = input.split(Utils.charRep(args[0]));
if (samples.length !== 2) throw "Incorrect number of samples.";
return ssdeep.similarity(samples[0], samples[1]);
},
/**
* @constant
* @default
*/
HMAC_FUNCTIONS: [
"MD2",
"MD4",
"MD5",
"SHA0",
"SHA1",
"SHA224",
"SHA256",
"SHA384",
"SHA512",
"SHA512/224",
"SHA512/256",
"RIPEMD128",
"RIPEMD160",
"RIPEMD256",
"RIPEMD320",
"HAS160",
"Whirlpool",
"Whirlpool-0",
"Whirlpool-T",
"Snefru"
],
/**
* HMAC operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runHMAC: function (input, args) {
const key = args[0],
hashFunc = args[1].toLowerCase(),
msg = Utils.arrayBufferToStr(input, false),
hasher = CryptoApi.getHasher(hashFunc);
// Horrible shim to fix constructor bug. Reported in nf404/crypto-api#8
hasher.reset = () => {
hasher.state = {};
const tmp = new hasher.constructor();
hasher.state = tmp.state;
};
const mac = CryptoApi.getHmac(CryptoApi.encoder.fromUtf(key), hasher);
mac.update(msg);
return CryptoApi.encoder.toHex(mac.finalize());
},
/**
* @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.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runAll: function (input, args) {
const arrayBuffer = input,
str = Utils.arrayBufferToStr(arrayBuffer, false),
byteArray = new Uint8Array(arrayBuffer),
output = "MD2: " + Hash.runMD2(arrayBuffer, []) +
"\nMD4: " + Hash.runMD4(arrayBuffer, []) +
"\nMD5: " + Hash.runMD5(arrayBuffer, []) +
"\nMD6: " + Hash.runMD6(str, []) +
"\nSHA0: " + Hash.runSHA0(arrayBuffer, []) +
"\nSHA1: " + Hash.runSHA1(arrayBuffer, []) +
"\nSHA2 224: " + Hash.runSHA2(arrayBuffer, ["224"]) +
"\nSHA2 256: " + Hash.runSHA2(arrayBuffer, ["256"]) +
"\nSHA2 384: " + Hash.runSHA2(arrayBuffer, ["384"]) +
"\nSHA2 512: " + Hash.runSHA2(arrayBuffer, ["512"]) +
"\nSHA3 224: " + Hash.runSHA3(arrayBuffer, ["224"]) +
"\nSHA3 256: " + Hash.runSHA3(arrayBuffer, ["256"]) +
"\nSHA3 384: " + Hash.runSHA3(arrayBuffer, ["384"]) +
"\nSHA3 512: " + Hash.runSHA3(arrayBuffer, ["512"]) +
"\nKeccak 224: " + Hash.runKeccak(arrayBuffer, ["224"]) +
"\nKeccak 256: " + Hash.runKeccak(arrayBuffer, ["256"]) +
"\nKeccak 384: " + Hash.runKeccak(arrayBuffer, ["384"]) +
"\nKeccak 512: " + Hash.runKeccak(arrayBuffer, ["512"]) +
"\nShake 128: " + Hash.runShake(arrayBuffer, ["128", 256]) +
"\nShake 256: " + Hash.runShake(arrayBuffer, ["256", 512]) +
"\nRIPEMD-128: " + Hash.runRIPEMD(arrayBuffer, ["128"]) +
"\nRIPEMD-160: " + Hash.runRIPEMD(arrayBuffer, ["160"]) +
"\nRIPEMD-256: " + Hash.runRIPEMD(arrayBuffer, ["256"]) +
"\nRIPEMD-320: " + Hash.runRIPEMD(arrayBuffer, ["320"]) +
"\nHAS-160: " + Hash.runHAS(arrayBuffer, []) +
"\nWhirlpool-0: " + Hash.runWhirlpool(arrayBuffer, ["Whirlpool-0"]) +
"\nWhirlpool-T: " + Hash.runWhirlpool(arrayBuffer, ["Whirlpool-T"]) +
"\nWhirlpool: " + Hash.runWhirlpool(arrayBuffer, ["Whirlpool"]) +
"\nSSDEEP: " + Hash.runSSDEEP(str) +
"\nCTPH: " + Hash.runCTPH(str) +
"\n\nChecksums:" +
"\nFletcher-8: " + Checksum.runFletcher8(byteArray, []) +
"\nFletcher-16: " + Checksum.runFletcher16(byteArray, []) +
"\nFletcher-32: " + Checksum.runFletcher32(byteArray, []) +
"\nFletcher-64: " + Checksum.runFletcher64(byteArray, []) +
"\nAdler-32: " + Checksum.runAdler32(byteArray, []) +
"\nCRC-16: " + Checksum.runCRC16(str, []) +
"\nCRC-32: " + Checksum.runCRC32(str, []);
return output;
},
/**
* Analyse hash operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runAnalyse: function(input, args) {
input = input.replace(/\s/g, "");
let output = "",
byteLength = input.length / 2,
bitLength = byteLength * 8,
possibleHashFunctions = [];
if (!/^[a-f0-9]+$/i.test(input)) {
return "Invalid hash";
}
output += "Hash length: " + input.length + "\n" +
"Byte length: " + byteLength + "\n" +
"Bit length: " + bitLength + "\n\n" +
"Based on the length, this hash could have been generated by one of the following hashing functions:\n";
switch (bitLength) {
case 4:
possibleHashFunctions = [
"Fletcher-4",
"Luhn algorithm",
"Verhoeff algorithm",
];
break;
case 8:
possibleHashFunctions = [
"Fletcher-8",
];
break;
case 16:
possibleHashFunctions = [
"BSD checksum",
"CRC-16",
"SYSV checksum",
"Fletcher-16"
];
break;
case 32:
possibleHashFunctions = [
"CRC-32",
"Fletcher-32",
"Adler-32",
];
break;
case 64:
possibleHashFunctions = [
"CRC-64",
"RIPEMD-64",
"SipHash",
];
break;
case 128:
possibleHashFunctions = [
"MD5",
"MD4",
"MD2",
"HAVAL-128",
"RIPEMD-128",
"Snefru",
"Tiger-128",
];
break;
case 160:
possibleHashFunctions = [
"SHA-1",
"SHA-0",
"FSB-160",
"HAS-160",
"HAVAL-160",
"RIPEMD-160",
"Tiger-160",
];
break;
case 192:
possibleHashFunctions = [
"Tiger",
"HAVAL-192",
];
break;
case 224:
possibleHashFunctions = [
"SHA-224",
"SHA3-224",
"ECOH-224",
"FSB-224",
"HAVAL-224",
];
break;
case 256:
possibleHashFunctions = [
"SHA-256",
"SHA3-256",
"BLAKE-256",
"ECOH-256",
"FSB-256",
"GOST",
"Grøstl-256",
"HAVAL-256",
"PANAMA",
"RIPEMD-256",
"Snefru",
];
break;
case 320:
possibleHashFunctions = [
"RIPEMD-320",
];
break;
case 384:
possibleHashFunctions = [
"SHA-384",
"SHA3-384",
"ECOH-384",
"FSB-384",
];
break;
case 512:
possibleHashFunctions = [
"SHA-512",
"SHA3-512",
"BLAKE-512",
"ECOH-512",
"FSB-512",
"Grøstl-512",
"JH",
"MD6",
"Spectral Hash",
"SWIFFT",
"Whirlpool",
];
break;
case 1024:
possibleHashFunctions = [
"Fowler-Noll-Vo",
];
break;
default:
possibleHashFunctions = [
"Unknown"
];
break;
}
return output + possibleHashFunctions.join("\n");
},
};
export default Hash;

View File

@ -1,203 +0,0 @@
import Utils from "../Utils.js";
import {fromHex} from "../lib/Hex";
/**
* Hexdump operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Hexdump = {
/**
* @constant
* @default
*/
WIDTH: 16,
/**
* @constant
* @default
*/
UPPER_CASE: false,
/**
* @constant
* @default
*/
INCLUDE_FINAL_LENGTH: false,
/**
* To Hexdump operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
const data = new Uint8Array(input);
const length = args[0] || Hexdump.WIDTH;
const upperCase = args[1];
const includeFinalLength = args[2];
let output = "", padding = 2;
for (let i = 0; i < data.length; i += length) {
const buff = data.slice(i, i+length);
let hexa = "";
for (let j = 0; j < buff.length; j++) {
hexa += Utils.hex(buff[j], padding) + " ";
}
let lineNo = Utils.hex(i, 8);
if (upperCase) {
hexa = hexa.toUpperCase();
lineNo = lineNo.toUpperCase();
}
output += lineNo + " " +
hexa.padEnd(length*(padding+1), " ") +
" |" + Utils.printable(Utils.byteArrayToChars(buff)).padEnd(buff.length, " ") + "|\n";
if (includeFinalLength && i+buff.length === data.length) {
output += Utils.hex(i+buff.length, 8) + "\n";
}
}
return output.slice(0, -1);
},
/**
* From Hexdump operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFrom: function(input, args) {
let output = [],
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 = fromHex(block[1].replace(/-/g, " "));
for (let i = 0; i < line.length; i++) {
output.push(line[i]);
}
}
// Is this a CyberChef hexdump or is it from a different tool?
const width = input.indexOf("\n");
const w = (width - 13) / 4;
// w should be the specified width of the hexdump and therefore a round number
if (Math.floor(w) !== w || input.indexOf("\r") !== -1 || output.indexOf(13) !== -1) {
if (ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
}
return output;
},
/**
* Highlight to hexdump
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightTo: function(pos, args) {
// Calculate overall selection
let w = args[0] || 16,
width = 14 + (w*4),
line = Math.floor(pos[0].start / w),
offset = pos[0].start % w,
start = 0,
end = 0;
pos[0].start = line*width + 10 + offset*3;
line = Math.floor(pos[0].end / w);
offset = pos[0].end % w;
if (offset === 0) {
line--;
offset = w;
}
pos[0].end = line*width + 10 + offset*3 - 1;
// Set up multiple selections for bytes
let startLineNum = Math.floor(pos[0].start / width);
const endLineNum = Math.floor(pos[0].end / width);
if (startLineNum === endLineNum) {
pos.push(pos[0]);
} else {
start = pos[0].start;
end = (startLineNum+1) * width - w - 5;
pos.push({ start: start, end: end });
while (end < pos[0].end) {
startLineNum++;
start = startLineNum * width + 10;
end = (startLineNum+1) * width - w - 5;
if (end > pos[0].end) end = pos[0].end;
pos.push({ start: start, end: end });
}
}
// Set up multiple selections for ASCII
let len = pos.length, lineNum = 0;
start = 0;
end = 0;
for (let i = 1; i < len; i++) {
lineNum = Math.floor(pos[i].start / width);
start = (((pos[i].start - (lineNum * width)) - 10) / 3) + (width - w -2) + (lineNum * width);
end = (((pos[i].end + 1 - (lineNum * width)) - 10) / 3) + (width - w -2) + (lineNum * width);
pos.push({ start: start, end: end });
}
return pos;
},
/**
* Highlight from hexdump
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightFrom: function(pos, args) {
const w = args[0] || 16;
const width = 14 + (w*4);
let line = Math.floor(pos[0].start / width);
let offset = pos[0].start % width;
if (offset < 10) { // In line number section
pos[0].start = line*w;
} else if (offset > 10+(w*3)) { // In ASCII section
pos[0].start = (line+1)*w;
} else { // In byte section
pos[0].start = line*w + Math.floor((offset-10)/3);
}
line = Math.floor(pos[0].end / width);
offset = pos[0].end % width;
if (offset < 10) { // In line number section
pos[0].end = line*w;
} else if (offset > 10+(w*3)) { // In ASCII section
pos[0].end = (line+1)*w;
} else { // In byte section
pos[0].end = line*w + Math.ceil((offset-10)/3);
}
return pos;
},
};
export default Hexdump;

File diff suppressed because it is too large Load Diff

View File

@ -1,125 +0,0 @@
import * as ExifParser from "exif-parser";
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";
/**
* Image operations.
*
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const Image = {
/**
* Extract EXIF operation.
*
* Extracts EXIF data from a byteArray, representing a JPG or a TIFF image.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runExtractEXIF(input, args) {
try {
const parser = ExifParser.create(input);
const result = parser.parse();
let lines = [];
for (let tagName in result.tags) {
let value = result.tags[tagName];
lines.push(`${tagName}: ${value}`);
}
const numTags = lines.length;
lines.unshift(`Found ${numTags} tags.\n`);
return lines.join("\n");
} catch (err) {
throw "Could not extract EXIF data from image: " + err;
}
},
/**
* Remove EXIF operation.
*
* Removes EXIF data from a byteArray, representing a JPG.
*
* @author David Moodie [davidmoodie12@gmail.com]
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runRemoveEXIF(input, args) {
// Do nothing if input is empty
if (input.length === 0) return input;
try {
return removeEXIF(input);
} catch (err) {
// Simply return input if no EXIF data is found
if (err === "Exif not found.") return input;
throw "Could not remove EXIF data from image: " + err;
}
},
/**
* @constant
* @default
*/
INPUT_FORMAT: ["Raw", "Base64", "Hex"],
/**
* Render Image operation.
*
* @author n1474335 [n1474335@gmail.com]
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runRenderImage(input, args) {
const inputFormat = args[0];
let dataURI = "data:";
if (!input.length) return "";
// Convert input to raw bytes
switch (inputFormat) {
case "Hex":
input = fromHex(input);
break;
case "Base64":
// Don't trust the Base64 entered by the user.
// Unwrap it first, then re-encode later.
input = fromBase64(input, null, "byteArray");
break;
case "Raw":
default:
input = Utils.strToByteArray(input);
break;
}
// Determine file type
const type = FileType.magicType(input);
if (type && type.mime.indexOf("image") === 0) {
dataURI += type.mime + ";";
} else {
throw "Invalid file type";
}
// Add image data to URI
dataURI += "base64," + toBase64(input);
return "<img src='" + dataURI + "'>";
},
};
export default Image;

View File

@ -1,105 +0,0 @@
/**
* MAC address operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const MAC = {
/**
* @constant
* @default
*/
OUTPUT_CASE: ["Both", "Upper only", "Lower only"],
/**
* @constant
* @default
*/
NO_DELIM: true,
/**
* @constant
* @default
*/
DASH_DELIM: true,
/**
* @constant
* @default
*/
COLON_DELIM: true,
/**
* @constant
* @default
*/
CISCO_STYLE: false,
/**
* @constant
* @default
*/
IPV6_INTERFACE_ID: false,
/**
* Format MAC addresses operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runFormat: function(input, args) {
if (!input) return "";
let outputCase = args[0],
noDelim = args[1],
dashDelim = args[2],
colonDelim = args[3],
ciscoStyle = args[4],
ipv6IntID = args[5],
outputList = [],
macs = input.toLowerCase().split(/[,\s\r\n]+/);
macs.forEach(function(mac) {
let cleanMac = mac.replace(/[:.-]+/g, ""),
macHyphen = cleanMac.replace(/(.{2}(?=.))/g, "$1-"),
macColon = cleanMac.replace(/(.{2}(?=.))/g, "$1:"),
macCisco = cleanMac.replace(/(.{4}(?=.))/g, "$1."),
macIPv6 = cleanMac.slice(0, 6) + "fffe" + cleanMac.slice(6);
macIPv6 = macIPv6.replace(/(.{4}(?=.))/g, "$1:");
let bite = parseInt(macIPv6.slice(0, 2), 16) ^ 2;
bite = bite.toString(16).padStart(2, "0");
macIPv6 = bite + macIPv6.slice(2);
if (outputCase === "Lower only") {
if (noDelim) outputList.push(cleanMac);
if (dashDelim) outputList.push(macHyphen);
if (colonDelim) outputList.push(macColon);
if (ciscoStyle) outputList.push(macCisco);
if (ipv6IntID) outputList.push(macIPv6);
} else if (outputCase === "Upper only") {
if (noDelim) outputList.push(cleanMac.toUpperCase());
if (dashDelim) outputList.push(macHyphen.toUpperCase());
if (colonDelim) outputList.push(macColon.toUpperCase());
if (ciscoStyle) outputList.push(macCisco.toUpperCase());
if (ipv6IntID) outputList.push(macIPv6.toUpperCase());
} else {
if (noDelim) outputList.push(cleanMac, cleanMac.toUpperCase());
if (dashDelim) outputList.push(macHyphen, macHyphen.toUpperCase());
if (colonDelim) outputList.push(macColon, macColon.toUpperCase());
if (ciscoStyle) outputList.push(macCisco, macCisco.toUpperCase());
if (ipv6IntID) outputList.push(macIPv6, macIPv6.toUpperCase());
}
outputList.push(
"" // Empty line to delimit groups
);
});
// Return the data as a string
return outputList.join("\n");
},
};
export default MAC;

View File

@ -1,213 +0,0 @@
/**
* Microsoft operations.
*
* @author bmwhitn [brian.m.whitney@outlook.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const MS = {
/**
* @constant
* @default
*/
D_DECODE: [
"",
"",
"",
"",
"",
"",
"",
"",
"",
"\x57\x6E\x7B",
"\x4A\x4C\x41",
"\x0B\x0B\x0B",
"\x0C\x0C\x0C",
"\x4A\x4C\x41",
"\x0E\x0E\x0E",
"\x0F\x0F\x0F",
"\x10\x10\x10",
"\x11\x11\x11",
"\x12\x12\x12",
"\x13\x13\x13",
"\x14\x14\x14",
"\x15\x15\x15",
"\x16\x16\x16",
"\x17\x17\x17",
"\x18\x18\x18",
"\x19\x19\x19",
"\x1A\x1A\x1A",
"\x1B\x1B\x1B",
"\x1C\x1C\x1C",
"\x1D\x1D\x1D",
"\x1E\x1E\x1E",
"\x1F\x1F\x1F",
"\x2E\x2D\x32",
"\x47\x75\x30",
"\x7A\x52\x21",
"\x56\x60\x29",
"\x42\x71\x5B",
"\x6A\x5E\x38",
"\x2F\x49\x33",
"\x26\x5C\x3D",
"\x49\x62\x58",
"\x41\x7D\x3A",
"\x34\x29\x35",
"\x32\x36\x65",
"\x5B\x20\x39",
"\x76\x7C\x5C",
"\x72\x7A\x56",
"\x43\x7F\x73",
"\x38\x6B\x66",
"\x39\x63\x4E",
"\x70\x33\x45",
"\x45\x2B\x6B",
"\x68\x68\x62",
"\x71\x51\x59",
"\x4F\x66\x78",
"\x09\x76\x5E",
"\x62\x31\x7D",
"\x44\x64\x4A",
"\x23\x54\x6D",
"\x75\x43\x71",
"\x4A\x4C\x41",
"\x7E\x3A\x60",
"\x4A\x4C\x41",
"\x5E\x7E\x53",
"\x40\x4C\x40",
"\x77\x45\x42",
"\x4A\x2C\x27",
"\x61\x2A\x48",
"\x5D\x74\x72",
"\x22\x27\x75",
"\x4B\x37\x31",
"\x6F\x44\x37",
"\x4E\x79\x4D",
"\x3B\x59\x52",
"\x4C\x2F\x22",
"\x50\x6F\x54",
"\x67\x26\x6A",
"\x2A\x72\x47",
"\x7D\x6A\x64",
"\x74\x39\x2D",
"\x54\x7B\x20",
"\x2B\x3F\x7F",
"\x2D\x38\x2E",
"\x2C\x77\x4C",
"\x30\x67\x5D",
"\x6E\x53\x7E",
"\x6B\x47\x6C",
"\x66\x34\x6F",
"\x35\x78\x79",
"\x25\x5D\x74",
"\x21\x30\x43",
"\x64\x23\x26",
"\x4D\x5A\x76",
"\x52\x5B\x25",
"\x63\x6C\x24",
"\x3F\x48\x2B",
"\x7B\x55\x28",
"\x78\x70\x23",
"\x29\x69\x41",
"\x28\x2E\x34",
"\x73\x4C\x09",
"\x59\x21\x2A",
"\x33\x24\x44",
"\x7F\x4E\x3F",
"\x6D\x50\x77",
"\x55\x09\x3B",
"\x53\x56\x55",
"\x7C\x73\x69",
"\x3A\x35\x61",
"\x5F\x61\x63",
"\x65\x4B\x50",
"\x46\x58\x67",
"\x58\x3B\x51",
"\x31\x57\x49",
"\x69\x22\x4F",
"\x6C\x6D\x46",
"\x5A\x4D\x68",
"\x48\x25\x7C",
"\x27\x28\x36",
"\x5C\x46\x70",
"\x3D\x4A\x6E",
"\x24\x32\x7A",
"\x79\x41\x2F",
"\x37\x3D\x5F",
"\x60\x5F\x4B",
"\x51\x4F\x5A",
"\x20\x42\x2C",
"\x36\x65\x57"
],
/**
* @constant
* @default
*/
D_COMBINATION: [
0, 1, 2, 0, 1, 2, 1, 2, 2, 1, 2, 1, 0, 2, 1, 2, 0, 2, 1, 2, 0, 0, 1, 2, 2, 1, 0, 2, 1, 2, 2, 1,
0, 0, 2, 1, 2, 1, 2, 0, 2, 0, 0, 1, 2, 0, 2, 1, 0, 2, 1, 2, 0, 0, 1, 2, 2, 0, 0, 1, 2, 0, 2, 1
],
/**
* Decodes Microsoft Encoded Script files that can be read and executed by cscript.exe/wscript.exe.
* This is a conversion of a Python script that was originally created by Didier Stevens
* (https://DidierStevens.com).
*
* @private
* @param {string} data
* @returns {string}
*/
_decode: function (data) {
let result = [];
let index = -1;
data = data.replace(/@&/g, String.fromCharCode(10))
.replace(/@#/g, String.fromCharCode(13))
.replace(/@\*/g, ">")
.replace(/@!/g, "<")
.replace(/@\$/g, "@");
for (let i = 0; i < data.length; i++) {
let byte = data.charCodeAt(i);
let char = data.charAt(i);
if (byte < 128) {
index++;
}
if ((byte === 9 || byte > 31 && byte < 128) &&
byte !== 60 &&
byte !== 62 &&
byte !== 64) {
char = MS.D_DECODE[byte].charAt(MS.D_COMBINATION[index % 64]);
}
result.push(char);
}
return result.join("");
},
/**
* Microsoft Script Decoder operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runDecodeScript: function (input, args) {
let matcher = /#@~\^.{6}==(.+).{6}==\^#~@/;
let encodedData = matcher.exec(input);
if (encodedData){
return MS._decode(encodedData[1]);
} else {
return "";
}
}
};
export default MS;

View File

@ -1,190 +0,0 @@
import Utils from "../Utils.js";
/**
* Morse Code translation operations.
*
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const MorseCode = {
/**
* @constant
* @default
*/
FORMAT_OPTIONS: ["-/.", "_/.", "Dash/Dot", "DASH/DOT", "dash/dot"],
/**
* @constant
* @default
*/
LETTER_DELIM_OPTIONS: ["Space", "Line feed", "CRLF", "Forward slash", "Backslash", "Comma", "Semi-colon", "Colon"],
/**
* @constant
* @default
*/
WORD_DELIM_OPTIONS: ["Line feed", "CRLF", "Forward slash", "Backslash", "Comma", "Semi-colon", "Colon"],
/**
* @constant
* @default
*/
MORSE_TABLE: {
"A": "<dot><dash>",
"B": "<dash><dot><dot><dot>",
"C": "<dash><dot><dash><dot>",
"D": "<dash><dot><dot>",
"E": "<dot>",
"F": "<dot><dot><dash><dot>",
"G": "<dash><dash><dot>",
"H": "<dot><dot><dot><dot>",
"I": "<dot><dot>",
"J": "<dot><dash><dash><dash>",
"K": "<dash><dot><dash>",
"L": "<dot><dash><dot><dot>",
"M": "<dash><dash>",
"N": "<dash><dot>",
"O": "<dash><dash><dash>",
"P": "<dot><dash><dash><dot>",
"Q": "<dash><dash><dot><dash>",
"R": "<dot><dash><dot>",
"S": "<dot><dot><dot>",
"T": "<dash>",
"U": "<dot><dot><dash>",
"V": "<dot><dot><dot><dash>",
"W": "<dot><dash><dash>",
"X": "<dash><dot><dot><dash>",
"Y": "<dash><dot><dash><dash>",
"Z": "<dash><dash><dot><dot>",
"1": "<dot><dash><dash><dash><dash>",
"2": "<dot><dot><dash><dash><dash>",
"3": "<dot><dot><dot><dash><dash>",
"4": "<dot><dot><dot><dot><dash>",
"5": "<dot><dot><dot><dot><dot>",
"6": "<dash><dot><dot><dot><dot>",
"7": "<dash><dash><dot><dot><dot>",
"8": "<dash><dash><dash><dot><dot>",
"9": "<dash><dash><dash><dash><dot>",
"0": "<dash><dash><dash><dash><dash>",
".": "<dot><dash><dot><dash><dot><dash>",
",": "<dash><dash><dot><dot><dash><dash>",
":": "<dash><dash><dash><dot><dot><dot>",
";": "<dash><dot><dash><dot><dash><dot>",
"!": "<dash><dot><dash><dot><dash><dash>",
"?": "<dot><dot><dash><dash><dot><dot>",
"'": "<dot><dash><dash><dash><dash><dot>",
"\"": "<dot><dash><dot><dot><dash><dot>",
"/": "<dash><dot><dot><dash><dot>",
"-": "<dash><dot><dot><dot><dot><dash>",
"+": "<dot><dash><dot><dash><dot>",
"(": "<dash><dot><dash><dash><dot>",
")": "<dash><dot><dash><dash><dot><dash>",
"@": "<dot><dash><dash><dot><dash><dot>",
"=": "<dash><dot><dot><dot><dash>",
"&": "<dot><dash><dot><dot><dot>",
"_": "<dot><dot><dash><dash><dot><dash>",
"$": "<dot><dot><dot><dash><dot><dot><dash>"
},
/**
* To Morse Code operation.
*
* @param {number} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
const format = args[0].split("/");
const dash = format[0];
const dot = format[1];
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) {
let words = line.split(/ +/);
words = Array.prototype.map.call(words, function(word) {
const letters = Array.prototype.map.call(word, function(character) {
const letter = character.toUpperCase();
if (typeof MorseCode.MORSE_TABLE[letter] == "undefined") {
return "";
}
return MorseCode.MORSE_TABLE[letter];
});
return letters.join("<ld>");
});
line = words.join("<wd>");
return line;
});
input = input.join("\n");
input = input.replace(
/<dash>|<dot>|<ld>|<wd>/g,
function(match) {
switch (match) {
case "<dash>": return dash;
case "<dot>": return dot;
case "<ld>": return letterDelim;
case "<wd>": return wordDelim;
}
}
);
return input;
},
/**
* From Morse Code operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runFrom: (function() {
let reversedTable = null;
const reverseTable = function() {
reversedTable = {};
for (const letter in MorseCode.MORSE_TABLE) {
const signal = MorseCode.MORSE_TABLE[letter];
reversedTable[signal] = letter;
}
};
return function(input, args) {
if (reversedTable === null) {
reverseTable();
}
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>");
let words = input.split(wordDelim);
words = Array.prototype.map.call(words, function(word) {
const signals = word.split(letterDelim);
const letters = signals.map(function(signal) {
return reversedTable[signal];
});
return letters.join("");
});
words = words.join(" ");
return words;
};
})(),
};
export default MorseCode;

View File

@ -1,70 +0,0 @@
/**
* NetBIOS operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const NetBIOS = {
/**
* @constant
* @default
*/
OFFSET: 65,
/**
* Encode NetBIOS Name operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runEncodeName: function(input, args) {
let output = [],
offset = args[0];
if (input.length <= 16) {
let len = input.length;
input.length = 16;
input.fill(32, len, 16);
for (let i = 0; i < input.length; i++) {
output.push((input[i] >> 4) + offset);
output.push((input[i] & 0xf) + offset);
}
}
return output;
},
/**
* Decode NetBIOS Name operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runDecodeName: function(input, args) {
let output = [],
offset = args[0];
if (input.length <= 32 && (input.length % 2) === 0) {
for (let i = 0; i < input.length; i += 2) {
output.push((((input[i] & 0xff) - offset) << 4) |
(((input[i + 1] & 0xff) - offset) & 0xf));
}
for (let i = output.length - 1; i > 0; i--) {
if (output[i] === 32) output.splice(i, i);
else break;
}
}
return output;
},
};
export default NetBIOS;

View File

@ -1,85 +0,0 @@
/**
* Numberwang operations.
*
* @author Unknown Male 282
* @namespace
*/
const Numberwang = {
/**
* Numberwang operation. Remain indoors.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run: function(input, args) {
let output;
if (!input) {
output = "Let's play Wangernumb!";
} else {
const match = input.match(/(f0rty-s1x|shinty-six|filth-hundred and neeb|-?√?\d+(\.\d+)?i?([a-z]?)%?)/i);
if (match) {
if (match[3]) output = match[0] + "! That's AlphaNumericWang!";
else output = match[0] + "! That's Numberwang!";
} else {
// That's a bad miss!
output = "Sorry, that's not Numberwang. Let's rotate the board!";
}
}
const rand = Math.floor(Math.random() * Numberwang._didYouKnow.length);
return output + "\n\nDid you know: " + Numberwang._didYouKnow[rand];
},
/**
* Taken from http://numberwang.wikia.com/wiki/Numberwang_Wikia
*
* @private
* @constant
*/
_didYouKnow: [
"Numberwang, contrary to popular belief, is a fruit and not a vegetable.",
"Robert Webb once got WordWang while presenting an episode of Numberwang.",
"The 6705th digit of pi is Numberwang.",
"Numberwang was invented on a Sevenday.",
"Contrary to popular belief, Albert Einstein always got good grades in Numberwang at school. He once scored ^4$ on a test.",
"680 asteroids have been named after Numberwang.",
"Archimedes is most famous for proclaiming \"That's Numberwang!\" during an epiphany about water displacement he had while taking a bath.",
"Numberwang Day is celebrated in Japan on every day of the year apart from June 6.",
"Biologists recently discovered Numberwang within a strand of human DNA.",
"Numbernot is a special type of non-Numberwang number. It is divisible by 3 and the letter \"y\".",
"Julie once got 612.04 Numberwangs in a single episode of Emmerdale.",
"In India, it is traditional to shout out \"Numberwang!\" instead of checkmate during games of chess.",
"There is a rule on Countdown which states that if you get Numberwang in the numbers round, you automatically win. It has only ever been invoked twice.",
"\"Numberwang\" was the third-most common baby name for a brief period in 1722.",
"\"The Lion King\" was loosely based on Numberwang.",
"\"A Numberwang a day keeps the doctor away\" is how Donny Cosy, the oldest man in the world, explained how he was in such good health at the age of 136.",
"The \"number lock\" button on a keyboard is based on the popular round of the same name in \"Numberwang\".",
"Cambridge became the first university to offer a course in Numberwang in 1567.",
"Schrödinger's Numberwang is a number that has been confusing dentists for centuries.",
"\"Harry Potter and the Numberwang of Numberwang\" was rejected by publishers -41 times before it became a bestseller.",
"\"Numberwang\" is the longest-running British game show in history; it has aired 226 seasons, each containing 19 episodes, which makes a grand total of 132 episodes.",
"The triple Numberwang bonus was discovered by archaeologist Thomas Jefferson in Somerset.",
"Numberwang is illegal in parts of Czechoslovakia.",
"Numberwang was discovered in India in the 12th century.",
"Numberwang has the chemical formula Zn4SO2(HgEs)3.",
"The first pack of cards ever created featured two \"Numberwang\" cards instead of jokers.",
"Julius Caesar was killed by an overdose of Numberwang.",
"The most Numberwang musical note is G#.",
"In 1934, the forty-third Google Doodle promoted the upcoming television show \"Numberwang on Ice\".",
"A recent psychology study found that toddlers were 17% faster at identifying numbers which were Numberwang.",
"There are 700 ways to commit a foul in the television show \"Numberwang\". All 700 of these fouls were committed by Julie in one single episode in 1473.",
"Astronomers suspect God is Numberwang.",
"Numberwang is the official beverage of Canada.",
"In the pilot episode of \"The Price is Right\", if a contestant got the value of an item exactly right they were told \"That's Numberwang!\" and immediately won ₹5.7032.",
"The first person to get three Numberwangs in a row was Madonna.",
"\"Numberwang\" has the code U+46402 in Unicode.",
"The musical note \"Numberwang\" is between D# and E♮.",
"Numberwang was first played on the moon in 1834.",
],
};
export default Numberwang;

View File

@ -1,311 +0,0 @@
/**
* Operating system operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const OS = {
/**
* Parse UNIX file permissions operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runParseUnixPerms: function(input, args) {
let perms = {
d: false, // directory
sl: false, // symbolic link
np: false, // named pipe
s: false, // socket
cd: false, // character device
bd: false, // block device
dr: false, // door
sb: false, // sticky bit
su: false, // setuid
sg: false, // setgid
ru: false, // read user
wu: false, // write user
eu: false, // execute user
rg: false, // read group
wg: false, // write group
eg: false, // execute group
ro: false, // read other
wo: false, // write other
eo: false // execute other
},
d = 0,
u = 0,
g = 0,
o = 0,
output = "",
octal = null,
textual = null;
if (input.search(/\s*[0-7]{1,4}\s*/i) === 0) {
// Input is octal
octal = input.match(/\s*([0-7]{1,4})\s*/i)[1];
if (octal.length === 4) {
d = parseInt(octal[0], 8);
u = parseInt(octal[1], 8);
g = parseInt(octal[2], 8);
o = parseInt(octal[3], 8);
} else {
if (octal.length > 0) u = parseInt(octal[0], 8);
if (octal.length > 1) g = parseInt(octal[1], 8);
if (octal.length > 2) o = parseInt(octal[2], 8);
}
perms.su = d >> 2 & 0x1;
perms.sg = d >> 1 & 0x1;
perms.sb = d & 0x1;
perms.ru = u >> 2 & 0x1;
perms.wu = u >> 1 & 0x1;
perms.eu = u & 0x1;
perms.rg = g >> 2 & 0x1;
perms.wg = g >> 1 & 0x1;
perms.eg = g & 0x1;
perms.ro = o >> 2 & 0x1;
perms.wo = o >> 1 & 0x1;
perms.eo = o & 0x1;
} else if (input.search(/\s*[dlpcbDrwxsStT-]{1,10}\s*/) === 0) {
// Input is textual
textual = input.match(/\s*([dlpcbDrwxsStT-]{1,10})\s*/)[1];
switch (textual[0]) {
case "d":
perms.d = true;
break;
case "l":
perms.sl = true;
break;
case "p":
perms.np = true;
break;
case "s":
perms.s = true;
break;
case "c":
perms.cd = true;
break;
case "b":
perms.bd = true;
break;
case "D":
perms.dr = true;
break;
}
if (textual.length > 1) perms.ru = textual[1] === "r";
if (textual.length > 2) perms.wu = textual[2] === "w";
if (textual.length > 3) {
switch (textual[3]) {
case "x":
perms.eu = true;
break;
case "s":
perms.eu = true;
perms.su = true;
break;
case "S":
perms.su = true;
break;
}
}
if (textual.length > 4) perms.rg = textual[4] === "r";
if (textual.length > 5) perms.wg = textual[5] === "w";
if (textual.length > 6) {
switch (textual[6]) {
case "x":
perms.eg = true;
break;
case "s":
perms.eg = true;
perms.sg = true;
break;
case "S":
perms.sg = true;
break;
}
}
if (textual.length > 7) perms.ro = textual[7] === "r";
if (textual.length > 8) perms.wo = textual[8] === "w";
if (textual.length > 9) {
switch (textual[9]) {
case "x":
perms.eo = true;
break;
case "t":
perms.eo = true;
perms.sb = true;
break;
case "T":
perms.sb = true;
break;
}
}
} else {
return "Invalid input format.\nPlease enter the permissions in either octal (e.g. 755) or textual (e.g. drwxr-xr-x) format.";
}
output += "Textual representation: " + OS._permsToStr(perms);
output += "\nOctal representation: " + OS._permsToOctal(perms);
// File type
if (textual) {
output += "\nFile type: " + OS._ftFromPerms(perms);
}
// setuid, setgid
if (perms.su) {
output += "\nThe setuid flag is set";
}
if (perms.sg) {
output += "\nThe setgid flag is set";
}
// sticky bit
if (perms.sb) {
output += "\nThe sticky bit is set";
}
// Permission matrix
output += "\n\n +---------+-------+-------+-------+\n" +
" | | User | Group | Other |\n" +
" +---------+-------+-------+-------+\n" +
" | Read | " + (perms.ru ? "X" : " ") + " | " + (perms.rg ? "X" : " ") + " | " + (perms.ro ? "X" : " ") + " |\n" +
" +---------+-------+-------+-------+\n" +
" | Write | " + (perms.wu ? "X" : " ") + " | " + (perms.wg ? "X" : " ") + " | " + (perms.wo ? "X" : " ") + " |\n" +
" +---------+-------+-------+-------+\n" +
" | Execute | " + (perms.eu ? "X" : " ") + " | " + (perms.eg ? "X" : " ") + " | " + (perms.eo ? "X" : " ") + " |\n" +
" +---------+-------+-------+-------+\n";
return output;
},
/**
* Given a permissions object dictionary, generates a textual permissions string.
*
* @private
* @param {Object} perms
* @returns {string}
*/
_permsToStr: function(perms) {
let str = "",
type = "-";
if (perms.d) type = "d";
if (perms.sl) type = "l";
if (perms.np) type = "p";
if (perms.s) type = "s";
if (perms.cd) type = "c";
if (perms.bd) type = "b";
if (perms.dr) type = "D";
str = type;
str += perms.ru ? "r" : "-";
str += perms.wu ? "w" : "-";
if (perms.eu && perms.su) {
str += "s";
} else if (perms.su) {
str += "S";
} else if (perms.eu) {
str += "x";
} else {
str += "-";
}
str += perms.rg ? "r" : "-";
str += perms.wg ? "w" : "-";
if (perms.eg && perms.sg) {
str += "s";
} else if (perms.sg) {
str += "S";
} else if (perms.eg) {
str += "x";
} else {
str += "-";
}
str += perms.ro ? "r" : "-";
str += perms.wo ? "w" : "-";
if (perms.eo && perms.sb) {
str += "t";
} else if (perms.sb) {
str += "T";
} else if (perms.eo) {
str += "x";
} else {
str += "-";
}
return str;
},
/**
* Given a permissions object dictionary, generates an octal permissions string.
*
* @private
* @param {Object} perms
* @returns {string}
*/
_permsToOctal: function(perms) {
let d = 0,
u = 0,
g = 0,
o = 0;
if (perms.su) d += 4;
if (perms.sg) d += 2;
if (perms.sb) d += 1;
if (perms.ru) u += 4;
if (perms.wu) u += 2;
if (perms.eu) u += 1;
if (perms.rg) g += 4;
if (perms.wg) g += 2;
if (perms.eg) g += 1;
if (perms.ro) o += 4;
if (perms.wo) o += 2;
if (perms.eo) o += 1;
return d.toString() + u.toString() + g.toString() + o.toString();
},
/**
* Given a permissions object dictionary, returns the file type.
*
* @private
* @param {Object} perms
* @returns {string}
*/
_ftFromPerms: function(perms) {
if (perms.d) return "Directory";
if (perms.sl) return "Symbolic link";
if (perms.np) return "Named pipe";
if (perms.s) return "Socket";
if (perms.cd) return "Character device";
if (perms.bd) return "Block device";
if (perms.dr) return "Door";
return "Regular file";
},
};
export default OS;

View File

@ -1,56 +0,0 @@
import otp from "otp";
import Base64 from "./Base64.js";
/**
* One-Time Password operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const OTP = {
/**
* Generate TOTP operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runTOTP: function(input, args) {
const otpObj = otp({
name: args[0],
keySize: args[1],
codeLength: args[2],
secret: Base64.runTo32(input, []),
epoch: args[3],
timeSlice: args[4]
});
return `URI: ${otpObj.totpURL}\n\nPassword: ${otpObj.totp()}`;
},
/**
* Generate HOTP operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runHOTP: function(input, args) {
const otpObj = otp({
name: args[0],
keySize: args[1],
codeLength: args[2],
secret: Base64.runTo32(input, []),
});
const counter = args[3];
return `URI: ${otpObj.hotpURL}\n\nPassword: ${otpObj.hotp(counter)}`;
},
};
export default OTP;

View File

@ -1,160 +0,0 @@
/**
* PHP operations.
*
* @author Jarmo van Lenthe [github.com/jarmovanlenthe]
* @copyright Jarmo van Lenthe
* @license Apache-2.0
*
* @namespace
*/
const PHP = {
/**
* @constant
* @default
*/
OUTPUT_VALID_JSON: true,
/**
* PHP Deserialize operation.
*
* This Javascript implementation is based on the Python implementation by
* Armin Ronacher (2016), who released it under the 3-Clause BSD license.
* See: https://github.com/mitsuhiko/phpserialize/
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runDeserialize: function (input, args) {
/**
* Recursive method for deserializing.
* @returns {*}
*/
function handleInput() {
/**
* Read `length` characters from the input, shifting them out the input.
* @param length
* @returns {string}
*/
function read(length) {
let result = "";
for (let idx = 0; idx < length; idx++) {
let char = inputPart.shift();
if (char === undefined) {
throw "End of input reached before end of script";
}
result += char;
}
return result;
}
/**
* Read characters from the input until `until` is found.
* @param until
* @returns {string}
*/
function readUntil(until) {
let result = "";
for (;;) {
let char = read(1);
if (char === until) {
break;
} else {
result += char;
}
}
return result;
}
/**
* Read characters from the input that must be equal to `expect`
* @param expect
* @returns {string}
*/
function expect(expect) {
let result = read(expect.length);
if (result !== expect) {
throw "Unexpected input found";
}
return result;
}
/**
* Helper function to handle deserialized arrays.
* @returns {Array}
*/
function handleArray() {
let items = parseInt(readUntil(":"), 10) * 2;
expect("{");
let result = [];
let isKey = true;
let lastItem = null;
for (let idx = 0; idx < items; idx++) {
let item = handleInput();
if (isKey) {
lastItem = item;
isKey = false;
} else {
let numberCheck = lastItem.match(/[0-9]+/);
if (args[0] && numberCheck && numberCheck[0].length === lastItem.length) {
result.push("\"" + lastItem + "\": " + item);
} else {
result.push(lastItem + ": " + item);
}
isKey = true;
}
}
expect("}");
return result;
}
let kind = read(1).toLowerCase();
switch (kind) {
case "n":
expect(";");
return "";
case "i":
case "d":
case "b": {
expect(":");
let data = readUntil(";");
if (kind === "b") {
return (parseInt(data, 10) !== 0);
}
return data;
}
case "a":
expect(":");
return "{" + handleArray() + "}";
case "s": {
expect(":");
let length = readUntil(":");
expect("\"");
let value = read(length);
expect("\";");
if (args[0]) {
return "\"" + value.replace(/"/g, "\\\"") + "\"";
} else {
return "\"" + value + "\"";
}
}
default:
throw "Unknown type: " + kind;
}
}
let inputPart = input.split("");
return handleInput();
}
};
export default PHP;

View File

@ -1,348 +0,0 @@
import Utils from "../Utils.js";
import {fromBase64} from "../lib/Base64";
import {toHex, fromHex} from "../lib/Hex";
import * as r from "jsrsasign";
/**
* Public Key operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const PublicKey = {
/**
* @constant
* @default
*/
X509_INPUT_FORMAT: ["PEM", "DER Hex", "Base64", "Raw"],
/**
* Parse X.509 certificate operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runParseX509: function (input, args) {
if (!input.length) {
return "No input";
}
let cert = new r.X509(),
inputFormat = args[0];
switch (inputFormat) {
case "DER Hex":
input = input.replace(/\s/g, "");
cert.readCertHex(input);
break;
case "PEM":
cert.readCertPEM(input);
break;
case "Base64":
cert.readCertHex(toHex(fromBase64(input, null, "byteArray"), ""));
break;
case "Raw":
cert.readCertHex(toHex(Utils.strToByteArray(input), ""));
break;
default:
throw "Undefined input format";
}
let sn = cert.getSerialNumberHex(),
issuer = cert.getIssuerString(),
subject = cert.getSubjectString(),
pk = cert.getPublicKey(),
pkFields = [],
pkStr = "",
sig = cert.getSignatureValueHex(),
sigStr = "",
extensions = "";
// Public Key fields
pkFields.push({
key: "Algorithm",
value: pk.type
});
if (pk.type === "EC") { // ECDSA
pkFields.push({
key: "Curve Name",
value: pk.curveName
});
pkFields.push({
key: "Length",
value: (((new r.BigInteger(pk.pubKeyHex, 16)).bitLength()-3) /2) + " bits"
});
pkFields.push({
key: "pub",
value: PublicKey._formatByteStr(pk.pubKeyHex, 16, 18)
});
} else if (pk.type === "DSA") { // DSA
pkFields.push({
key: "pub",
value: PublicKey._formatByteStr(pk.y.toString(16), 16, 18)
});
pkFields.push({
key: "P",
value: PublicKey._formatByteStr(pk.p.toString(16), 16, 18)
});
pkFields.push({
key: "Q",
value: PublicKey._formatByteStr(pk.q.toString(16), 16, 18)
});
pkFields.push({
key: "G",
value: PublicKey._formatByteStr(pk.g.toString(16), 16, 18)
});
} else if (pk.e) { // RSA
pkFields.push({
key: "Length",
value: pk.n.bitLength() + " bits"
});
pkFields.push({
key: "Modulus",
value: PublicKey._formatByteStr(pk.n.toString(16), 16, 18)
});
pkFields.push({
key: "Exponent",
value: pk.e + " (0x" + pk.e.toString(16) + ")"
});
} else {
pkFields.push({
key: "Error",
value: "Unknown Public Key type"
});
}
// Format Public Key fields
for (let i = 0; i < pkFields.length; i++) {
pkStr += " " + pkFields[i].key + ":" +
(pkFields[i].value + "\n").padStart(
18 - (pkFields[i].key.length + 3) + pkFields[i].value.length + 1,
" "
);
}
// Signature fields
let breakoutSig = false;
try {
breakoutSig = r.ASN1HEX.dump(sig).indexOf("SEQUENCE") === 0;
} catch (err) {
// Error processing signature, output without further breakout
}
if (breakoutSig) { // DSA or ECDSA
sigStr = " r: " + PublicKey._formatByteStr(r.ASN1HEX.getV(sig, 4), 16, 18) + "\n" +
" s: " + PublicKey._formatByteStr(r.ASN1HEX.getV(sig, 48), 16, 18);
} else { // RSA or unknown
sigStr = " Signature: " + PublicKey._formatByteStr(sig, 16, 18);
}
// Extensions
try {
extensions = cert.getInfo().split("X509v3 Extensions:\n")[1].split("signature")[0];
} catch (err) {}
let issuerStr = PublicKey._formatDnStr(issuer, 2),
nbDate = PublicKey._formatDate(cert.getNotBefore()),
naDate = PublicKey._formatDate(cert.getNotAfter()),
subjectStr = PublicKey._formatDnStr(subject, 2);
return `Version: ${cert.version} (0x${Utils.hex(cert.version - 1)})
Serial number: ${new r.BigInteger(sn, 16).toString()} (0x${sn})
Algorithm ID: ${cert.getSignatureAlgorithmField()}
Validity
Not Before: ${nbDate} (dd-mm-yy hh:mm:ss) (${cert.getNotBefore()})
Not After: ${naDate} (dd-mm-yy hh:mm:ss) (${cert.getNotAfter()})
Issuer
${issuerStr}
Subject
${subjectStr}
Public Key
${pkStr.slice(0, -1)}
Certificate Signature
Algorithm: ${cert.getSignatureAlgorithmName()}
${sigStr}
Extensions
${extensions}`;
},
/**
* PEM to Hex operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runPemToHex: function(input, args) {
if (input.indexOf("-----BEGIN") < 0) {
// Add header so that the KEYUTIL function works
input = "-----BEGIN CERTIFICATE-----" + input;
}
if (input.indexOf("-----END") < 0) {
// Add footer so that the KEYUTIL function works
input = input + "-----END CERTIFICATE-----";
}
let cert = new r.X509();
cert.readCertPEM(input);
return cert.hex;
},
/**
* @constant
* @default
*/
PEM_HEADER_STRING: "CERTIFICATE",
/**
* Hex to PEM operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runHexToPem: function(input, args) {
return r.KJUR.asn1.ASN1Util.getPEMStringFromHex(input.replace(/\s/g, ""), args[0]);
},
/**
* Hex to Object Identifier operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runHexToObjectIdentifier: function(input, args) {
return r.KJUR.asn1.ASN1Util.oidHexToInt(input.replace(/\s/g, ""));
},
/**
* Object Identifier to Hex operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runObjectIdentifierToHex: function(input, args) {
return r.KJUR.asn1.ASN1Util.oidIntToHex(input);
},
/**
* @constant
* @default
*/
ASN1_TRUNCATE_LENGTH: 32,
/**
* Parse ASN.1 hex string operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runParseAsn1HexString: function(input, args) {
let truncateLen = args[1],
index = args[0];
return r.ASN1HEX.dump(input.replace(/\s/g, ""), {
"ommitLongOctet": truncateLen
}, index);
},
/**
* Formats Distinguished Name (DN) strings.
*
* @private
* @param {string} dnStr
* @param {number} indent
* @returns {string}
*/
_formatDnStr: function(dnStr, indent) {
let output = "",
fields = dnStr.substr(1).replace(/([^\\])\//g, "$1$1/").split(/[^\\]\//),
maxKeyLen = 0,
key,
value,
i,
str;
for (i = 0; i < fields.length; i++) {
if (!fields[i].length) continue;
key = fields[i].split("=")[0];
maxKeyLen = key.length > maxKeyLen ? key.length : maxKeyLen;
}
for (i = 0; i < fields.length; i++) {
if (!fields[i].length) continue;
key = fields[i].split("=")[0];
value = fields[i].split("=")[1];
str = key.padEnd(maxKeyLen, " ") + " = " + value + "\n";
output += str.padStart(indent + str.length, " ");
}
return output.slice(0, -1);
},
/**
* Formats byte strings by adding line breaks and delimiters.
*
* @private
* @param {string} byteStr
* @param {number} length - Line width
* @param {number} indent
* @returns {string}
*/
_formatByteStr: function(byteStr, length, indent) {
byteStr = toHex(fromHex(byteStr), ":");
length = length * 3;
let output = "";
for (let i = 0; i < byteStr.length; i += length) {
const str = byteStr.slice(i, i + length) + "\n";
if (i === 0) {
output += str;
} else {
output += str.padStart(indent + str.length, " ");
}
}
return output.slice(0, output.length-1);
},
/**
* Formats dates.
*
* @private
* @param {string} dateStr
* @returns {string}
*/
_formatDate: function(dateStr) {
return dateStr[4] + dateStr[5] + "/" +
dateStr[2] + dateStr[3] + "/" +
dateStr[0] + dateStr[1] + " " +
dateStr[6] + dateStr[7] + ":" +
dateStr[8] + dateStr[9] + ":" +
dateStr[10] + dateStr[11];
},
};
export default PublicKey;

View File

@ -1,58 +0,0 @@
import punycode from "punycode";
/**
* Punycode operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Punycode = {
/**
* @constant
* @default
*/
IDN: false,
/**
* To Punycode operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runToAscii: function(input, args) {
const idn = args[0];
if (idn) {
return punycode.toASCII(input);
} else {
return punycode.encode(input);
}
},
/**
* From Punycode operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runToUnicode: function(input, args) {
const idn = args[0];
if (idn) {
return punycode.toUnicode(input);
} else {
return punycode.decode(input);
}
},
};
export default Punycode;

View File

@ -1,272 +0,0 @@
/** @license
========================================================================
mimelib: http://github.com/andris9/mimelib
Copyright (c) 2011-2012 Andris Reinman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* Quoted Printable operations.
* Some parts taken from mimelib (http://github.com/andris9/mimelib)
*
* @author Andris Reinman
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const QuotedPrintable = {
/**
* To Quoted Printable operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function (input, args) {
let mimeEncodedStr = QuotedPrintable.mimeEncode(input);
// fix line breaks
mimeEncodedStr = mimeEncodedStr.replace(/\r?\n|\r/g, function() {
return "\r\n";
}).replace(/[\t ]+$/gm, function(spaces) {
return spaces.replace(/ /g, "=20").replace(/\t/g, "=09");
});
return QuotedPrintable._addSoftLinebreaks(mimeEncodedStr, "qp");
},
/**
* From Quoted Printable operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFrom: function (input, args) {
const str = input.replace(/=(?:\r?\n|$)/g, "");
return QuotedPrintable.mimeDecode(str);
},
/**
* Decodes mime-encoded data.
*
* @param {string} str
* @returns {byteArray}
*/
mimeDecode: function(str) {
let encodedBytesCount = (str.match(/=[\da-fA-F]{2}/g) || []).length,
bufferLength = str.length - encodedBytesCount * 2,
chr, hex,
buffer = new Array(bufferLength),
bufferPos = 0;
for (let i = 0, len = str.length; i < len; i++) {
chr = str.charAt(i);
if (chr === "=" && (hex = str.substr(i + 1, 2)) && /[\da-fA-F]{2}/.test(hex)) {
buffer[bufferPos++] = parseInt(hex, 16);
i += 2;
continue;
}
buffer[bufferPos++] = chr.charCodeAt(0);
}
return buffer;
},
/**
* Encodes mime data.
*
* @param {byteArray} buffer
* @returns {string}
*/
mimeEncode: function(buffer) {
let ranges = [
[0x09],
[0x0A],
[0x0D],
[0x20],
[0x21],
[0x23, 0x3C],
[0x3E],
[0x40, 0x5E],
[0x60, 0x7E]
],
result = "";
for (let i = 0, len = buffer.length; i < len; i++) {
if (this._checkRanges(buffer[i], ranges)) {
result += String.fromCharCode(buffer[i]);
continue;
}
result += "=" + (buffer[i] < 0x10 ? "0" : "") + buffer[i].toString(16).toUpperCase();
}
return result;
},
/**
* Checks if a given number falls within a given set of ranges.
*
* @private
* @param {number} nr
* @param {byteArray[]} ranges
* @returns {bolean}
*/
_checkRanges: function(nr, ranges) {
for (let i = ranges.length - 1; i >= 0; i--) {
if (!ranges[i].length)
continue;
if (ranges[i].length === 1 && nr === ranges[i][0])
return true;
if (ranges[i].length === 2 && nr >= ranges[i][0] && nr <= ranges[i][1])
return true;
}
return false;
},
/**
* Adds soft line breaks to a string.
* Lines can't be longer that 76 + <CR><LF> = 78 bytes
* http://tools.ietf.org/html/rfc2045#section-6.7
*
* @private
* @param {string} str
* @param {string} encoding
* @returns {string}
*/
_addSoftLinebreaks: function(str, encoding) {
const lineLengthMax = 76;
encoding = (encoding || "base64").toString().toLowerCase().trim();
if (encoding === "qp") {
return this._addQPSoftLinebreaks(str, lineLengthMax);
} else {
return this._addBase64SoftLinebreaks(str, lineLengthMax);
}
},
/**
* Adds soft line breaks to a base64 string.
*
* @private
* @param {string} base64EncodedStr
* @param {number} lineLengthMax
* @returns {string}
*/
_addBase64SoftLinebreaks: function(base64EncodedStr, lineLengthMax) {
base64EncodedStr = (base64EncodedStr || "").toString().trim();
return base64EncodedStr.replace(new RegExp(".{" + lineLengthMax + "}", "g"), "$&\r\n").trim();
},
/**
* Adds soft line breaks to a quoted printable string.
*
* @private
* @param {string} mimeEncodedStr
* @param {number} lineLengthMax
* @returns {string}
*/
_addQPSoftLinebreaks: function(mimeEncodedStr, lineLengthMax) {
let pos = 0,
len = mimeEncodedStr.length,
match, code, line,
lineMargin = Math.floor(lineLengthMax / 3),
result = "";
// insert soft linebreaks where needed
while (pos < len) {
line = mimeEncodedStr.substr(pos, lineLengthMax);
if ((match = line.match(/\r\n/))) {
line = line.substr(0, match.index + match[0].length);
result += line;
pos += line.length;
continue;
}
if (line.substr(-1) === "\n") {
// nothing to change here
result += line;
pos += line.length;
continue;
} else if ((match = line.substr(-lineMargin).match(/\n.*?$/))) {
// truncate to nearest line break
line = line.substr(0, line.length - (match[0].length - 1));
result += line;
pos += line.length;
continue;
} else if (line.length > lineLengthMax - lineMargin && (match = line.substr(-lineMargin).match(/[ \t.,!?][^ \t.,!?]*$/))) {
// truncate to nearest space
line = line.substr(0, line.length - (match[0].length - 1));
} else if (line.substr(-1) === "\r") {
line = line.substr(0, line.length - 1);
} else {
if (line.match(/=[\da-f]{0,2}$/i)) {
// push incomplete encoding sequences to the next line
if ((match = line.match(/=[\da-f]{0,1}$/i))) {
line = line.substr(0, line.length - match[0].length);
}
// ensure that utf-8 sequences are not split
while (line.length > 3 && line.length < len - pos && !line.match(/^(?:=[\da-f]{2}){1,4}$/i) && (match = line.match(/=[\da-f]{2}$/ig))) {
code = parseInt(match[0].substr(1, 2), 16);
if (code < 128) {
break;
}
line = line.substr(0, line.length - 3);
if (code >= 0xC0) {
break;
}
}
}
}
if (pos + line.length < len && line.substr(-1) !== "\n") {
if (line.length === 76 && line.match(/=[\da-f]{2}$/i)) {
line = line.substr(0, line.length - 3);
} else if (line.length === 76) {
line = line.substr(0, line.length - 1);
}
pos += line.length;
line += "=\r\n";
} else {
pos += line.length;
}
result += line;
}
return result;
},
};
export default QuotedPrintable;

View File

@ -1,278 +0,0 @@
import XRegExp from "xregexp";
import Utils from "../Utils.js";
/**
* Regex operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*
* @namespace
*/
const Regex = {
/**
* @constant
* @default
*/
REGEX_PRE_POPULATE: [
{
name: "User defined",
value: ""
},
{
name: "IPv4 address",
value: "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?"
},
{
name: "IPv6 address",
value: "((?=.*::)(?!.*::.+::)(::)?([\\dA-Fa-f]{1,4}:(:|\\b)|){5}|([\\dA-Fa-f]{1,4}:){6})((([\\dA-Fa-f]{1,4}((?!\\3)::|:\\b|(?![\\dA-Fa-f])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})"
},
{
name: "Email address",
value: "\\b(\\w[-.\\w]*)@([-\\w]+(?:\\.[-\\w]+)*)\\.([A-Za-z]{2,4})\\b"
},
{
name: "URL",
value: "([A-Za-z]+://)([-\\w]+(?:\\.\\w[-\\w]*)+)(:\\d+)?(/[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]*(?:[.!,?]+[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]+)*)?"
},
{
name: "Domain",
value: "\\b((?=[a-z0-9-]{1,63}\\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,63}\\b"
},
{
name: "Windows file path",
value: "([A-Za-z]):\\\\((?:[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)~]{0,61}\\\\?)*[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)]{0,61})(\\.[A-Za-z\\d]{1,6})?"
},
{
name: "UNIX file path",
value: "(?:/[A-Za-z\\d.][A-Za-z\\d\\-.]{0,61})+"
},
{
name: "MAC address",
value: "[A-Fa-f\\d]{2}(?:[:-][A-Fa-f\\d]{2}){5}"
},
{
name: "Date (yyyy-mm-dd)",
value: "((?:19|20)\\d\\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])"
},
{
name: "Date (dd/mm/yyyy)",
value: "(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.]((?:19|20)\\d\\d)"
},
{
name: "Date (mm/dd/yyyy)",
value: "(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.]((?:19|20)\\d\\d)"
},
{
name: "Strings",
value: "[A-Za-z\\d/\\-:.,_$%\\x27\"()<>= !\\[\\]{}@]{4,}"
},
],
/**
* @constant
* @default
*/
OUTPUT_FORMAT: ["Highlight matches", "List matches", "List capture groups", "List matches with capture groups"],
/**
* @constant
* @default
*/
DISPLAY_TOTAL: false,
/**
* Regular expression operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runRegex: function(input, args) {
const userRegex = args[1],
i = args[2],
m = args[3],
s = args[4],
u = args[5],
a = args[6],
displayTotal = args[7],
outputFormat = args[8];
let modifiers = "g";
if (i) modifiers += "i";
if (m) modifiers += "m";
if (s) modifiers += "s";
if (u) modifiers += "u";
if (a) modifiers += "A";
if (userRegex && userRegex !== "^" && userRegex !== "$") {
try {
const regex = new XRegExp(userRegex, modifiers);
switch (outputFormat) {
case "Highlight matches":
return Regex._regexHighlight(input, regex, displayTotal);
case "List matches":
return Utils.escapeHtml(Regex._regexList(input, regex, displayTotal, true, false));
case "List capture groups":
return Utils.escapeHtml(Regex._regexList(input, regex, displayTotal, false, true));
case "List matches with capture groups":
return Utils.escapeHtml(Regex._regexList(input, regex, displayTotal, true, true));
default:
return "Error: Invalid output format";
}
} catch (err) {
return "Invalid regex. Details: " + err.message;
}
} else {
return Utils.escapeHtml(input);
}
},
/**
* @constant
* @default
*/
SEARCH_TYPE: ["Regex", "Extended (\\n, \\t, \\x...)", "Simple string"],
/**
* @constant
* @default
*/
FIND_REPLACE_GLOBAL: true,
/**
* @constant
* @default
*/
FIND_REPLACE_CASE: false,
/**
* @constant
* @default
*/
FIND_REPLACE_MULTILINE: true,
/**
* Find / Replace operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runFindReplace: function(input, args) {
let find = args[0].string,
type = args[0].option,
replace = args[1],
g = args[2],
i = args[3],
m = args[4],
modifiers = "";
if (g) modifiers += "g";
if (i) modifiers += "i";
if (m) modifiers += "m";
if (type === "Regex") {
find = new RegExp(find, modifiers);
return input.replace(find, replace);
}
if (type.indexOf("Extended") === 0) {
find = Utils.parseEscapedChars(find);
}
find = new RegExp(Utils.escapeRegex(find), modifiers);
return input.replace(find, replace);
},
/**
* Adds HTML highlights to matches within a string.
*
* @private
* @param {string} input
* @param {RegExp} regex
* @param {boolean} displayTotal
* @returns {string}
*/
_regexHighlight: function(input, regex, displayTotal) {
let output = "",
m,
hl = 1,
i = 0,
total = 0;
while ((m = regex.exec(input))) {
// Moves pointer when an empty string is matched (prevents infinite loop)
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
// Add up to match
output += Utils.escapeHtml(input.slice(i, m.index));
// Add match with highlighting
output += "<span class='hl"+hl+"'>" + Utils.escapeHtml(m[0]) + "</span>";
// Switch highlight
hl = hl === 1 ? 2 : 1;
i = regex.lastIndex;
total++;
}
// Add all after final match
output += Utils.escapeHtml(input.slice(i, input.length));
if (displayTotal)
output = "Total found: " + total + "\n\n" + output;
return output;
},
/**
* Creates a string listing the matches within a string.
*
* @private
* @param {string} input
* @param {RegExp} regex
* @param {boolean} displayTotal
* @param {boolean} matches - Display full match
* @param {boolean} captureGroups - Display each of the capture groups separately
* @returns {string}
*/
_regexList: function(input, regex, displayTotal, matches, captureGroups) {
let output = "",
total = 0,
match;
while ((match = regex.exec(input))) {
// Moves pointer when an empty string is matched (prevents infinite loop)
if (match.index === regex.lastIndex) {
regex.lastIndex++;
}
total++;
if (matches) {
output += match[0] + "\n";
}
if (captureGroups) {
for (let i = 1; i < match.length; i++) {
if (matches) {
output += " Group " + i + ": ";
}
output += match[i] + "\n";
}
}
}
if (displayTotal)
output = "Total found: " + total + "\n\n" + output;
return output.slice(0, -1);
},
};
export default Regex;

View File

@ -1,257 +0,0 @@
import Utils from "../Utils.js";
/**
* Sequence utility operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const SeqUtils = {
/**
* @constant
* @default
*/
DELIMITER_OPTIONS: ["Line feed", "CRLF", "Space", "Comma", "Semi-colon", "Colon", "Nothing (separate chars)"],
/**
* @constant
* @default
*/
SORT_REVERSE: false,
/**
* @constant
* @default
*/
SORT_ORDER: ["Alphabetical (case sensitive)", "Alphabetical (case insensitive)", "IP address", "Numeric"],
/**
* Sort operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSort: function (input, args) {
let delim = Utils.charRep(args[0]),
sortReverse = args[1],
order = args[2],
sorted = input.split(delim);
if (order === "Alphabetical (case sensitive)") {
sorted = sorted.sort();
} else if (order === "Alphabetical (case insensitive)") {
sorted = sorted.sort(SeqUtils._caseInsensitiveSort);
} else if (order === "IP address") {
sorted = sorted.sort(SeqUtils._ipSort);
} else if (order === "Numeric") {
sorted = sorted.sort(SeqUtils._numericSort);
}
if (sortReverse) sorted.reverse();
return sorted.join(delim);
},
/**
* Unique operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runUnique: function (input, args) {
const delim = Utils.charRep(args[0]);
return input.split(delim).unique().join(delim);
},
/**
* @constant
* @default
*/
SEARCH_TYPE: ["Regex", "Extended (\\n, \\t, \\x...)", "Simple string"],
/**
* Count occurrences operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
*/
runCount: function(input, args) {
let search = args[0].string,
type = args[0].option;
if (type === "Regex" && search) {
try {
let regex = new RegExp(search, "gi"),
matches = input.match(regex);
return matches.length;
} catch (err) {
return 0;
}
} else if (search) {
if (type.indexOf("Extended") === 0) {
search = Utils.parseEscapedChars(search);
}
return input.count(search);
} else {
return 0;
}
},
/**
* @constant
* @default
*/
REVERSE_BY: ["Character", "Line"],
/**
* Reverse operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runReverse: function (input, args) {
let i;
if (args[0] === "Line") {
let lines = [],
line = [],
result = [];
for (i = 0; i < input.length; i++) {
if (input[i] === 0x0a) {
lines.push(line);
line = [];
} else {
line.push(input[i]);
}
}
lines.push(line);
lines.reverse();
for (i = 0; i < lines.length; i++) {
result = result.concat(lines[i]);
result.push(0x0a);
}
return result.slice(0, input.length);
} else {
return input.reverse();
}
},
/**
* Add line numbers operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runAddLineNumbers: function(input, args) {
let lines = input.split("\n"),
output = "",
width = lines.length.toString().length;
for (let n = 0; n < lines.length; n++) {
output += (n+1).toString().padStart(width, " ") + " " + lines[n] + "\n";
}
return output.slice(0, output.length-1);
},
/**
* Remove line numbers operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runRemoveLineNumbers: function(input, args) {
return input.replace(/^[ \t]{0,5}\d+[\s:|\-,.)\]]/gm, "");
},
/**
* Expand alphabet range operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runExpandAlphRange: function(input, args) {
return Utils.expandAlphRange(input).join(args[0]);
},
/**
* Comparison operation for sorting of strings ignoring case.
*
* @private
* @param {string} a
* @param {string} b
* @returns {number}
*/
_caseInsensitiveSort: function(a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
},
/**
* Comparison operation for sorting of IPv4 addresses.
*
* @private
* @param {string} a
* @param {string} b
* @returns {number}
*/
_ipSort: function(a, b) {
let a_ = a.split("."),
b_ = b.split(".");
a_ = a_[0] * 0x1000000 + a_[1] * 0x10000 + a_[2] * 0x100 + a_[3] * 1;
b_ = b_[0] * 0x1000000 + b_[1] * 0x10000 + b_[2] * 0x100 + b_[3] * 1;
if (isNaN(a_) && !isNaN(b_)) return 1;
if (!isNaN(a_) && isNaN(b_)) return -1;
if (isNaN(a_) && isNaN(b_)) return a.localeCompare(b);
return a_ - b_;
},
/**
* Comparison operation for sorting of numeric values.
*
* @author Chris van Marle
* @private
* @param {string} a
* @param {string} b
* @returns {number}
*/
_numericSort: function _numericSort(a, b) {
let a_ = a.split(/([^\d]+)/),
b_ = b.split(/([^\d]+)/);
for (let i = 0; i < a_.length && i < b.length; ++i) {
if (isNaN(a_[i]) && !isNaN(b_[i])) return 1; // Numbers after non-numbers
if (!isNaN(a_[i]) && isNaN(b_[i])) return -1;
if (isNaN(a_[i]) && isNaN(b_[i])) {
let ret = a_[i].localeCompare(b_[i]); // Compare strings
if (ret !== 0) return ret;
}
if (!isNaN(a_[i]) && !isNaN(a_[i])) { // Compare numbers
if (a_[i] - b_[i] !== 0) return a_[i] - b_[i];
}
}
return a.localeCompare(b);
},
};
export default SeqUtils;

View File

@ -1,96 +0,0 @@
import disassemble from "../vendor/DisassembleX86-64.js";
/**
* Shellcode operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const Shellcode = {
/**
* @constant
* @default
*/
MODE: ["64", "32", "16"],
/**
* @constant
* @default
*/
COMPATIBILITY: [
"Full x86 architecture",
"Knights Corner",
"Larrabee",
"Cyrix",
"Geode",
"Centaur",
"X86/486"
],
/**
* Disassemble x86 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runDisassemble: function(input, args) {
const mode = args[0],
compatibility = args[1],
codeSegment = args[2],
offset = args[3],
showInstructionHex = args[4],
showInstructionPos = args[5];
switch (mode) {
case "64":
disassemble.setBitMode(2);
break;
case "32":
disassemble.setBitMode(1);
break;
case "16":
disassemble.setBitMode(0);
break;
default:
throw "Invalid mode value";
}
switch (compatibility) {
case "Full x86 architecture":
disassemble.CompatibilityMode(0);
break;
case "Knights Corner":
disassemble.CompatibilityMode(1);
break;
case "Larrabee":
disassemble.CompatibilityMode(2);
break;
case "Cyrix":
disassemble.CompatibilityMode(3);
break;
case "Geode":
disassemble.CompatibilityMode(4);
break;
case "Centaur":
disassemble.CompatibilityMode(5);
break;
case "X86/486":
disassemble.CompatibilityMode(6);
break;
}
disassemble.SetBasePosition(codeSegment + ":" + offset);
disassemble.setShowInstructionHex(showInstructionHex);
disassemble.setShowInstructionPos(showInstructionPos);
disassemble.LoadBinCode(input.replace(/\s/g, ""));
return disassemble.LDisassemble();
},
};
export default Shellcode;

View File

@ -1,413 +0,0 @@
import Utils from "../Utils.js";
import {fromHex} from "../lib/Hex";
import jsesc from "jsesc";
/**
* String utility operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const StrUtils = {
/**
* @constant
* @default
*/
CASE_SCOPE: ["All", "Word", "Sentence", "Paragraph"],
/**
* To Upper case operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runUpper: function (input, args) {
const scope = args[0];
switch (scope) {
case "Word":
return input.replace(/(\b\w)/gi, function(m) {
return m.toUpperCase();
});
case "Sentence":
return input.replace(/(?:\.|^)\s*(\b\w)/gi, function(m) {
return m.toUpperCase();
});
case "Paragraph":
return input.replace(/(?:\n|^)\s*(\b\w)/gi, function(m) {
return m.toUpperCase();
});
case "All":
/* falls through */
default:
return input.toUpperCase();
}
},
/**
* To Upper case operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runLower: function (input, args) {
return input.toLowerCase();
},
/**
* @constant
* @default
*/
SPLIT_DELIM_OPTIONS: [
{name: "Comma", value: ","},
{name: "Space", value: " "},
{name: "Line feed", value: "\\n"},
{name: "CRLF", value: "\\r\\n"},
{name: "Semi-colon", value: ";"},
{name: "Colon", value: ":"},
{name: "Nothing (separate chars)", value: ""}
],
/**
* @constant
* @default
*/
JOIN_DELIM_OPTIONS: [
{name: "Line feed", value: "\\n"},
{name: "CRLF", value: "\\r\\n"},
{name: "Space", value: " "},
{name: "Comma", value: ","},
{name: "Semi-colon", value: ";"},
{name: "Colon", value: ":"},
{name: "Nothing (join chars)", value: ""}
],
/**
* Split operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSplit: function(input, args) {
let splitDelim = args[0],
joinDelim = args[1],
sections = input.split(splitDelim);
return sections.join(joinDelim);
},
/**
* @constant
* @default
*/
DELIMITER_OPTIONS: ["Line feed", "CRLF", "Space", "Comma", "Semi-colon", "Colon", "Nothing (separate chars)"],
/**
* Filter operation.
*
* @author Mikescher (https://github.com/Mikescher | https://mikescher.com)
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runFilter: function(input, args) {
let delim = Utils.charRep(args[0]),
regex,
reverse = args[2];
try {
regex = new RegExp(args[1]);
} catch (err) {
return "Invalid regex. Details: " + err.message;
}
const regexFilter = function(value) {
return reverse ^ regex.test(value);
};
return input.split(delim).filter(regexFilter).join(delim);
},
/**
* @constant
* @default
*/
OFF_CHK_SAMPLE_DELIMITER: "\\n\\n",
/**
* Offset checker operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runOffsetChecker: function(input, args) {
let sampleDelim = args[0],
samples = input.split(sampleDelim),
outputs = new Array(samples.length),
i = 0,
s = 0,
match = false,
inMatch = false,
chr;
if (!samples || samples.length < 2) {
return "Not enough samples, perhaps you need to modify the sample delimiter or add more data?";
}
// Initialise output strings
outputs.fill("", 0, samples.length);
// Loop through each character in the first sample
for (i = 0; i < samples[0].length; i++) {
chr = samples[0][i];
match = false;
// Loop through each sample to see if the chars are the same
for (s = 1; s < samples.length; s++) {
if (samples[s][i] !== chr) {
match = false;
break;
}
match = true;
}
// Write output for each sample
for (s = 0; s < samples.length; s++) {
if (samples[s].length <= i) {
if (inMatch) outputs[s] += "</span>";
if (s === samples.length - 1) inMatch = false;
continue;
}
if (match && !inMatch) {
outputs[s] += "<span class='hl5'>" + Utils.escapeHtml(samples[s][i]);
if (samples[s].length === i + 1) outputs[s] += "</span>";
if (s === samples.length - 1) inMatch = true;
} else if (!match && inMatch) {
outputs[s] += "</span>" + Utils.escapeHtml(samples[s][i]);
if (s === samples.length - 1) inMatch = false;
} else {
outputs[s] += Utils.escapeHtml(samples[s][i]);
if (inMatch && samples[s].length === i + 1) {
outputs[s] += "</span>";
if (samples[s].length - 1 !== i) inMatch = false;
}
}
if (samples[0].length - 1 === i) {
if (inMatch) outputs[s] += "</span>";
outputs[s] += Utils.escapeHtml(samples[s].substring(i + 1));
}
}
}
return outputs.join(sampleDelim);
},
/**
* @constant
* @default
*/
QUOTE_TYPES: ["Single", "Double", "Backtick"],
/**
* @constant
* @default
*/
ESCAPE_LEVEL: ["Special chars", "Everything", "Minimal"],
/**
* Escape string operation.
*
* @author Vel0x [dalemy@microsoft.com]
* @author n1474335 [n1474335@gmail.com]
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @example
* StrUtils.runEscape("Don't do that", [])
* > "Don\'t do that"
* StrUtils.runEscape(`Hello
* World`, [])
* > "Hello\nWorld"
*/
runEscape: function(input, args) {
const level = args[0],
quotes = args[1],
jsonCompat = args[2],
es6Compat = args[3],
lowercaseHex = !args[4];
return jsesc(input, {
quotes: quotes.toLowerCase(),
es6: es6Compat,
escapeEverything: level === "Everything",
minimal: level === "Minimal",
json: jsonCompat,
lowercaseHex: lowercaseHex,
});
},
/**
* Unescape string operation.
*
* @author Vel0x [dalemy@microsoft.com]
* @author n1474335 [n1474335@gmail.com]
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @example
* StrUtils.runUnescape("Don\'t do that", [])
* > "Don't do that"
* StrUtils.runUnescape("Hello\nWorld", [])
* > `Hello
* World`
*/
runUnescape: function(input, args) {
return Utils.parseEscapedChars(input);
},
/**
* Head lines operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runHead: function(input, args) {
let delimiter = args[0],
number = args[1];
delimiter = Utils.charRep(delimiter);
const splitInput = input.split(delimiter);
return splitInput
.filter((line, lineIndex) => {
lineIndex += 1;
if (number < 0) {
return lineIndex <= splitInput.length + number;
} else {
return lineIndex <= number;
}
})
.join(delimiter);
},
/**
* Tail lines operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runTail: function(input, args) {
let delimiter = args[0],
number = args[1];
delimiter = Utils.charRep(delimiter);
const splitInput = input.split(delimiter);
return splitInput
.filter((line, lineIndex) => {
lineIndex += 1;
if (number < 0) {
return lineIndex > -number;
} else {
return lineIndex > splitInput.length - number;
}
})
.join(delimiter);
},
/**
* @constant
* @default
*/
HAMMING_DELIM: "\\n\\n",
/**
* @constant
* @default
*/
HAMMING_INPUT_TYPE: ["Raw string", "Hex"],
/**
* @constant
* @default
*/
HAMMING_UNIT: ["Byte", "Bit"],
/**
* Hamming Distance operation.
*
* @author GCHQ Contributor [2]
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runHamming: function(input, args) {
const delim = args[0],
byByte = args[1] === "Byte",
inputType = args[2],
samples = input.split(delim);
if (samples.length !== 2) {
return "Error: You can only calculae the edit distance between 2 strings. Please ensure exactly two inputs are provided, separated by the specified delimiter.";
}
if (samples[0].length !== samples[1].length) {
return "Error: Both inputs must be of the same length.";
}
if (inputType === "Hex") {
samples[0] = fromHex(samples[0]);
samples[1] = fromHex(samples[1]);
} else {
samples[0] = Utils.strToByteArray(samples[0]);
samples[1] = Utils.strToByteArray(samples[1]);
}
let dist = 0;
for (let i = 0; i < samples[0].length; i++) {
const lhs = samples[0][i],
rhs = samples[1][i];
if (byByte && lhs !== rhs) {
dist++;
} else if (!byByte) {
let xord = lhs ^ rhs;
while (xord) {
dist++;
xord &= xord - 1;
}
}
}
return dist.toString();
},
};
export default StrUtils;

View File

@ -1,250 +0,0 @@
/**
* Tidy operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Tidy = {
/**
* @constant
* @default
*/
REMOVE_SPACES: true,
/**
* @constant
* @default
*/
REMOVE_CARIAGE_RETURNS: true,
/**
* @constant
* @default
*/
REMOVE_LINE_FEEDS: true,
/**
* @constant
* @default
*/
REMOVE_TABS: true,
/**
* @constant
* @default
*/
REMOVE_FORM_FEEDS: true,
/**
* @constant
* @default
*/
REMOVE_FULL_STOPS: false,
/**
* Remove whitespace operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runRemoveWhitespace: function (input, args) {
let removeSpaces = args[0],
removeCariageReturns = args[1],
removeLineFeeds = args[2],
removeTabs = args[3],
removeFormFeeds = args[4],
removeFullStops = args[5],
data = input;
if (removeSpaces) data = data.replace(/ /g, "");
if (removeCariageReturns) data = data.replace(/\r/g, "");
if (removeLineFeeds) data = data.replace(/\n/g, "");
if (removeTabs) data = data.replace(/\t/g, "");
if (removeFormFeeds) data = data.replace(/\f/g, "");
if (removeFullStops) data = data.replace(/\./g, "");
return data;
},
/**
* Remove null bytes operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runRemoveNulls: function (input, args) {
const output = [];
for (let i = 0; i < input.length; i++) {
if (input[i] !== 0) output.push(input[i]);
}
return output;
},
/**
* @constant
* @default
*/
APPLY_TO_EACH_LINE: false,
/**
* @constant
* @default
*/
DROP_START: 0,
/**
* @constant
* @default
*/
DROP_LENGTH: 5,
/**
* Drop bytes operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
runDropBytes: function(input, args) {
const start = args[0],
length = args[1],
applyToEachLine = args[2];
if (start < 0 || length < 0)
throw "Error: Invalid value";
if (!applyToEachLine) {
const left = input.slice(0, start),
right = input.slice(start + length, input.byteLength);
let result = new Uint8Array(left.byteLength + right.byteLength);
result.set(new Uint8Array(left), 0);
result.set(new Uint8Array(right), left.byteLength);
return result.buffer;
}
// Split input into lines
const data = new Uint8Array(input);
let lines = [],
line = [],
i;
for (i = 0; i < data.length; i++) {
if (data[i] === 0x0a) {
lines.push(line);
line = [];
} else {
line.push(data[i]);
}
}
lines.push(line);
let output = [];
for (i = 0; i < lines.length; i++) {
output = output.concat(lines[i].slice(0, start).concat(lines[i].slice(start+length, lines[i].length)));
output.push(0x0a);
}
return new Uint8Array(output.slice(0, output.length-1)).buffer;
},
/**
* @constant
* @default
*/
TAKE_START: 0,
/**
* @constant
* @default
*/
TAKE_LENGTH: 5,
/**
* Take bytes operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
runTakeBytes: function(input, args) {
const start = args[0],
length = args[1],
applyToEachLine = args[2];
if (start < 0 || length < 0)
throw "Error: Invalid value";
if (!applyToEachLine)
return input.slice(start, start+length);
// Split input into lines
const data = new Uint8Array(input);
let lines = [],
line = [],
i;
for (i = 0; i < data.length; i++) {
if (data[i] === 0x0a) {
lines.push(line);
line = [];
} else {
line.push(data[i]);
}
}
lines.push(line);
let output = [];
for (i = 0; i < lines.length; i++) {
output = output.concat(lines[i].slice(start, start+length));
output.push(0x0a);
}
return new Uint8Array(output.slice(0, output.length-1)).buffer;
},
/**
* @constant
* @default
*/
PAD_POSITION: ["Start", "End"],
/**
* @constant
* @default
*/
PAD_LENGTH: 5,
/**
* @constant
* @default
*/
PAD_CHAR: " ",
/**
* Pad lines operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runPad: function(input, args) {
let position = args[0],
len = args[1],
chr = args[2],
lines = input.split("\n"),
output = "",
i = 0;
if (position === "Start") {
for (i = 0; i < lines.length; i++) {
output += lines[i].padStart(lines[i].length+len, chr) + "\n";
}
} else if (position === "End") {
for (i = 0; i < lines.length; i++) {
output += lines[i].padEnd(lines[i].length+len, chr) + "\n";
}
}
return output.slice(0, output.length-1);
},
};
export default Tidy;

View File

@ -1,164 +0,0 @@
/**
* ToTable operations.
*
* @author Mark Jones [github.com/justanothermark]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*
* @namespace
*/
import Utils from "../Utils.js";
const ToTable = {
/**
* @constant
* @default
*/
FORMATS: [
"ASCII",
"HTML"
],
/**
* To Table operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runToTable: function (input, args) {
const [cellDelims, rowDelims, firstRowHeader, format] = args;
// Process the input into a nested array of elements.
const tableData = Utils.parseCSV(input, cellDelims.split(""), rowDelims.split(""));
if (!tableData.length) return "";
// Render the data in the requested format.
switch (format) {
case "ASCII":
return asciiOutput(tableData);
case "HTML":
default:
return htmlOutput(tableData);
}
/**
* Outputs an array of data as an ASCII table.
*
* @param {string[][]} tableData
* @returns {string}
*/
function asciiOutput(tableData) {
const horizontalBorder = "-";
const verticalBorder = "|";
const crossBorder = "+";
let output = "";
let longestCells = [];
// Find longestCells value per column to pad cells equally.
tableData.forEach(function(row, index) {
row.forEach(function(cell, cellIndex) {
if (longestCells[cellIndex] === undefined || cell.length > longestCells[cellIndex]) {
longestCells[cellIndex] = cell.length;
}
});
});
// Add the top border of the table to the output.
output += outputHorizontalBorder(longestCells);
// If the first row is a header, remove the row from the data and
// add it to the output with another horizontal border.
if (firstRowHeader) {
let row = tableData.shift();
output += outputRow(row, longestCells);
output += outputHorizontalBorder(longestCells);
}
// Add the rest of the table rows.
tableData.forEach(function(row, index) {
output += outputRow(row, longestCells);
});
// Close the table with a final horizontal border.
output += outputHorizontalBorder(longestCells);
return output;
/**
* Outputs a row of correctly padded cells.
*/
function outputRow(row, longestCells) {
let rowOutput = verticalBorder;
row.forEach(function(cell, index) {
rowOutput += " " + cell + " ".repeat(longestCells[index] - cell.length) + " " + verticalBorder;
});
rowOutput += "\n";
return rowOutput;
}
/**
* Outputs a horizontal border with a different character where
* the horizontal border meets a vertical border.
*/
function outputHorizontalBorder(longestCells) {
let rowOutput = crossBorder;
longestCells.forEach(function(cellLength) {
rowOutput += horizontalBorder.repeat(cellLength + 2) + crossBorder;
});
rowOutput += "\n";
return rowOutput;
}
}
/**
* Outputs a table of data as a HTML table.
*
* @param {string[][]} tableData
* @returns {string}
*/
function htmlOutput(tableData) {
// Start the HTML output with suitable classes for styling.
let output = "<table class='table table-hover table-condensed table-bordered table-nonfluid'>";
// If the first row is a header then put it in <thead> with <th> cells.
if (firstRowHeader) {
let row = tableData.shift();
output += "<thead>";
output += outputRow(row, "th");
output += "</thead>";
}
// Output the rest of the rows in the <tbody>.
output += "<tbody>";
tableData.forEach(function(row, index) {
output += outputRow(row, "td");
});
// Close the body and table elements.
output += "</tbody></table>";
return output;
/**
* Outputs a table row.
*
* @param {string[]} row
* @param {string} cellType
*/
function outputRow(row, cellType) {
let output = "<tr>";
row.forEach(function(cell) {
output += "<" + cellType + ">" + cell + "</" + cellType + ">";
});
output += "</tr>";
return output;
}
}
}
};
export default ToTable;

View File

@ -1,36 +0,0 @@
import crypto from "crypto";
/**
* UUID operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const UUID = {
/**
* Generate UUID operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runGenerateV4: function(input, args) {
const buf = new Uint32Array(4).map(() => {
return crypto.randomBytes(4).readUInt32BE(0, true);
});
let i = 0;
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
let r = (buf[i >> 3] >> ((i % 8) * 4)) & 0xf,
v = c === "x" ? r : (r & 0x3 | 0x8);
i++;
return v.toString(16);
});
}
};
export default UUID;

View File

@ -1,101 +0,0 @@
import Utils from "../Utils.js";
/**
* Unicode operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Unicode = {
/**
* @constant
* @default
*/
PREFIXES: ["\\u", "%u", "U+"],
/**
* Unescape Unicode Characters operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runUnescape: function(input, args) {
let prefix = Unicode._prefixToRegex[args[0]],
regex = new RegExp(prefix+"([a-f\\d]{4})", "ig"),
output = "",
m,
i = 0;
while ((m = regex.exec(input))) {
// Add up to match
output += input.slice(i, m.index);
i = m.index;
// Add match
output += Utils.chr(parseInt(m[1], 16));
i = regex.lastIndex;
}
// Add all after final match
output += input.slice(i, input.length);
return output;
},
/**
* Escape Unicode Characters operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runEscape: function(input, args) {
const regexWhitelist = /[ -~]/i,
prefix = args[0],
encodeAll = args[1],
padding = args[2],
uppercaseHex = args[3];
let output = "",
character = "";
for (let i = 0; i < input.length; i++) {
character = input[i];
if (!encodeAll && regexWhitelist.test(character)) {
// Its a printable ASCII character so dont escape it.
output += character;
continue;
}
let cp = character.codePointAt(0).toString(16);
if (uppercaseHex) cp = cp.toUpperCase();
output += prefix + cp.padStart(padding, "0");
}
return output;
},
/**
* Lookup table to add prefixes to unicode delimiters so that they can be used in a regex.
*
* @private
* @constant
*/
_prefixToRegex: {
"\\u": "\\\\u",
"%u": "%u",
"U+": "U\\+"
},
};
export default Unicode;

View File

@ -1,26 +0,0 @@
/**
* XKCD operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*
* @namespace
*/
const XKCD = {
/**
* XKCD Random Number operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
*/
runRandomNumber: function(input, args) {
return 4; // chosen by fair dice roll.
// guaranteed to be random.
},
};
export default XKCD;

View File

@ -18,10 +18,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import Utils from "../Utils.js";
import Utils from "../Utils.mjs";
// Param jpeg should be a binaryArray
function removeEXIF(jpeg) {
export function removeEXIF(jpeg) {
// Convert binaryArray to char string
jpeg = Utils.byteArrayToChars(jpeg);
if (jpeg.slice(0, 2) != "\xff\xd8") {
@ -149,5 +149,3 @@ function unpack(mark, str) {
return unpacked;
}
export default removeEXIF;

View File

@ -0,0 +1,156 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import ChefWorker from "worker-loader?inline&fallback=false!../core/ChefWorker";
/**
* Waiter to handle conversations with a ChefWorker in the background.
*/
class BackgroundWorkerWaiter {
/**
* BackgroundWorkerWaiter constructor.
*
* @param {App} app - The main view object for CyberChef.
* @param {Manager} manager - The CyberChef event manager.
*/
constructor(app, manager) {
this.app = app;
this.manager = manager;
this.callbacks = {};
this.callbackID = 0;
this.completedCallback = -1;
this.timeout = null;
}
/**
* Sets up the ChefWorker and associated listeners.
*/
registerChefWorker() {
log.debug("Registering new background ChefWorker");
this.chefWorker = new ChefWorker();
this.chefWorker.addEventListener("message", this.handleChefMessage.bind(this));
let docURL = document.location.href.split(/[#?]/)[0];
const index = docURL.lastIndexOf("/");
if (index > 0) {
docURL = docURL.substring(0, index);
}
this.chefWorker.postMessage({"action": "docURL", "data": docURL});
}
/**
* Handler for messages sent back by the ChefWorker.
*
* @param {MessageEvent} e
*/
handleChefMessage(e) {
const r = e.data;
log.debug("Receiving '" + r.action + "' from ChefWorker in the background");
switch (r.action) {
case "bakeComplete":
case "bakeError":
if (typeof r.data.id !== "undefined") {
clearTimeout(this.timeout);
this.callbacks[r.data.id].bind(this)(r.data);
this.completedCallback = r.data.id;
}
break;
case "workerLoaded":
log.debug("Background ChefWorker loaded");
break;
case "optionUpdate":
// Ignore these messages
break;
default:
log.error("Unrecognised message from background ChefWorker", e);
break;
}
}
/**
* Cancels the current bake by terminating the ChefWorker and creating a new one.
*/
cancelBake() {
if (this.chefWorker)
this.chefWorker.terminate();
this.registerChefWorker();
}
/**
* Asks the ChefWorker to bake the input using the specified recipe.
*
* @param {string} input
* @param {Object[]} recipeConfig
* @param {Object} options
* @param {number} progress
* @param {boolean} step
* @param {Function} callback
*/
bake(input, recipeConfig, options, progress, step, callback) {
const id = this.callbackID++;
this.callbacks[id] = callback;
this.chefWorker.postMessage({
action: "bake",
data: {
input: input,
recipeConfig: recipeConfig,
options: options,
progress: progress,
step: step,
id: id
}
});
}
/**
* Asks the Magic operation what it can do with the input data.
*
* @param {string|ArrayBuffer} input
*/
magic(input) {
// If we're still working on the previous bake, cancel it before stating a new one.
if (this.completedCallback + 1 < this.callbackID) {
clearTimeout(this.timeout);
this.cancelBake();
}
this.bake(input, [
{
"op": "Magic",
"args": [3, false, false]
}
], {}, 0, false, this.magicComplete);
// Cancel this bake if it takes too long.
this.timeout = setTimeout(this.cancelBake.bind(this), 3000);
}
/**
* Handler for completed Magic bakes.
*
* @param {Object} response
*/
magicComplete(response) {
log.debug("--- Background Magic Bake complete ---");
if (!response || response.error) return;
this.manager.output.backgroundMagicResult(response.dish.value);
}
}
export default BackgroundWorkerWaiter;

View File

@ -15,6 +15,7 @@ import OptionsWaiter from "./OptionsWaiter";
import HighlighterWaiter from "./HighlighterWaiter";
import SeasonalWaiter from "./SeasonalWaiter";
import BindingsWaiter from "./BindingsWaiter";
import BackgroundWorkerWaiter from "./BackgroundWorkerWaiter";
/**
@ -68,6 +69,7 @@ class Manager {
this.highlighter = new HighlighterWaiter(this.app, this);
this.seasonal = new SeasonalWaiter(this.app, this);
this.bindings = new BindingsWaiter(this.app, this);
this.background = new BackgroundWorkerWaiter(this.app, this);
// Object to store dynamic handlers to fire on elements that may not exist yet
this.dynamicHandlers = {};
@ -85,6 +87,7 @@ class Manager {
this.controls.initComponents();
this.controls.autoBakeChange();
this.bindings.updateKeybList();
this.background.registerChefWorker();
this.seasonal.load();
}

View File

@ -117,6 +117,7 @@ class OutputWaiter {
this.manager.highlighter.removeHighlights();
this.setOutputInfo(length, lines, duration);
this.backgroundMagic();
}
@ -416,6 +417,42 @@ class OutputWaiter {
return this.dishBuffer;
}
/**
* Triggers the BackgroundWorker to attempt Magic on the current output.
*/
backgroundMagic() {
const sample = this.dishStr ? this.dishStr.slice(0, 1000) :
this.dishBuffer ? this.dishBuffer.slice(0, 1000) : "";
if (sample.length) {
this.manager.background.magic(sample);
}
}
/**
* Handles the results of a background Magic call.
*
* @param {Object[]} options
*/
backgroundMagicResult(options) {
if (!options.length ||
!options[0].recipe.length)
return;
//console.log(options);
const currentRecipeConfig = this.app.getRecipeConfig();
const newRecipeConfig = currentRecipeConfig.concat(options[0].recipe);
const recipeURL = "#recipe=" + Utils.encodeURIFragment(Utils.generatePrettyRecipe(newRecipeConfig));
const opSequence = options[0].recipe.map(o => o.op).join(", ");
log.log(`Running <a href="${recipeURL}">${opSequence}</a> will result in "${Utils.truncate(options[0].data, 20)}"`);
//this.app.setRecipeConfig(newRecipeConfig);
//this.app.autoBake();
}
}
export default OutputWaiter;

View File

@ -34,7 +34,7 @@ import "./tests/operations/CartesianProduct";
import "./tests/operations/CharEnc";
import "./tests/operations/Ciphers";
import "./tests/operations/Checksum";
// import "./tests/operations/Code";
import "./tests/operations/Code";
import "./tests/operations/Compress";
import "./tests/operations/Crypt";
import "./tests/operations/DateTime";
@ -45,16 +45,17 @@ import "./tests/operations/Register";
import "./tests/operations/Comment";
import "./tests/operations/Hash";
import "./tests/operations/Hexdump";
// import "./tests/operations/Image";
import "./tests/operations/Image";
import "./tests/operations/MorseCode";
import "./tests/operations/MS";
import "./tests/operations/PGP";
import "./tests/operations/PHP";
import "./tests/operations/NetBIOS";
import "./tests/operations/OTP";
import "./tests/operations/PowerSet";
// import "./tests/operations/Regex";
import "./tests/operations/Regex";
import "./tests/operations/Rotate";
// import "./tests/operations/StrUtils";
import "./tests/operations/StrUtils";
import "./tests/operations/SeqUtils";
import "./tests/operations/SetDifference";
import "./tests/operations/SetIntersection";

View File

@ -57,7 +57,7 @@ TestRegister.addTests([
{
name: "Extract EXIF: hello world text (error)",
input: "hello world",
expectedError: true,
expectedOutput: "Could not extract EXIF data from image: Error: Invalid JPEG section offset",
recipeConfig: [
{
op: "Extract EXIF",
@ -129,7 +129,7 @@ TestRegister.addTests([
{
name: "Remove EXIF: hello world text (error)",
input: "hello world",
expectedError: true,
expectedOutput: "Could not remove EXIF data from image: Given data is not jpeg.",
recipeConfig: [
{
op: "Remove EXIF",

View File

@ -0,0 +1,285 @@
/**
* PGP tests.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import TestRegister from "../../TestRegister";
const ASCII_TEXT = "A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools.";
const UTF8_TEXT = "Шанцы на высвятленне таго, што адбываецца на самай справе ў сусвеце настолькі выдаленыя, адзінае, што трэба зрабіць, гэта павесіць пачуццё яго і трымаць сябе занятымі.";
const ALL_BYTES = [
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
].join("");
// RSA-1024
const ALICE_PRIVATE = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: Keybase OpenPGP v2.0.77
Comment: https://keybase.io/crypto
xcEYBFsNaYsBBAC9rnmjTzLLBCey2gq9un+XXP089sP3AONhSivdJlJEEWjt999o
g8vM18TcEk1sxyItp/GLlE/T70NPFAvdVXKI0KDQZ9fm77JDKitl587npRaspOOX
L1yUVFGVr4YEPDLoAT4PJgwI3TsEBfLGeOZqBqd/stw/FKjrNZJLRYfjnQARAQAB
AAP9G3EGgAM74fXJQy1wUwqMMvsXrUjgs6IZQ0Cryo7PZVxExfNlCtsmZ42VGWbn
H071OY22eu8LWCn2nut+MUM6EnjDZ7e/u85eHd0r5fY/3Vl0lQCy53RDdEQ3w8vA
XcUSabxwqpubmtyC3jxIXmVH6rLLmSpGGX8IqHRTfNDwHTECAPsgeVy0qkT0kJq1
Aw8gthHO6c3m1NOcAPyTqSRLVspxRDB0LuYHnVxAN+dUHFYAfwPj+h+E6ROolqe4
IKtrls8CAMFcwisDUQXFQFmO2pkpgaQTkN9XjGqBhjYd1EGs+WcYZb7eD98Ue0TZ
GUF4UtzHUW5hIiCgkTrwpdpRqE3xudMCAJbhnzE+Mj7yKAHAV8LjZfpJA9hh1G8c
ATDpoWD1yAcLO1mMVkSExpMHoiuQ5ujzWyCHYnXDdRo6jkowP4JxIX+axs0AwrQE
EwEKAB4FAlsNaYsCGy8DCwkHAxUKCAIeAQIXgAMWAgECGQEACgkQhCRlSN+Y5IXj
5QP8CZns1zlWk7S37Dhvxe3K3EYVgefc+EDWsj3xvlo+QUKQMAmANFNnqYzt++mv
cVhvGzyn+wa244fJb3xGYAi+G4Ya5pWQbXSzAVhjteHyLcjS6VZ/ydxDGCZK37Gc
MYs/8x4kwdU+A8/bQhJ+nRVEJjkg0OcoH9rJv0kB+ilcC7LHwRgEWw1piwEEAOXO
Jib0QIvuqKAZiN3Yol3xC9Wz5aXQyg7qCnnYHHrPIMgYvGrTjjvDFCwM5uxCv38Q
d6rJnzrRXTC1EiAia/b7f76Z0r4W+j6KdVCGpQnVQE6b//WdY5ys1xLAkNr4xwNj
42nrOIMGB1qV0XezJ4VBOMpMHlwE2WR27HOQakXDABEBAAEAA/9Svw4BzMVJHaBe
NZOQviaIyPjH9ETmle2LvT4UbXqjxd057544oQCACFhFHEgyHj6x1A4i0wKgvS5f
EXP7WimhUEybo8YktbYX691QGPHNNQw5dc6IzLZmSm9p1zpuOs1VBHs6lpR5Y0WT
/2vDrbY2Loa+Dojuvuq3hY1Bu5fjcQIA/SdK6T8sEYwdZTfCKEWdvMQ8zhjioNNn
5enUNT/WQXw6qvkczD2U48PlIXpwfn4Rjh3sGEiumng334LTslXtvwIA6GOl6eFC
337clY0Yyog7cTsEZTdCQBIScZi7grMuL9KFWx4UbfHiDS976MRu1ciATCTSCdc5
xgLEUF51WrWw/QIApR/pGgDg/Ow32jS38VonCH6TpFFMk9KciKCMm7sRrG3J6kFK
UxuxWXPs+pWXjTn6ItfrX6M8dZZkC2BBR9UyrqB5wsCDBBgBCgAPBQJbDWmLBQkP
CZwAAhsuAKgJEIQkZUjfmOSFnSAEGQEKAAYFAlsNaYsACgkQPtlTQFIjCzrjvQQA
hyGjZ2zDMxyXA3oEoD3RfjPQtAYFPJ5i0/ir/FD2nX7//cyd8zJS24P6S9+ID8vB
0n+JwF6KrvjqpMneXXbPmi5OebqMogLahWmhCtjriDKrfJJiL0HmTKGl89p2Z59e
BoLbm4Jpk1rL7EmoibsmUZdBUutf0tw5IusXd/B5sNwhdgQArBzyHVIFyN+fegTE
9cR677M92NjYhqY8c+fF/AV+7XQv0Vsi9B//HeTMCml6jytxdSIZBl7uLrasIOd0
FJk+VP7UrOfDaz6oVq+tarStAelfqT9DRQXw+nEdes4bxDsPvi1OieTtexJRO181
zdsmOukv2RhgrJzFCcpzAkUYGqjHwRgEWw1piwEEALDKG2L6NNhTXZ3MJJLVtEPD
65c9KmT8DagJXCp0cl3bQbcs+zLmsfYwnIKNTOxnhxAER+5e6jmW4K7sbLY593rO
iqDXXX8OR28t88IGjlIrVd+2t6+ma5ecSgsyVqBDFFTpAzg+QFWdk2VEOlA5zNfC
dX94pLUMjPDAHSsZfrufABEBAAEAA/wIFwePzPFUIOR8zxWxXnQkUbfbMOJawqoB
UYRVMQT2xIzKTBWmq6XjJTBUTREDFG26zudXwiInxn67ongLErX/Zhohq8lBYjjx
QkQRtU+QSDsksdJJL0Lj/6SAkljkhXmO+jYmRVirQfGBVl33Dk9YWnd/VePO5epn
nYPxEGT+MQIA6n9nHmzbz5ohSi6Ovn4OW0704K3kLhOQ3WP3j+5+bWXv0Xd89w7v
ZMyv0IJOvhOw1670BcBxbI/CSFD4Nz+k+QIAwQAP/M8TdG1Twwx6yED0syNvPDfN
5hzZM8031zTBbguSRJskD18aBtwcUun93+dcilRY94gXbl4xSq3YitTDVwIAvX+P
06nJmFdgeoanpcYIBA4hwi+LPyfPcGo6tfnsxk7ul1mBK27TR+AjnJ+HNFCh54CM
4cLH1djyBmwEt30Wm6FjwsCDBBgBCgAPBQJbDWmLBQkDwmcAAhsuAKgJEIQkZUjf
mOSFnSAEGQEKAAYFAlsNaYsACgkQW/Z2teklb4u5/gP/fnZ7ZuV+l4c2EQY5Xnk8
S/lY3Xr9zoucjQwQWeRKwAQYoiovzxA4XV8gGyrdAsrIPUFLp7PmUBG4YJV/7sVo
zzRwVq+jS8Jo0xYbGJMv2DuAnXXrYCZWZRqRscr1Wlc+CUACmxYZjC1DVVrAXr0j
TqYTk+jjhemTTAtUelgMhcX82wP9EEU66hCYFUayjn4bBlR1yEvMpJd8JSTHR/dZ
H8t3Ri6R2AYRqBxro0JEXDhL9iDnuPQVxsbgq2YlhHrPJI8opKuxV7wrXrupzwFf
KixJMNwsAPk/nSc4qIZvXTi2fmyAZDJYUgsm6CwkxumaVvIdVNGRmxqGSRTuEInt
W03Cwfs=
=Mb52
-----END PGP PRIVATE KEY BLOCK-----`;
const ALICE_PUBLIC = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: Keybase OpenPGP v2.0.77
Comment: https://keybase.io/crypto
xo0EWw1piwEEAL2ueaNPMssEJ7LaCr26f5dc/Tz2w/cA42FKK90mUkQRaO3332iD
y8zXxNwSTWzHIi2n8YuUT9PvQ08UC91VcojQoNBn1+bvskMqK2XnzuelFqyk45cv
XJRUUZWvhgQ8MugBPg8mDAjdOwQF8sZ45moGp3+y3D8UqOs1kktFh+OdABEBAAHN
AMK0BBMBCgAeBQJbDWmLAhsvAwsJBwMVCggCHgECF4ADFgIBAhkBAAoJEIQkZUjf
mOSF4+UD/AmZ7Nc5VpO0t+w4b8XtytxGFYHn3PhA1rI98b5aPkFCkDAJgDRTZ6mM
7fvpr3FYbxs8p/sGtuOHyW98RmAIvhuGGuaVkG10swFYY7Xh8i3I0ulWf8ncQxgm
St+xnDGLP/MeJMHVPgPP20ISfp0VRCY5INDnKB/ayb9JAfopXAuyzo0EWw1piwEE
AOXOJib0QIvuqKAZiN3Yol3xC9Wz5aXQyg7qCnnYHHrPIMgYvGrTjjvDFCwM5uxC
v38Qd6rJnzrRXTC1EiAia/b7f76Z0r4W+j6KdVCGpQnVQE6b//WdY5ys1xLAkNr4
xwNj42nrOIMGB1qV0XezJ4VBOMpMHlwE2WR27HOQakXDABEBAAHCwIMEGAEKAA8F
AlsNaYsFCQ8JnAACGy4AqAkQhCRlSN+Y5IWdIAQZAQoABgUCWw1piwAKCRA+2VNA
UiMLOuO9BACHIaNnbMMzHJcDegSgPdF+M9C0BgU8nmLT+Kv8UPadfv/9zJ3zMlLb
g/pL34gPy8HSf4nAXoqu+Oqkyd5dds+aLk55uoyiAtqFaaEK2OuIMqt8kmIvQeZM
oaXz2nZnn14GgtubgmmTWsvsSaiJuyZRl0FS61/S3Dki6xd38Hmw3CF2BACsHPId
UgXI3596BMT1xHrvsz3Y2NiGpjxz58X8BX7tdC/RWyL0H/8d5MwKaXqPK3F1IhkG
Xu4utqwg53QUmT5U/tSs58NrPqhWr61qtK0B6V+pP0NFBfD6cR16zhvEOw++LU6J
5O17ElE7XzXN2yY66S/ZGGCsnMUJynMCRRgaqM6NBFsNaYsBBACwyhti+jTYU12d
zCSS1bRDw+uXPSpk/A2oCVwqdHJd20G3LPsy5rH2MJyCjUzsZ4cQBEfuXuo5luCu
7Gy2Ofd6zoqg111/DkdvLfPCBo5SK1XftrevpmuXnEoLMlagQxRU6QM4PkBVnZNl
RDpQOczXwnV/eKS1DIzwwB0rGX67nwARAQABwsCDBBgBCgAPBQJbDWmLBQkDwmcA
AhsuAKgJEIQkZUjfmOSFnSAEGQEKAAYFAlsNaYsACgkQW/Z2teklb4u5/gP/fnZ7
ZuV+l4c2EQY5Xnk8S/lY3Xr9zoucjQwQWeRKwAQYoiovzxA4XV8gGyrdAsrIPUFL
p7PmUBG4YJV/7sVozzRwVq+jS8Jo0xYbGJMv2DuAnXXrYCZWZRqRscr1Wlc+CUAC
mxYZjC1DVVrAXr0jTqYTk+jjhemTTAtUelgMhcX82wP9EEU66hCYFUayjn4bBlR1
yEvMpJd8JSTHR/dZH8t3Ri6R2AYRqBxro0JEXDhL9iDnuPQVxsbgq2YlhHrPJI8o
pKuxV7wrXrupzwFfKixJMNwsAPk/nSc4qIZvXTi2fmyAZDJYUgsm6CwkxumaVvId
VNGRmxqGSRTuEIntW03Cwfs=
=PuGL
-----END PGP PUBLIC KEY BLOCK-----`;
// ECC-384
const BOB_PRIVATE = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: Keybase OpenPGP v2.0.77
Comment: https://keybase.io/crypto
xcAABFsNafYBAYDHiv+tCi4267xI6iTmBrhOKdNbKLWIYMG1OoE1f9qpT+nAVKFR
zUAFXKqQjqMDESkAEQEAAQABewd7cLkIQHGKly8PE+P0h7fV7X5bJqwZiqDwC8DU
38vCUO/KtkZO3jEQYA1U9DsNDQDA73KCr3K1tSX1afeWzb8vVBY4ZzXocKb9AMDV
Vk17t1N4nClMfqpGIDELtYBMiiCDyJ0AwLsnQb9cE+g1MZETtNDYXXxilkO/4CP9
8j4HzQDCZAQTAQoAHgUCWw1p9gIbLwMLCQcDFQoIAh4BAheAAxYCAQIZAQAKCRCE
cIHWt/IPg+sqAYC6goCyOCYD/DytOW3I2cb12iDyFOSDsOx6lrmIgLyP0dDnbJHb
S9ar68yuHeDqP7jHiARbDWn2AQEAwSE4qpbLQzSIUfwmfWXmHneAuQIkEYawRxK/
H1JkGxcAEQEAAQAA/0pvbnK5OdBGMABBSehs3LrW/hWWIL0y/MfS7h/6gSJ5AID3
YOgHLqEgM1Bo2TzvIjwlAIDH3E+0ynQFdLH96FPp47eLAH9e/NZ74e2N8sTMBoYO
1sbcLp7CkwQYAQoADwUCWw1p9gUJDwmcAAIbLgBICRCEcIHWt/IPgz0gBBkBCgAG
BQJbDWn2AAoJELU8cYHhYcru2lwBAL4OUK2fkhzh2VU3meXgAMWjoP6ryRUCTmSQ
xuULvvCyfZMBfiHzV5QLgXSUVUA7Og6mlH5pw2gtgsZhijwwywkzF3tQ+s++hOZR
161wHxQKgwHIU8eIBFsNafYBAQCjOV/I3a0HkXVtLndCrWFcjmLzim9PX8EpYUV3
yG2/AQARAQABAAD8DBWPVduzl7/ZJcAu7CzR7F376NxG8J42+ioX12n9cNEAgNj7
qAcnQCtTDlb1waf4mdcAgMCTCuwur8AqIOSjoOzqwucAf1MfeKXhwNAEtoiD7S44
f8UvxsKTBBgBCgAPBQJbDWn2BQkDwmcAAhsuAEgJEIRwgda38g+DPSAEGQEKAAYF
AlsNafYACgkQNBtaoVz6VrvTVAD+LD063VrU7vlJ7xQwtMun4G3FW+RKgb7Rsww8
B1mt68F5dQGAm8ctxECzEMmyO8jSkjOLkG6u8zLQWFm9MBZqcdmt6EUDf1dA/3xu
/y59qEGb0j4w
=I/Gz
-----END PGP PRIVATE KEY BLOCK-----`;
const BOB_PUBLIC = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: Keybase OpenPGP v2.0.77
Comment: https://keybase.io/crypto
xj0EWw1p9gEBgMeK/60KLjbrvEjqJOYGuE4p01sotYhgwbU6gTV/2qlP6cBUoVHN
QAVcqpCOowMRKQARAQABzQDCZAQTAQoAHgUCWw1p9gIbLwMLCQcDFQoIAh4BAheA
AxYCAQIZAQAKCRCEcIHWt/IPg+sqAYC6goCyOCYD/DytOW3I2cb12iDyFOSDsOx6
lrmIgLyP0dDnbJHbS9ar68yuHeDqP7jOLQRbDWn2AQEAwSE4qpbLQzSIUfwmfWXm
HneAuQIkEYawRxK/H1JkGxcAEQEAAcKTBBgBCgAPBQJbDWn2BQkPCZwAAhsuAEgJ
EIRwgda38g+DPSAEGQEKAAYFAlsNafYACgkQtTxxgeFhyu7aXAEAvg5QrZ+SHOHZ
VTeZ5eAAxaOg/qvJFQJOZJDG5Qu+8LJ9kwF+IfNXlAuBdJRVQDs6DqaUfmnDaC2C
xmGKPDDLCTMXe1D6z76E5lHXrXAfFAqDAchTzi0EWw1p9gEBAKM5X8jdrQeRdW0u
d0KtYVyOYvOKb09fwSlhRXfIbb8BABEBAAHCkwQYAQoADwUCWw1p9gUJA8JnAAIb
LgBICRCEcIHWt/IPgz0gBBkBCgAGBQJbDWn2AAoJEDQbWqFc+la701QA/iw9Ot1a
1O75Se8UMLTLp+BtxVvkSoG+0bMMPAdZrevBeXUBgJvHLcRAsxDJsjvI0pIzi5Bu
rvMy0FhZvTAWanHZrehFA39XQP98bv8ufahBm9I+MA==
=K9ht
-----END PGP PUBLIC KEY BLOCK-----`;
TestRegister.addTests([
{
name: "PGP Encrypt/Decrypt: RSA, nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
"op": "PGP Encrypt",
"args": [ALICE_PUBLIC]
},
{
"op": "PGP Decrypt",
"args": [ALICE_PRIVATE, ""]
}
]
},
{
name: "PGP Encrypt/Decrypt: RSA, All bytes",
input: ALL_BYTES,
expectedOutput: ALL_BYTES,
recipeConfig: [
{
"op": "PGP Encrypt",
"args": [ALICE_PUBLIC]
},
{
"op": "PGP Decrypt",
"args": [ALICE_PRIVATE, ""]
}
]
},
{
name: "PGP Encrypt/Decrypt: ECC, nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
"op": "PGP Encrypt",
"args": [BOB_PUBLIC]
},
{
"op": "PGP Decrypt",
"args": [BOB_PRIVATE, ""]
}
]
},
{
name: "PGP Encrypt/Decrypt: ECC, All bytes",
input: ALL_BYTES,
expectedOutput: ALL_BYTES,
recipeConfig: [
{
"op": "PGP Encrypt",
"args": [BOB_PUBLIC]
},
{
"op": "PGP Decrypt",
"args": [BOB_PRIVATE, ""]
}
]
},
{
name: "PGP Decrypt and Verify: UTF8, Alice -> Bob",
input: `-----BEGIN PGP MESSAGE-----
Version: Keybase OpenPGP v2.0.77
Comment: https://keybase.io/crypto
wTwDhHCB1rfyD4MBAX9ld8xGcf2v+X+pwINN0R0TvkWxNesKOQIKPV01AH8JG0J+
+yFqLXqDHgYSLANNamfSwQoBOTWuh/5V6gpiXVm2oLHPv997AtoD/kVQrqylF5Xo
HUsqPGtSgBA5WPX8tMoHKuqWxEy9FviLnIv73OZN0Ph70uo2E+QIv0Qx27znK0Jy
KDSERvcldgShmVbDP3Pxtxkfr9xa2gar5f0OPovOmKGsTGciQJqPkclRwzIXg12L
hyd2ElYOMf6vg/yOc06sX4Ih1Tn6JkYqMVJydykMv3g4Z8OXTfwrMLxwO1n3ZB/T
OLdhBdsnREnyCqntBVjMKoRTQhfwq48n7b6caZ+aCPISdDIyDKBpxEzXaNBeEY2V
GCqORM9WhsQ4A6pAx2SP694qH5vgOwrYrgeOU17oK++mzd1GyU2CXoFi73/PANJD
TdC3hGr+S4XeuqZ368QG1cBWhNybsOu5sM2YbArb71ZMYuLDp+VolJbEkVf4c/dD
pVEOaX39NVKe6HcpOiw+CFO6GEkQqCXNprWK6ivBHzkAlF2pjjqlS6qhWxFPicSD
+1ZKM1fmZu99bhTmdqE3MJx//QMu7mvlHaM85OQkWhWPBxGw/60GVBX9YtvUtfMS
IOE1W/Zqmqzq+4frwnzWwYv9/U1RwIs/qlFVnzliREOzW+om8EncSSd7fQ==
=fEAT
-----END PGP MESSAGE-----
`,
expectedOutput: `Signed by PGP fingerprint: e94e06dd0b3744a0e970de9d84246548df98e485
Signed on Tue, 29 May 2018 15:44:52 GMT
----------------------------------
${UTF8_TEXT}`,
recipeConfig: [
{
"op": "PGP Decrypt and Verify",
"args": [ALICE_PUBLIC, BOB_PRIVATE, ""]
}
]
},
{
name: "PGP Decrypt: ASCII, Alice -> Bob",
input: `-----BEGIN PGP MESSAGE-----
Version: Keybase OpenPGP v2.0.77
Comment: https://keybase.io/crypto
wYwDPtlTQFIjCzoBBACSlbN7tmQVxR5ZD0rvCwXUkxO3RU8WgBkkmrTCUs9a+xrS
F9HuKcpX/N6XrwTXyuX3BN2tGys4zd6nHV8jYqBoIyWJsWe3viTa1dh/x4183+GP
fP61gizi3pj0gi2vfGnMhnThbdiO32PVKAeHLHBK+r3XlXZ0kzZCQKRgd55yr9Kk
Aa4SR+qpvtdobkDzbnbhcPLR6CQ8TMjTiNXEpgTc1i0JcP8jaMVFzBt8qgmDMdqU
H2qMY1O7hezH3fp+EZzCAccJMtK7VPk13WAgMRH22HirG4aK1i75IVOtjBgObzDh
8zKua7QLi6wJD/AtQ+D3/NgVpzoXwdoLvTjEcAyy+YWNWkJF/jvx3XV1Q/Fz7sHJ
/bspORYvbi591S4U0m4pikwiOZk=
=AVb/
-----END PGP MESSAGE-----`,
expectedOutput: ASCII_TEXT,
recipeConfig: [
{
"op": "PGP Decrypt",
"args": [ALICE_PRIVATE, ""]
}
]
},
]);

View File

@ -12,10 +12,10 @@ const ExtractTextPlugin = require("extract-text-webpack-plugin");
const banner = `/**
* CyberChef - The Cyber Swiss Army Knife
*
* @copyright Crown Copyright 2017
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* Copyright 2017 Crown Copyright
* Copyright 2016 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.