diff --git a/.eslintignore b/.eslintignore
index 83279ae8..6ff4ce5d 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1 +1,2 @@
-src/core/vendor/**
\ No newline at end of file
+src/core/vendor/**
+src/core/operations/legacy/**
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
index 5d0a6331..e512df1b 100755
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,6 +1,6 @@
{
"parserOptions": {
- "ecmaVersion": 8,
+ "ecmaVersion": 9,
"ecmaFeatures": {
"impliedStrict": true
},
@@ -84,7 +84,8 @@
"no-whitespace-before-property": "error",
"operator-linebreak": ["error", "after"],
"space-in-parens": "error",
- "no-var": "error"
+ "no-var": "error",
+ "prefer-const": "error"
},
"globals": {
"$": false,
diff --git a/Gruntfile.js b/Gruntfile.js
index 4b0eef0e..f3a2e2bd 100755
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -117,7 +117,7 @@ module.exports = function (grunt) {
* Generates an entry list for all the modules.
*/
function listEntryModules() {
- let entryModules = {};
+ const entryModules = {};
glob.sync("./src/core/config/modules/*.mjs").forEach(file => {
const basename = path.basename(file);
diff --git a/src/core/Chef.mjs b/src/core/Chef.mjs
index c7338184..9c52695e 100755
--- a/src/core/Chef.mjs
+++ b/src/core/Chef.mjs
@@ -121,9 +121,9 @@ class Chef {
silentBake(recipeConfig) {
log.debug("Running silent bake");
- let startTime = new Date().getTime(),
- recipe = new Recipe(recipeConfig),
- dish = new Dish("", Dish.STRING);
+ const startTime = new Date().getTime(),
+ recipe = new Recipe(recipeConfig),
+ dish = new Dish("", Dish.STRING);
try {
recipe.execute(dish);
diff --git a/src/core/ChefWorker.js b/src/core/ChefWorker.js
index 3ef65a95..604189e7 100644
--- a/src/core/ChefWorker.js
+++ b/src/core/ChefWorker.js
@@ -132,7 +132,7 @@ function silentBake(data) {
*/
function loadRequiredModules(recipeConfig) {
recipeConfig.forEach(op => {
- let module = self.OperationConfig[op.op].module;
+ const module = self.OperationConfig[op.op].module;
if (!OpModules.hasOwnProperty(module)) {
log.info("Loading module " + module);
diff --git a/src/core/FlowControl.js b/src/core/FlowControl.js
index daa1c343..73f29d78 100755
--- a/src/core/FlowControl.js
+++ b/src/core/FlowControl.js
@@ -23,7 +23,7 @@ const FlowControl = {
* @returns {Object} The updated state of the recipe.
*/
runFork: async function(state) {
- let opList = state.opList,
+ const opList = state.opList,
inputType = opList[state.progress].inputType,
outputType = opList[state.progress].outputType,
input = state.dish.get(inputType),
@@ -31,8 +31,8 @@ const FlowControl = {
splitDelim = ings[0],
mergeDelim = ings[1],
ignoreErrors = ings[2],
- subOpList = [],
- inputs = [],
+ subOpList = [];
+ let inputs = [],
i;
if (input)
@@ -48,8 +48,8 @@ const FlowControl = {
}
}
- let recipe = new Recipe(),
- output = "",
+ const recipe = new Recipe();
+ let output = "",
progress = 0;
state.forkOffset += state.progress + 1;
@@ -223,7 +223,7 @@ const FlowControl = {
}
if (regexStr !== "") {
- let strMatch = dish.get(Dish.STRING).search(regexStr) > -1;
+ const strMatch = dish.get(Dish.STRING).search(regexStr) > -1;
if (!invert && strMatch || invert && !strMatch) {
state.progress = jmpIndex;
state.numJumps++;
@@ -274,9 +274,9 @@ const FlowControl = {
*/
_getLabelIndex: function(name, state) {
for (let o = 0; o < state.opList.length; o++) {
- let operation = state.opList[o];
+ const operation = state.opList[o];
if (operation.name === "Label"){
- let ings = operation.ingValues;
+ const ings = operation.ingValues;
if (name === ings[0]) {
return o;
}
diff --git a/src/core/Recipe.mjs b/src/core/Recipe.mjs
index ce03f8bf..7afaeab9 100755
--- a/src/core/Recipe.mjs
+++ b/src/core/Recipe.mjs
@@ -226,7 +226,7 @@ class Recipe {
const highlights = [];
for (let i = 0; i < this.opList.length; i++) {
- let op = this.opList[i];
+ const op = this.opList[i];
if (op.disabled) continue;
// If any breakpoints are set, do not attempt to highlight
diff --git a/src/core/Utils.mjs b/src/core/Utils.mjs
index 116d856c..0ee29e0d 100755
--- a/src/core/Utils.mjs
+++ b/src/core/Utils.mjs
@@ -6,6 +6,7 @@
import utf8 from "utf8";
import moment from "moment-timezone";
+import {fromBase64} from "./lib/Base64";
/**
@@ -270,7 +271,7 @@ class Utils {
if (i < alphStr.length - 2 &&
alphStr[i+1] === "-" &&
alphStr[i] !== "\\") {
- let start = Utils.ord(alphStr[i]),
+ const start = Utils.ord(alphStr[i]),
end = Utils.ord(alphStr[i+2]);
for (let j = start; j <= end; j++) {
@@ -313,7 +314,7 @@ class Utils {
case "hex":
return Utils.fromHex(str);
case "base64":
- return Utils.fromBase64(str, null, "byteArray");
+ return fromBase64(str, null, "byteArray");
case "utf8":
return Utils.strToUtf8ByteArray(str);
case "latin1":
@@ -346,7 +347,7 @@ class Utils {
case "hex":
return Utils.byteArrayToChars(Utils.fromHex(str));
case "base64":
- return Utils.byteArrayToChars(Utils.fromBase64(str, null, "byteArray"));
+ return Utils.byteArrayToChars(fromBase64(str, null, "byteArray"));
case "utf8":
return utf8.encode(str);
case "latin1":
@@ -518,118 +519,6 @@ class Utils {
}
- /**
- * Base64's the input byte array using the given alphabet, returning a string.
- *
- * @param {byteArray|Uint8Array|string} data
- * @param {string} [alphabet="A-Za-z0-9+/="]
- * @returns {string}
- *
- * @example
- * // returns "SGVsbG8="
- * Utils.toBase64([72, 101, 108, 108, 111]);
- *
- * // returns "SGVsbG8="
- * Utils.toBase64("Hello");
- */
- static toBase64(data, alphabet="A-Za-z0-9+/=") {
- if (!data) return "";
- if (typeof data == "string") {
- data = Utils.strToByteArray(data);
- }
-
- alphabet = Utils.expandAlphRange(alphabet).join("");
-
- let output = "",
- chr1, chr2, chr3,
- enc1, enc2, enc3, enc4,
- i = 0;
-
- while (i < data.length) {
- chr1 = data[i++];
- chr2 = data[i++];
- chr3 = data[i++];
-
- enc1 = chr1 >> 2;
- enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
- enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
- enc4 = chr3 & 63;
-
- if (isNaN(chr2)) {
- enc3 = enc4 = 64;
- } else if (isNaN(chr3)) {
- enc4 = 64;
- }
-
- output += alphabet.charAt(enc1) + alphabet.charAt(enc2) +
- alphabet.charAt(enc3) + alphabet.charAt(enc4);
- }
-
- return output;
- }
-
-
- /**
- * UnBase64's the input string using the given alphabet, returning a byte array.
- *
- * @param {byteArray} data
- * @param {string} [alphabet="A-Za-z0-9+/="]
- * @param {string} [returnType="string"] - Either "string" or "byteArray"
- * @param {boolean} [removeNonAlphChars=true]
- * @returns {byteArray}
- *
- * @example
- * // returns "Hello"
- * Utils.fromBase64("SGVsbG8=");
- *
- * // returns [72, 101, 108, 108, 111]
- * Utils.fromBase64("SGVsbG8=", null, "byteArray");
- */
- static fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", removeNonAlphChars=true) {
- if (!data) {
- return returnType === "string" ? "" : [];
- }
-
- alphabet = Utils.expandAlphRange(alphabet).join("");
-
- let output = [],
- chr1, chr2, chr3,
- enc1, enc2, enc3, enc4,
- i = 0;
-
- if (removeNonAlphChars) {
- const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
- data = data.replace(re, "");
- }
-
- while (i < data.length) {
- enc1 = alphabet.indexOf(data.charAt(i++));
- enc2 = alphabet.indexOf(data.charAt(i++) || "=");
- enc3 = alphabet.indexOf(data.charAt(i++) || "=");
- enc4 = alphabet.indexOf(data.charAt(i++) || "=");
-
- enc2 = enc2 === -1 ? 64 : enc2;
- enc3 = enc3 === -1 ? 64 : enc3;
- enc4 = enc4 === -1 ? 64 : enc4;
-
- chr1 = (enc1 << 2) | (enc2 >> 4);
- chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
- chr3 = ((enc3 & 3) << 6) | enc4;
-
- output.push(chr1);
-
- if (enc3 !== 64) {
- output.push(chr2);
- }
- if (enc4 !== 64) {
- output.push(chr3);
- }
- }
-
- return returnType === "string" ? Utils.byteArrayToUtf8(output) : output;
- }
-
-
/**
* Convert a byte array into a hex string.
*
@@ -734,8 +623,8 @@ class Utils {
ignoreNext = false,
inString = false,
cell = "",
- line = [],
- lines = [];
+ line = [];
+ const lines = [];
for (let i = 0; i < data.length; i++) {
b = data[i];
@@ -952,10 +841,9 @@ class Utils {
// Parse bespoke recipe format
recipe = recipe.replace(/\n/g, "");
- let m,
- recipeRegex = /([^(]+)\(((?:'[^'\\]*(?:\\.[^'\\]*)*'|[^)/'])*)(\/[^)]+)?\)/g,
- recipeConfig = [],
- args;
+ let m, args;
+ const recipeRegex = /([^(]+)\(((?:'[^'\\]*(?:\\.[^'\\]*)*'|[^)/'])*)(\/[^)]+)?\)/g,
+ recipeConfig = [];
while ((m = recipeRegex.exec(recipe))) {
// Translate strings in args back to double-quotes
@@ -966,7 +854,7 @@ class Utils {
.replace(/\\'/g, "'"); // Unescape single quotes
args = "[" + args + "]";
- let op = {
+ const op = {
op: m[1].replace(/_/g, " "),
args: JSON.parse(args)
};
@@ -1252,7 +1140,7 @@ export default Utils;
* ["One", "Two", "Three", "One"].unique();
*/
Array.prototype.unique = function() {
- let u = {}, a = [];
+ const u = {}, a = [];
for (let i = 0, l = this.length; i < l; i++) {
if (u.hasOwnProperty(this[i])) {
continue;
diff --git a/src/core/config/Categories.js b/src/core/config/Categories.js
index 985bd3c0..8adf96f5 100755
--- a/src/core/config/Categories.js
+++ b/src/core/config/Categories.js
@@ -21,340 +21,340 @@ const Categories = [
{
name: "Favourites",
ops: []
- }/*,
+ },
{
name: "Data format",
ops: [
- "To Hexdump",
- "From Hexdump",
- "To Hex",
- "From Hex",
- "To Charcode",
- "From Charcode",
- "To Decimal",
- "From Decimal",
- "To Binary",
- "From Binary",
- "To Octal",
- "From Octal",
+ // "To Hexdump",
+ // "From Hexdump",
+ // "To Hex",
+ // "From Hex",
+ // "To Charcode",
+ // "From Charcode",
+ // "To Decimal",
+ // "From Decimal",
+ // "To Binary",
+ // "From Binary",
+ // "To Octal",
+ // "From Octal",
"To Base64",
"From Base64",
"Show Base64 offsets",
"To Base32",
"From Base32",
- "To Base58",
- "From Base58",
- "To Base",
- "From Base",
- "To BCD",
- "From BCD",
- "To HTML Entity",
- "From HTML Entity",
- "URL Encode",
- "URL Decode",
- "Escape Unicode Characters",
- "Unescape Unicode Characters",
- "To Quoted Printable",
- "From Quoted Printable",
- "To Punycode",
- "From Punycode",
- "To Hex Content",
- "From Hex Content",
- "PEM to Hex",
- "Hex to PEM",
- "Parse ASN.1 hex string",
- "Change IP format",
- "Encode text",
- "Decode text",
- "Swap endianness",
+ // "To Base58",
+ // "From Base58",
+ // "To Base",
+ // "From Base",
+ // "To BCD",
+ // "From BCD",
+ // "To HTML Entity",
+ // "From HTML Entity",
+ // "URL Encode",
+ // "URL Decode",
+ // "Escape Unicode Characters",
+ // "Unescape Unicode Characters",
+ // "To Quoted Printable",
+ // "From Quoted Printable",
+ // "To Punycode",
+ // "From Punycode",
+ // "To Hex Content",
+ // "From Hex Content",
+ // "PEM to Hex",
+ // "Hex to PEM",
+ // "Parse ASN.1 hex string",
+ // "Change IP format",
+ // "Encode text",
+ // "Decode text",
+ // "Swap endianness",
+ // ]
+ // },
+ // {
+ // name: "Encryption / Encoding",
+ // ops: [
+ // "AES Encrypt",
+ // "AES Decrypt",
+ // "Blowfish Encrypt",
+ // "Blowfish Decrypt",
+ // "DES Encrypt",
+ // "DES Decrypt",
+ // "Triple DES Encrypt",
+ // "Triple DES Decrypt",
+ // "RC2 Encrypt",
+ // "RC2 Decrypt",
+ // "RC4",
+ // "RC4 Drop",
+ // "ROT13",
+ // "ROT47",
+ // "XOR",
+ // "XOR Brute Force",
+ // "Vigenère Encode",
+ // "Vigenère Decode",
+ // "To Morse Code",
+ // "From Morse Code",
+ // "Bifid Cipher Encode",
+ // "Bifid Cipher Decode",
+ // "Affine Cipher Encode",
+ // "Affine Cipher Decode",
+ // "Atbash Cipher",
+ // "Substitute",
+ // "Derive PBKDF2 key",
+ // "Derive EVP key",
+ // "Bcrypt",
+ // "Scrypt",
+ // "Pseudo-Random Number Generator",
]
},
- {
- name: "Encryption / Encoding",
- ops: [
- "AES Encrypt",
- "AES Decrypt",
- "Blowfish Encrypt",
- "Blowfish Decrypt",
- "DES Encrypt",
- "DES Decrypt",
- "Triple DES Encrypt",
- "Triple DES Decrypt",
- "RC2 Encrypt",
- "RC2 Decrypt",
- "RC4",
- "RC4 Drop",
- "ROT13",
- "ROT47",
- "XOR",
- "XOR Brute Force",
- "Vigenère Encode",
- "Vigenère Decode",
- "To Morse Code",
- "From Morse Code",
- "Bifid Cipher Encode",
- "Bifid Cipher Decode",
- "Affine Cipher Encode",
- "Affine Cipher Decode",
- "Atbash Cipher",
- "Substitute",
- "Derive PBKDF2 key",
- "Derive EVP key",
- "Bcrypt",
- "Scrypt",
- "Pseudo-Random Number Generator",
- ]
- },
- {
- name: "Public Key",
- ops: [
- "Parse X.509 certificate",
- "Parse ASN.1 hex string",
- "PEM to Hex",
- "Hex to PEM",
- "Hex to Object Identifier",
- "Object Identifier to Hex",
- ]
- },
- {
- name: "Arithmetic / Logic",
- ops: [
- "XOR",
- "XOR Brute Force",
- "OR",
- "NOT",
- "AND",
- "ADD",
- "SUB",
- "Sum",
- "Subtract",
- "Multiply",
- "Divide",
- "Mean",
- "Median",
- "Standard Deviation",
- "Bit shift left",
- "Bit shift right",
- "Rotate left",
- "Rotate right",
- "ROT13",
- ]
- },
- {
- name: "Networking",
- ops: [
- "HTTP request",
- "Strip HTTP headers",
- "Parse User Agent",
- "Parse IP range",
- "Parse IPv6 address",
- "Parse IPv4 header",
- "Parse URI",
- "URL Encode",
- "URL Decode",
- "Format MAC addresses",
- "Change IP format",
- "Group IP addresses",
- "Encode NetBIOS Name",
- "Decode NetBIOS Name",
- ]
- },
- {
- name: "Language",
- ops: [
- "Encode text",
- "Decode text",
- "Unescape Unicode Characters",
- ]
- },
- {
- name: "Utils",
- ops: [
- "Diff",
- "Remove whitespace",
- "Remove null bytes",
- "To Upper case",
- "To Lower case",
- "Add line numbers",
- "Remove line numbers",
- "Reverse",
- "Sort",
- "Unique",
- "Split",
- "Filter",
- "Head",
- "Tail",
- "Count occurrences",
- "Expand alphabet range",
- "Drop bytes",
- "Take bytes",
- "Pad lines",
- "Find / Replace",
- "Regular expression",
- "Offset checker",
- "Hamming Distance",
- "Convert distance",
- "Convert area",
- "Convert mass",
- "Convert speed",
- "Convert data units",
- "Parse UNIX file permissions",
- "Swap endianness",
- "Parse colour code",
- "Escape string",
- "Unescape string",
- "Pseudo-Random Number Generator",
- "Sleep",
- ]
- },
- {
- name: "Date / Time",
- ops: [
- "Parse DateTime",
- "Translate DateTime Format",
- "From UNIX Timestamp",
- "To UNIX Timestamp",
- "Windows Filetime to UNIX Timestamp",
- "UNIX Timestamp to Windows Filetime",
- "Extract dates",
- "Sleep",
- ]
- },
- {
- name: "Extractors",
- ops: [
- "Strings",
- "Extract IP addresses",
- "Extract email addresses",
- "Extract MAC addresses",
- "Extract URLs",
- "Extract domains",
- "Extract file paths",
- "Extract dates",
- "Regular expression",
- "XPath expression",
- "JPath expression",
- "CSS selector",
- "Extract EXIF",
- ]
- },
- {
- name: "Compression",
- ops: [
- "Raw Deflate",
- "Raw Inflate",
- "Zlib Deflate",
- "Zlib Inflate",
- "Gzip",
- "Gunzip",
- "Zip",
- "Unzip",
- "Bzip2 Decompress",
- "Tar",
- "Untar",
- ]
- },
- {
- name: "Hashing",
- ops: [
- "Analyse hash",
- "Generate all hashes",
- "MD2",
- "MD4",
- "MD5",
- "MD6",
- "SHA0",
- "SHA1",
- "SHA2",
- "SHA3",
- "Keccak",
- "Shake",
- "RIPEMD",
- "HAS-160",
- "Whirlpool",
- "Snefru",
- "SSDEEP",
- "CTPH",
- "Compare SSDEEP hashes",
- "Compare CTPH hashes",
- "HMAC",
- "Bcrypt",
- "Bcrypt compare",
- "Bcrypt parse",
- "Scrypt",
- "Fletcher-8 Checksum",
- "Fletcher-16 Checksum",
- "Fletcher-32 Checksum",
- "Fletcher-64 Checksum",
- "Adler-32 Checksum",
- "CRC-16 Checksum",
- "CRC-32 Checksum",
- "TCP/IP Checksum",
- ]
- },
- {
- name: "Code tidy",
- ops: [
- "Syntax highlighter",
- "Generic Code Beautify",
- "JavaScript Parser",
- "JavaScript Beautify",
- "JavaScript Minify",
- "JSON Beautify",
- "JSON Minify",
- "XML Beautify",
- "XML Minify",
- "SQL Beautify",
- "SQL Minify",
- "CSS Beautify",
- "CSS Minify",
- "XPath expression",
- "JPath expression",
- "CSS selector",
- "PHP Deserialize",
- "Microsoft Script Decoder",
- "Strip HTML tags",
- "Diff",
- "To Snake case",
- "To Camel case",
- "To Kebab case",
- "BSON serialise",
- "BSON deserialise",
- ]
- },
- {
- name: "Other",
- ops: [
- "Entropy",
- "Frequency distribution",
- "Chi Square",
- "Detect File Type",
- "Scan for Embedded Files",
- "Disassemble x86",
- "Pseudo-Random Number Generator",
- "Generate UUID",
- "Generate TOTP",
- "Generate HOTP",
- "Render Image",
- "Remove EXIF",
- "Extract EXIF",
- "Numberwang",
- "XKCD Random Number",
- ]
- },
- {
- name: "Flow control",
- ops: [
- "Fork",
- "Merge",
- "Register",
- "Label",
- "Jump",
- "Conditional Jump",
- "Return",
- "Comment"
- ]
- },*/
+ // {
+ // name: "Public Key",
+ // ops: [
+ // "Parse X.509 certificate",
+ // "Parse ASN.1 hex string",
+ // "PEM to Hex",
+ // "Hex to PEM",
+ // "Hex to Object Identifier",
+ // "Object Identifier to Hex",
+ // ]
+ // },
+ // {
+ // name: "Arithmetic / Logic",
+ // ops: [
+ // "XOR",
+ // "XOR Brute Force",
+ // "OR",
+ // "NOT",
+ // "AND",
+ // "ADD",
+ // "SUB",
+ // "Sum",
+ // "Subtract",
+ // "Multiply",
+ // "Divide",
+ // "Mean",
+ // "Median",
+ // "Standard Deviation",
+ // "Bit shift left",
+ // "Bit shift right",
+ // "Rotate left",
+ // "Rotate right",
+ // "ROT13",
+ // ]
+ // },
+ // {
+ // name: "Networking",
+ // ops: [
+ // "HTTP request",
+ // "Strip HTTP headers",
+ // "Parse User Agent",
+ // "Parse IP range",
+ // "Parse IPv6 address",
+ // "Parse IPv4 header",
+ // "Parse URI",
+ // "URL Encode",
+ // "URL Decode",
+ // "Format MAC addresses",
+ // "Change IP format",
+ // "Group IP addresses",
+ // "Encode NetBIOS Name",
+ // "Decode NetBIOS Name",
+ // ]
+ // },
+ // {
+ // name: "Language",
+ // ops: [
+ // "Encode text",
+ // "Decode text",
+ // "Unescape Unicode Characters",
+ // ]
+ // },
+ // {
+ // name: "Utils",
+ // ops: [
+ // "Diff",
+ // "Remove whitespace",
+ // "Remove null bytes",
+ // "To Upper case",
+ // "To Lower case",
+ // "Add line numbers",
+ // "Remove line numbers",
+ // "Reverse",
+ // "Sort",
+ // "Unique",
+ // "Split",
+ // "Filter",
+ // "Head",
+ // "Tail",
+ // "Count occurrences",
+ // "Expand alphabet range",
+ // "Drop bytes",
+ // "Take bytes",
+ // "Pad lines",
+ // "Find / Replace",
+ // "Regular expression",
+ // "Offset checker",
+ // "Hamming Distance",
+ // "Convert distance",
+ // "Convert area",
+ // "Convert mass",
+ // "Convert speed",
+ // "Convert data units",
+ // "Parse UNIX file permissions",
+ // "Swap endianness",
+ // "Parse colour code",
+ // "Escape string",
+ // "Unescape string",
+ // "Pseudo-Random Number Generator",
+ // "Sleep",
+ // ]
+ // },
+ // {
+ // name: "Date / Time",
+ // ops: [
+ // "Parse DateTime",
+ // "Translate DateTime Format",
+ // "From UNIX Timestamp",
+ // "To UNIX Timestamp",
+ // "Windows Filetime to UNIX Timestamp",
+ // "UNIX Timestamp to Windows Filetime",
+ // "Extract dates",
+ // "Sleep",
+ // ]
+ // },
+ // {
+ // name: "Extractors",
+ // ops: [
+ // "Strings",
+ // "Extract IP addresses",
+ // "Extract email addresses",
+ // "Extract MAC addresses",
+ // "Extract URLs",
+ // "Extract domains",
+ // "Extract file paths",
+ // "Extract dates",
+ // "Regular expression",
+ // "XPath expression",
+ // "JPath expression",
+ // "CSS selector",
+ // "Extract EXIF",
+ // ]
+ // },
+ // {
+ // name: "Compression",
+ // ops: [
+ // "Raw Deflate",
+ // "Raw Inflate",
+ // "Zlib Deflate",
+ // "Zlib Inflate",
+ // "Gzip",
+ // "Gunzip",
+ // "Zip",
+ // "Unzip",
+ // "Bzip2 Decompress",
+ // "Tar",
+ // "Untar",
+ // ]
+ // },
+ // {
+ // name: "Hashing",
+ // ops: [
+ // "Analyse hash",
+ // "Generate all hashes",
+ // "MD2",
+ // "MD4",
+ // "MD5",
+ // "MD6",
+ // "SHA0",
+ // "SHA1",
+ // "SHA2",
+ // "SHA3",
+ // "Keccak",
+ // "Shake",
+ // "RIPEMD",
+ // "HAS-160",
+ // "Whirlpool",
+ // "Snefru",
+ // "SSDEEP",
+ // "CTPH",
+ // "Compare SSDEEP hashes",
+ // "Compare CTPH hashes",
+ // "HMAC",
+ // "Bcrypt",
+ // "Bcrypt compare",
+ // "Bcrypt parse",
+ // "Scrypt",
+ // "Fletcher-8 Checksum",
+ // "Fletcher-16 Checksum",
+ // "Fletcher-32 Checksum",
+ // "Fletcher-64 Checksum",
+ // "Adler-32 Checksum",
+ // "CRC-16 Checksum",
+ // "CRC-32 Checksum",
+ // "TCP/IP Checksum",
+ // ]
+ // },
+ // {
+ // name: "Code tidy",
+ // ops: [
+ // "Syntax highlighter",
+ // "Generic Code Beautify",
+ // "JavaScript Parser",
+ // "JavaScript Beautify",
+ // "JavaScript Minify",
+ // "JSON Beautify",
+ // "JSON Minify",
+ // "XML Beautify",
+ // "XML Minify",
+ // "SQL Beautify",
+ // "SQL Minify",
+ // "CSS Beautify",
+ // "CSS Minify",
+ // "XPath expression",
+ // "JPath expression",
+ // "CSS selector",
+ // "PHP Deserialize",
+ // "Microsoft Script Decoder",
+ // "Strip HTML tags",
+ // "Diff",
+ // "To Snake case",
+ // "To Camel case",
+ // "To Kebab case",
+ // "BSON serialise",
+ // "BSON deserialise",
+ // ]
+ // },
+ // {
+ // name: "Other",
+ // ops: [
+ // "Entropy",
+ // "Frequency distribution",
+ // "Chi Square",
+ // "Detect File Type",
+ // "Scan for Embedded Files",
+ // "Disassemble x86",
+ // "Pseudo-Random Number Generator",
+ // "Generate UUID",
+ // "Generate TOTP",
+ // "Generate HOTP",
+ // "Render Image",
+ // "Remove EXIF",
+ // "Extract EXIF",
+ // "Numberwang",
+ // "XKCD Random Number",
+ // ]
+ // },
+ // {
+ // name: "Flow control",
+ // ops: [
+ // "Fork",
+ // "Merge",
+ // "Register",
+ // "Label",
+ // "Jump",
+ // "Conditional Jump",
+ // "Return",
+ // "Comment"
+ // ]
+ // },
];
export default Categories;
diff --git a/src/core/config/OperationConfig.json b/src/core/config/OperationConfig.json
index f8107d91..0eef1c42 100644
--- a/src/core/config/OperationConfig.json
+++ b/src/core/config/OperationConfig.json
@@ -1,68 +1,20 @@
{
- "To Base64": {
+ "From Base32": {
"module": "Default",
- "description": "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.
This operation decodes data from an ASCII Base64 string back into its raw format.
e.g. aGVsbG8=
becomes hello
",
- "inputType": "ArrayBuffer",
- "outputType": "string",
+ "description": "Base32 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers. It uses a smaller set of characters than Base64, usually the uppercase alphabet and the numbers 2 to 7.",
+ "inputType": "string",
+ "outputType": "byteArray",
"flowControl": false,
"args": [
{
"name": "Alphabet",
- "type": "editableOption",
- "value": [
- {
- "name": "Standard: A-Za-z0-9+/=",
- "value": "A-Za-z0-9+/="
- },
- {
- "name": "URL safe: A-Za-z0-9-_",
- "value": "A-Za-z0-9-_"
- },
- {
- "name": "Filename safe: A-Za-z0-9+-=",
- "value": "A-Za-z0-9+\\-="
- },
- {
- "name": "itoa64: ./0-9A-Za-z=",
- "value": "./0-9A-Za-z="
- },
- {
- "name": "XML: A-Za-z0-9_.",
- "value": "A-Za-z0-9_."
- },
- {
- "name": "y64: A-Za-z0-9._-",
- "value": "A-Za-z0-9._-"
- },
- {
- "name": "z64: 0-9a-zA-Z+/=",
- "value": "0-9a-zA-Z+/="
- },
- {
- "name": "Radix-64: 0-9A-Za-z+/=",
- "value": "0-9A-Za-z+/="
- },
- {
- "name": "Uuencoding: [space]-_",
- "value": " -_"
- },
- {
- "name": "Xxencoding: +-0-9A-Za-z",
- "value": "+\\-0-9A-Za-z"
- },
- {
- "name": "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r",
- "value": "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"
- },
- {
- "name": "ROT13: N-ZA-Mn-za-m0-9+/=",
- "value": "N-ZA-Mn-za-m0-9+/="
- },
- {
- "name": "UNIX crypt: ./0-9A-Za-z",
- "value": "./0-9A-Za-z"
- }
- ]
+ "type": "binaryString",
+ "value": "A-Z2-7="
+ },
+ {
+ "name": "Remove non-alphabet chars",
+ "type": "boolean",
+ "value": true
}
]
},
@@ -137,5 +89,105 @@
"value": true
}
]
+ },
+ "Show Base64 offsets": {
+ "module": "Default",
+ "description": "When a string is within a block of data and the whole block is Base64'd, the string itself could be represented in Base64 in three distinct ways depending on its offset within the block.
This operation shows all possible offsets for a given string so that each possible encoding can be considered.",
+ "inputType": "byteArray",
+ "outputType": "html",
+ "flowControl": false,
+ "args": [
+ {
+ "name": "Alphabet",
+ "type": "binaryString",
+ "value": "A-Za-z0-9+/="
+ },
+ {
+ "name": "Show variable chars and padding",
+ "type": "boolean",
+ "value": true
+ }
+ ]
+ },
+ "To Base32": {
+ "module": "Default",
+ "description": "Base32 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers. It uses a smaller set of characters than Base64, usually the uppercase alphabet and the numbers 2 to 7.",
+ "inputType": "byteArray",
+ "outputType": "string",
+ "flowControl": false,
+ "args": [
+ {
+ "name": "Alphabet",
+ "type": "binaryString",
+ "value": "A-Z2-7="
+ }
+ ]
+ },
+ "To Base64": {
+ "module": "Default",
+ "description": "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.
This operation decodes data from an ASCII Base64 string back into its raw format.
e.g. aGVsbG8=
becomes hello
",
+ "inputType": "ArrayBuffer",
+ "outputType": "string",
+ "flowControl": false,
+ "args": [
+ {
+ "name": "Alphabet",
+ "type": "editableOption",
+ "value": [
+ {
+ "name": "Standard: A-Za-z0-9+/=",
+ "value": "A-Za-z0-9+/="
+ },
+ {
+ "name": "URL safe: A-Za-z0-9-_",
+ "value": "A-Za-z0-9-_"
+ },
+ {
+ "name": "Filename safe: A-Za-z0-9+-=",
+ "value": "A-Za-z0-9+\\-="
+ },
+ {
+ "name": "itoa64: ./0-9A-Za-z=",
+ "value": "./0-9A-Za-z="
+ },
+ {
+ "name": "XML: A-Za-z0-9_.",
+ "value": "A-Za-z0-9_."
+ },
+ {
+ "name": "y64: A-Za-z0-9._-",
+ "value": "A-Za-z0-9._-"
+ },
+ {
+ "name": "z64: 0-9a-zA-Z+/=",
+ "value": "0-9a-zA-Z+/="
+ },
+ {
+ "name": "Radix-64: 0-9A-Za-z+/=",
+ "value": "0-9A-Za-z+/="
+ },
+ {
+ "name": "Uuencoding: [space]-_",
+ "value": " -_"
+ },
+ {
+ "name": "Xxencoding: +-0-9A-Za-z",
+ "value": "+\\-0-9A-Za-z"
+ },
+ {
+ "name": "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r",
+ "value": "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"
+ },
+ {
+ "name": "ROT13: N-ZA-Mn-za-m0-9+/=",
+ "value": "N-ZA-Mn-za-m0-9+/="
+ },
+ {
+ "name": "UNIX crypt: ./0-9A-Za-z",
+ "value": "./0-9A-Za-z"
+ }
+ ]
+ }
+ ]
}
}
\ No newline at end of file
diff --git a/src/core/config/generateConfig.mjs b/src/core/config/generateConfig.mjs
index 038bd70b..1ccab8e1 100644
--- a/src/core/config/generateConfig.mjs
+++ b/src/core/config/generateConfig.mjs
@@ -14,7 +14,7 @@
import path from "path";
import fs from "fs";
import process from "process";
-import OpIndex from "../operations/index";
+import * as Ops from "../operations/index";
const dir = path.join(process.cwd() + "/src/core/config/");
if (!fs.existsSync(dir)) {
@@ -25,14 +25,14 @@ if (!fs.existsSync(dir)) {
}
-let operationConfig = {},
+const operationConfig = {},
modules = {};
/**
* Generate operation config and module lists.
*/
-OpIndex.forEach(opObj => {
- const op = new opObj();
+for (const opObj in Ops) {
+ const op = new Ops[opObj]();
operationConfig[op.name] = {
module: op.module,
@@ -45,8 +45,8 @@ OpIndex.forEach(opObj => {
if (!modules.hasOwnProperty(op.module))
modules[op.module] = {};
- modules[op.module][op.name] = op.name.replace(/\s/g, "");
-});
+ modules[op.module][op.name] = opObj;
+}
/**
@@ -67,7 +67,7 @@ fs.writeFile(
/**
* Write modules.
*/
-for (let module in modules) {
+for (const module in modules) {
let code = `/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/generateConfig.mjs
*
@@ -77,17 +77,17 @@ for (let module in modules) {
*/
`;
- for (let opName in modules[module]) {
+ for (const opName in modules[module]) {
const objName = modules[module][opName];
code += `import ${objName} from "../../operations/${objName}";\n`;
}
code += `
-let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
+const OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.${module} = {
`;
- for (let opName in modules[module]) {
+ for (const opName in modules[module]) {
const objName = modules[module][opName];
code += ` "${opName}": ${objName},\n`;
}
@@ -123,18 +123,18 @@ let opModulesCode = `/**
*/
`;
-for (let module in modules) {
+for (const module in modules) {
opModulesCode += `import ${module}Module from "./${module}";\n`;
}
opModulesCode += `
-let OpModules = {};
+const OpModules = {};
Object.assign(
OpModules,
`;
-for (let module in modules) {
+for (const module in modules) {
opModulesCode += ` ${module}Module,\n`;
}
diff --git a/src/core/config/modules/Default.mjs b/src/core/config/modules/Default.mjs
index cf08f71f..d5ac0684 100644
--- a/src/core/config/modules/Default.mjs
+++ b/src/core/config/modules/Default.mjs
@@ -5,14 +5,20 @@
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
-import ToBase64 from "../../operations/ToBase64";
+import FromBase32 from "../../operations/FromBase32";
import FromBase64 from "../../operations/FromBase64";
+import ShowBase64Offsets from "../../operations/ShowBase64Offsets";
+import ToBase32 from "../../operations/ToBase32";
+import ToBase64 from "../../operations/ToBase64";
-let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
+const OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Default = {
- "To Base64": ToBase64,
+ "From Base32": FromBase32,
"From Base64": FromBase64,
+ "Show Base64 offsets": ShowBase64Offsets,
+ "To Base32": ToBase32,
+ "To Base64": ToBase64,
};
export default OpModules;
diff --git a/src/core/config/modules/OpModules.mjs b/src/core/config/modules/OpModules.mjs
index f1e1308a..58dfb09e 100644
--- a/src/core/config/modules/OpModules.mjs
+++ b/src/core/config/modules/OpModules.mjs
@@ -9,7 +9,7 @@
*/
import DefaultModule from "./Default";
-let OpModules = {};
+const OpModules = {};
Object.assign(
OpModules,
diff --git a/src/core/lib/Base64.mjs b/src/core/lib/Base64.mjs
index 814c2bbf..dd4ab878 100755
--- a/src/core/lib/Base64.mjs
+++ b/src/core/lib/Base64.mjs
@@ -1,263 +1,129 @@
-import Utils from "../Utils";
-
-
/**
- * Base64 operations.
+ * Base64 functions.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
- *
- * @namespace
*/
-const Base64 = {
- /**
- * @constant
- * @default
- */
- BASE32_ALPHABET: "A-Z2-7=",
+import Utils from "../Utils";
- /**
- * To Base32 operation.
- *
- * @param {byteArray} input
- * @param {Object[]} args
- * @returns {string}
- */
- runTo32: function(input, args) {
- if (!input) return "";
- let alphabet = args[0] ?
- Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
- output = "",
- chr1, chr2, chr3, chr4, chr5,
- enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
- i = 0;
+/**
+ * Base64's the input byte array using the given alphabet, returning a string.
+ *
+ * @param {byteArray|Uint8Array|string} data
+ * @param {string} [alphabet="A-Za-z0-9+/="]
+ * @returns {string}
+ *
+ * @example
+ * // returns "SGVsbG8="
+ * toBase64([72, 101, 108, 108, 111]);
+ *
+ * // returns "SGVsbG8="
+ * toBase64("Hello");
+ */
+export function toBase64(data, alphabet="A-Za-z0-9+/=") {
+ if (!data) return "";
+ if (typeof data == "string") {
+ data = Utils.strToByteArray(data);
+ }
- while (i < input.length) {
- chr1 = input[i++];
- chr2 = input[i++];
- chr3 = input[i++];
- chr4 = input[i++];
- chr5 = input[i++];
+ alphabet = Utils.expandAlphRange(alphabet).join("");
- enc1 = chr1 >> 3;
- enc2 = ((chr1 & 7) << 2) | (chr2 >> 6);
- enc3 = (chr2 >> 1) & 31;
- enc4 = ((chr2 & 1) << 4) | (chr3 >> 4);
- enc5 = ((chr3 & 15) << 1) | (chr4 >> 7);
- enc6 = (chr4 >> 2) & 31;
- enc7 = ((chr4 & 3) << 3) | (chr5 >> 5);
- enc8 = chr5 & 31;
+ let output = "",
+ chr1, chr2, chr3,
+ enc1, enc2, enc3, enc4,
+ i = 0;
- if (isNaN(chr2)) {
- enc3 = enc4 = enc5 = enc6 = enc7 = enc8 = 32;
- } else if (isNaN(chr3)) {
- enc5 = enc6 = enc7 = enc8 = 32;
- } else if (isNaN(chr4)) {
- enc6 = enc7 = enc8 = 32;
- } else if (isNaN(chr5)) {
- enc8 = 32;
- }
+ while (i < data.length) {
+ chr1 = data[i++];
+ chr2 = data[i++];
+ chr3 = data[i++];
- output += alphabet.charAt(enc1) + alphabet.charAt(enc2) + alphabet.charAt(enc3) +
- alphabet.charAt(enc4) + alphabet.charAt(enc5) + alphabet.charAt(enc6) +
- alphabet.charAt(enc7) + alphabet.charAt(enc8);
+ enc1 = chr1 >> 2;
+ enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+ enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+ enc4 = chr3 & 63;
+
+ if (isNaN(chr2)) {
+ enc3 = enc4 = 64;
+ } else if (isNaN(chr3)) {
+ enc4 = 64;
}
- return output;
- },
+ output += alphabet.charAt(enc1) + alphabet.charAt(enc2) +
+ alphabet.charAt(enc3) + alphabet.charAt(enc4);
+ }
+
+ return output;
+}
- /**
- * From Base32 operation.
- *
- * @param {string} input
- * @param {Object[]} args
- * @returns {byteArray}
- */
- runFrom32: function(input, args) {
- if (!input) return [];
+/**
+ * UnBase64's the input string using the given alphabet, returning a byte array.
+ *
+ * @param {byteArray} data
+ * @param {string} [alphabet="A-Za-z0-9+/="]
+ * @param {string} [returnType="string"] - Either "string" or "byteArray"
+ * @param {boolean} [removeNonAlphChars=true]
+ * @returns {byteArray}
+ *
+ * @example
+ * // returns "Hello"
+ * fromBase64("SGVsbG8=");
+ *
+ * // returns [72, 101, 108, 108, 111]
+ * fromBase64("SGVsbG8=", null, "byteArray");
+ */
+export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", removeNonAlphChars=true) {
+ if (!data) {
+ return returnType === "string" ? "" : [];
+ }
- let alphabet = args[0] ?
- Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
- removeNonAlphChars = args[0];
+ alphabet = Utils.expandAlphRange(alphabet).join("");
- let output = [],
- chr1, chr2, chr3, chr4, chr5,
- enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
- i = 0;
+ const output = [];
+ let chr1, chr2, chr3,
+ enc1, enc2, enc3, enc4,
+ i = 0;
- if (removeNonAlphChars) {
- const re = new RegExp("[^" + alphabet.replace(/[\]\\\-^]/g, "\\$&") + "]", "g");
- input = input.replace(re, "");
+ if (removeNonAlphChars) {
+ const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
+ data = data.replace(re, "");
+ }
+
+ while (i < data.length) {
+ enc1 = alphabet.indexOf(data.charAt(i++));
+ enc2 = alphabet.indexOf(data.charAt(i++) || "=");
+ enc3 = alphabet.indexOf(data.charAt(i++) || "=");
+ enc4 = alphabet.indexOf(data.charAt(i++) || "=");
+
+ enc2 = enc2 === -1 ? 64 : enc2;
+ enc3 = enc3 === -1 ? 64 : enc3;
+ enc4 = enc4 === -1 ? 64 : enc4;
+
+ chr1 = (enc1 << 2) | (enc2 >> 4);
+ chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+ chr3 = ((enc3 & 3) << 6) | enc4;
+
+ output.push(chr1);
+
+ if (enc3 !== 64) {
+ output.push(chr2);
}
-
- while (i < input.length) {
- enc1 = alphabet.indexOf(input.charAt(i++));
- enc2 = alphabet.indexOf(input.charAt(i++) || "=");
- enc3 = alphabet.indexOf(input.charAt(i++) || "=");
- enc4 = alphabet.indexOf(input.charAt(i++) || "=");
- enc5 = alphabet.indexOf(input.charAt(i++) || "=");
- enc6 = alphabet.indexOf(input.charAt(i++) || "=");
- enc7 = alphabet.indexOf(input.charAt(i++) || "=");
- enc8 = alphabet.indexOf(input.charAt(i++) || "=");
-
- chr1 = (enc1 << 3) | (enc2 >> 2);
- chr2 = ((enc2 & 3) << 6) | (enc3 << 1) | (enc4 >> 4);
- chr3 = ((enc4 & 15) << 4) | (enc5 >> 1);
- chr4 = ((enc5 & 1) << 7) | (enc6 << 2) | (enc7 >> 3);
- chr5 = ((enc7 & 7) << 5) | enc8;
-
- output.push(chr1);
- if (enc2 & 3 !== 0 || enc3 !== 32) output.push(chr2);
- if (enc4 & 15 !== 0 || enc5 !== 32) output.push(chr3);
- if (enc5 & 1 !== 0 || enc6 !== 32) output.push(chr4);
- if (enc7 & 7 !== 0 || enc8 !== 32) output.push(chr5);
+ if (enc4 !== 64) {
+ output.push(chr3);
}
+ }
- return output;
- },
+ return returnType === "string" ? Utils.byteArrayToUtf8(output) : output;
+}
- /**
- * @constant
- * @default
- */
- SHOW_IN_BINARY: false,
- /**
- * @constant
- * @default
- */
- OFFSETS_SHOW_VARIABLE: true,
-
- /**
- * Show Base64 offsets operation.
- *
- * @param {byteArray} input
- * @param {Object[]} args
- * @returns {html}
- */
- runOffsets: function(input, args) {
- let alphabet = args[0] || Base64.ALPHABET,
- showVariable = args[1],
- offset0 = Utils.toBase64(input, alphabet),
- offset1 = Utils.toBase64([0].concat(input), alphabet),
- offset2 = Utils.toBase64([0, 0].concat(input), alphabet),
- len0 = offset0.indexOf("="),
- len1 = offset1.indexOf("="),
- len2 = offset2.indexOf("="),
- script = "",
- staticSection = "",
- padding = "";
-
- if (input.length < 1) {
- return "Please enter a string.";
- }
-
- // Highlight offset 0
- if (len0 % 4 === 2) {
- staticSection = offset0.slice(0, -3);
- offset0 = "" +
- staticSection + "" +
- "" + offset0.substr(offset0.length - 3, 1) + "" +
- "" + offset0.substr(offset0.length - 2) + "";
- } else if (len0 % 4 === 3) {
- staticSection = offset0.slice(0, -2);
- offset0 = "" +
- staticSection + "" +
- "" + offset0.substr(offset0.length - 2, 1) + "" +
- "" + offset0.substr(offset0.length - 1) + "";
- } else {
- staticSection = offset0;
- offset0 = "" +
- staticSection + "";
- }
-
- if (!showVariable) {
- offset0 = staticSection;
- }
-
-
- // Highlight offset 1
- padding = "" + offset1.substr(0, 1) + "" +
- "" + offset1.substr(1, 1) + "";
- offset1 = offset1.substr(2);
- if (len1 % 4 === 2) {
- staticSection = offset1.slice(0, -3);
- offset1 = padding + "" +
- staticSection + "" +
- "" + offset1.substr(offset1.length - 3, 1) + "" +
- "" + offset1.substr(offset1.length - 2) + "";
- } else if (len1 % 4 === 3) {
- staticSection = offset1.slice(0, -2);
- offset1 = padding + "" +
- staticSection + "" +
- "" + offset1.substr(offset1.length - 2, 1) + "" +
- "" + offset1.substr(offset1.length - 1) + "";
- } else {
- staticSection = offset1;
- offset1 = padding + "" +
- staticSection + "";
- }
-
- if (!showVariable) {
- offset1 = staticSection;
- }
-
- // Highlight offset 2
- padding = "" + offset2.substr(0, 2) + "" +
- "" + offset2.substr(2, 1) + "";
- offset2 = offset2.substr(3);
- if (len2 % 4 === 2) {
- staticSection = offset2.slice(0, -3);
- offset2 = padding + "" +
- staticSection + "" +
- "" + offset2.substr(offset2.length - 3, 1) + "" +
- "" + offset2.substr(offset2.length - 2) + "";
- } else if (len2 % 4 === 3) {
- staticSection = offset2.slice(0, -2);
- offset2 = padding + "" +
- staticSection + "" +
- "" + offset2.substr(offset2.length - 2, 1) + "" +
- "" + offset2.substr(offset2.length - 1) + "";
- } else {
- staticSection = offset2;
- offset2 = padding + "" +
- staticSection + "";
- }
-
- if (!showVariable) {
- offset2 = staticSection;
- }
-
- return (showVariable ? "Characters highlighted in green could change if the input is surrounded by more data." +
- "\nCharacters highlighted in red are for padding purposes only." +
- "\nUnhighlighted characters are static." +
- "\nHover over the static sections to see what they decode to on their own.\n" +
- "\nOffset 0: " + offset0 +
- "\nOffset 1: " + offset1 +
- "\nOffset 2: " + offset2 +
- script :
- offset0 + "\n" + offset1 + "\n" + offset2);
- },
-
-};
-
-export default Base64;
-
-export const ALPHABET = "A-Za-z0-9+/=";
-
+/**
+ * Base64 alphabets.
+ */
export const ALPHABET_OPTIONS = [
{name: "Standard: A-Za-z0-9+/=", value: "A-Za-z0-9+/="},
{name: "URL safe: A-Za-z0-9-_", value: "A-Za-z0-9-_"},
diff --git a/src/core/operations/FromBase32.mjs b/src/core/operations/FromBase32.mjs
new file mode 100644
index 00000000..0334ef51
--- /dev/null
+++ b/src/core/operations/FromBase32.mjs
@@ -0,0 +1,90 @@
+/**
+ * @author n1474335 [n1474335@gmail.com]
+ * @copyright Crown Copyright 2016
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation";
+import Utils from "../Utils";
+
+/**
+ * From Base32 operation
+ */
+class FromBase32 extends Operation {
+
+ /**
+ * FromBase32 constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "From Base32";
+ this.module = "Default";
+ this.description = "Base32 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers. It uses a smaller set of characters than Base64, usually the uppercase alphabet and the numbers 2 to 7.";
+ this.inputType = "string";
+ this.outputType = "byteArray";
+ this.args = [
+ {
+ name: "Alphabet",
+ type: "binaryString",
+ value: "A-Z2-7="
+ },
+ {
+ name: "Remove non-alphabet chars",
+ type: "boolean",
+ value: true
+ }
+ ];
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {byteArray}
+ */
+ run(input, args) {
+ if (!input) return [];
+
+ const alphabet = args[0] ?
+ Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
+ removeNonAlphChars = args[1],
+ output = [];
+
+ let chr1, chr2, chr3, chr4, chr5,
+ enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
+ i = 0;
+
+ if (removeNonAlphChars) {
+ const re = new RegExp("[^" + alphabet.replace(/[\]\\\-^]/g, "\\$&") + "]", "g");
+ input = input.replace(re, "");
+ }
+
+ while (i < input.length) {
+ enc1 = alphabet.indexOf(input.charAt(i++));
+ enc2 = alphabet.indexOf(input.charAt(i++) || "=");
+ enc3 = alphabet.indexOf(input.charAt(i++) || "=");
+ enc4 = alphabet.indexOf(input.charAt(i++) || "=");
+ enc5 = alphabet.indexOf(input.charAt(i++) || "=");
+ enc6 = alphabet.indexOf(input.charAt(i++) || "=");
+ enc7 = alphabet.indexOf(input.charAt(i++) || "=");
+ enc8 = alphabet.indexOf(input.charAt(i++) || "=");
+
+ chr1 = (enc1 << 3) | (enc2 >> 2);
+ chr2 = ((enc2 & 3) << 6) | (enc3 << 1) | (enc4 >> 4);
+ chr3 = ((enc4 & 15) << 4) | (enc5 >> 1);
+ chr4 = ((enc5 & 1) << 7) | (enc6 << 2) | (enc7 >> 3);
+ chr5 = ((enc7 & 7) << 5) | enc8;
+
+ output.push(chr1);
+ if (enc2 & 3 !== 0 || enc3 !== 32) output.push(chr2);
+ if (enc4 & 15 !== 0 || enc5 !== 32) output.push(chr3);
+ if (enc5 & 1 !== 0 || enc6 !== 32) output.push(chr4);
+ if (enc7 & 7 !== 0 || enc8 !== 32) output.push(chr5);
+ }
+
+ return output;
+ }
+
+}
+
+export default FromBase32;
diff --git a/src/core/operations/FromBase64.mjs b/src/core/operations/FromBase64.mjs
index e8ac9ae7..cab86835 100644
--- a/src/core/operations/FromBase64.mjs
+++ b/src/core/operations/FromBase64.mjs
@@ -5,8 +5,7 @@
*/
import Operation from "../Operation";
-import Utils from "../Utils";
-import {ALPHABET, ALPHABET_OPTIONS} from "../lib/Base64";
+import {fromBase64, ALPHABET_OPTIONS} from "../lib/Base64";
/**
* From Base64 operation
@@ -14,17 +13,17 @@ import {ALPHABET, ALPHABET_OPTIONS} from "../lib/Base64";
class FromBase64 extends Operation {
/**
- * ToBase64 constructor
+ * FromBase64 constructor
*/
constructor() {
super();
- this.name = "From Base64";
- this.module = "Default";
+ this.name = "From Base64";
+ this.module = "Default";
this.description = "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.
This operation decodes data from an ASCII Base64 string back into its raw format.
e.g. aGVsbG8=
becomes hello
";
- this.inputType = "string";
- this.outputType = "byteArray";
- this.args = [
+ this.inputType = "string";
+ this.outputType = "byteArray";
+ this.args = [
{
name: "Alphabet",
type: "editableOption",
@@ -44,10 +43,9 @@ class FromBase64 extends Operation {
* @returns {string}
*/
run(input, args) {
- let alphabet = args[0] || ALPHABET,
- removeNonAlphChars = args[1];
+ const [alphabet, removeNonAlphChars] = args;
- return Utils.fromBase64(input, alphabet, "byteArray", removeNonAlphChars);
+ return fromBase64(input, alphabet, "byteArray", removeNonAlphChars);
}
/**
diff --git a/src/core/operations/ShowBase64Offsets.mjs b/src/core/operations/ShowBase64Offsets.mjs
new file mode 100644
index 00000000..2f44296f
--- /dev/null
+++ b/src/core/operations/ShowBase64Offsets.mjs
@@ -0,0 +1,162 @@
+/**
+ * @author n1474335 [n1474335@gmail.com]
+ * @copyright Crown Copyright 2016
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation";
+import Utils from "../Utils";
+import {fromBase64, toBase64} from "../lib/Base64";
+
+/**
+ * Show Base64 offsets operation
+ */
+class ShowBase64Offsets extends Operation {
+
+ /**
+ * ShowBase64Offsets constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "Show Base64 offsets";
+ this.module = "Default";
+ this.description = "When a string is within a block of data and the whole block is Base64'd, the string itself could be represented in Base64 in three distinct ways depending on its offset within the block.
This operation shows all possible offsets for a given string so that each possible encoding can be considered.";
+ this.inputType = "byteArray";
+ this.outputType = "html";
+ this.args = [
+ {
+ name: "Alphabet",
+ type: "binaryString",
+ value: "A-Za-z0-9+/="
+ },
+ {
+ name: "Show variable chars and padding",
+ type: "boolean",
+ value: true
+ }
+ ];
+ }
+
+ /**
+ * @param {byteArray} input
+ * @param {Object[]} args
+ * @returns {html}
+ */
+ run(input, args) {
+ const [alphabet, showVariable] = args;
+
+ let offset0 = toBase64(input, alphabet),
+ offset1 = toBase64([0].concat(input), alphabet),
+ offset2 = toBase64([0, 0].concat(input), alphabet),
+ staticSection = "",
+ padding = "";
+
+ const len0 = offset0.indexOf("="),
+ len1 = offset1.indexOf("="),
+ len2 = offset2.indexOf("="),
+ script = "";
+
+ if (input.length < 1) {
+ return "Please enter a string.";
+ }
+
+ // Highlight offset 0
+ if (len0 % 4 === 2) {
+ staticSection = offset0.slice(0, -3);
+ offset0 = "" +
+ staticSection + "" +
+ "" + offset0.substr(offset0.length - 3, 1) + "" +
+ "" + offset0.substr(offset0.length - 2) + "";
+ } else if (len0 % 4 === 3) {
+ staticSection = offset0.slice(0, -2);
+ offset0 = "" +
+ staticSection + "" +
+ "" + offset0.substr(offset0.length - 2, 1) + "" +
+ "" + offset0.substr(offset0.length - 1) + "";
+ } else {
+ staticSection = offset0;
+ offset0 = "" +
+ staticSection + "";
+ }
+
+ if (!showVariable) {
+ offset0 = staticSection;
+ }
+
+
+ // Highlight offset 1
+ padding = "" + offset1.substr(0, 1) + "" +
+ "" + offset1.substr(1, 1) + "";
+ offset1 = offset1.substr(2);
+ if (len1 % 4 === 2) {
+ staticSection = offset1.slice(0, -3);
+ offset1 = padding + "" +
+ staticSection + "" +
+ "" + offset1.substr(offset1.length - 3, 1) + "" +
+ "" + offset1.substr(offset1.length - 2) + "";
+ } else if (len1 % 4 === 3) {
+ staticSection = offset1.slice(0, -2);
+ offset1 = padding + "" +
+ staticSection + "" +
+ "" + offset1.substr(offset1.length - 2, 1) + "" +
+ "" + offset1.substr(offset1.length - 1) + "";
+ } else {
+ staticSection = offset1;
+ offset1 = padding + "" +
+ staticSection + "";
+ }
+
+ if (!showVariable) {
+ offset1 = staticSection;
+ }
+
+ // Highlight offset 2
+ padding = "" + offset2.substr(0, 2) + "" +
+ "" + offset2.substr(2, 1) + "";
+ offset2 = offset2.substr(3);
+ if (len2 % 4 === 2) {
+ staticSection = offset2.slice(0, -3);
+ offset2 = padding + "" +
+ staticSection + "" +
+ "" + offset2.substr(offset2.length - 3, 1) + "" +
+ "" + offset2.substr(offset2.length - 2) + "";
+ } else if (len2 % 4 === 3) {
+ staticSection = offset2.slice(0, -2);
+ offset2 = padding + "" +
+ staticSection + "" +
+ "" + offset2.substr(offset2.length - 2, 1) + "" +
+ "" + offset2.substr(offset2.length - 1) + "";
+ } else {
+ staticSection = offset2;
+ offset2 = padding + "" +
+ staticSection + "";
+ }
+
+ if (!showVariable) {
+ offset2 = staticSection;
+ }
+
+ return (showVariable ? "Characters highlighted in green could change if the input is surrounded by more data." +
+ "\nCharacters highlighted in red are for padding purposes only." +
+ "\nUnhighlighted characters are static." +
+ "\nHover over the static sections to see what they decode to on their own.\n" +
+ "\nOffset 0: " + offset0 +
+ "\nOffset 1: " + offset1 +
+ "\nOffset 2: " + offset2 +
+ script :
+ offset0 + "\n" + offset1 + "\n" + offset2);
+ }
+
+}
+
+export default ShowBase64Offsets;
diff --git a/src/core/operations/ToBase32.mjs b/src/core/operations/ToBase32.mjs
new file mode 100644
index 00000000..1b217a34
--- /dev/null
+++ b/src/core/operations/ToBase32.mjs
@@ -0,0 +1,85 @@
+/**
+ * @author n1474335 [n1474335@gmail.com]
+ * @copyright Crown Copyright 2016
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation";
+import Utils from "../Utils";
+
+/**
+ * To Base32 operation
+ */
+class ToBase32 extends Operation {
+
+ /**
+ * ToBase32 constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "To Base32";
+ this.module = "Default";
+ this.description = "Base32 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers. It uses a smaller set of characters than Base64, usually the uppercase alphabet and the numbers 2 to 7.";
+ this.inputType = "byteArray";
+ this.outputType = "string";
+ this.args = [
+ {
+ name: "Alphabet",
+ type: "binaryString",
+ value: "A-Z2-7="
+ }
+ ];
+ }
+
+ /**
+ * @param {byteArray} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ run(input, args) {
+ if (!input) return "";
+
+ const alphabet = args[0] ? Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=";
+ let output = "",
+ chr1, chr2, chr3, chr4, chr5,
+ enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
+ i = 0;
+
+ while (i < input.length) {
+ chr1 = input[i++];
+ chr2 = input[i++];
+ chr3 = input[i++];
+ chr4 = input[i++];
+ chr5 = input[i++];
+
+ enc1 = chr1 >> 3;
+ enc2 = ((chr1 & 7) << 2) | (chr2 >> 6);
+ enc3 = (chr2 >> 1) & 31;
+ enc4 = ((chr2 & 1) << 4) | (chr3 >> 4);
+ enc5 = ((chr3 & 15) << 1) | (chr4 >> 7);
+ enc6 = (chr4 >> 2) & 31;
+ enc7 = ((chr4 & 3) << 3) | (chr5 >> 5);
+ enc8 = chr5 & 31;
+
+ if (isNaN(chr2)) {
+ enc3 = enc4 = enc5 = enc6 = enc7 = enc8 = 32;
+ } else if (isNaN(chr3)) {
+ enc5 = enc6 = enc7 = enc8 = 32;
+ } else if (isNaN(chr4)) {
+ enc6 = enc7 = enc8 = 32;
+ } else if (isNaN(chr5)) {
+ enc8 = 32;
+ }
+
+ output += alphabet.charAt(enc1) + alphabet.charAt(enc2) + alphabet.charAt(enc3) +
+ alphabet.charAt(enc4) + alphabet.charAt(enc5) + alphabet.charAt(enc6) +
+ alphabet.charAt(enc7) + alphabet.charAt(enc8);
+ }
+
+ return output;
+ }
+
+}
+
+export default ToBase32;
diff --git a/src/core/operations/ToBase64.mjs b/src/core/operations/ToBase64.mjs
index 82a78d7a..967ce8ed 100644
--- a/src/core/operations/ToBase64.mjs
+++ b/src/core/operations/ToBase64.mjs
@@ -5,8 +5,7 @@
*/
import Operation from "../Operation";
-import Utils from "../Utils";
-import {ALPHABET, ALPHABET_OPTIONS} from "../lib/Base64";
+import {toBase64, ALPHABET_OPTIONS} from "../lib/Base64";
/**
* To Base64 operation
@@ -19,12 +18,12 @@ class ToBase64 extends Operation {
constructor() {
super();
- this.name = "To Base64";
- this.module = "Default";
+ this.name = "To Base64";
+ this.module = "Default";
this.description = "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.
This operation decodes data from an ASCII Base64 string back into its raw format.
e.g. aGVsbG8=
becomes hello
";
- this.inputType = "ArrayBuffer";
- this.outputType = "string";
- this.args = [
+ this.inputType = "ArrayBuffer";
+ this.outputType = "string";
+ this.args = [
{
name: "Alphabet",
type: "editableOption",
@@ -39,8 +38,8 @@ class ToBase64 extends Operation {
* @returns {string}
*/
run(input, args) {
- const alphabet = args[0] || ALPHABET;
- return Utils.toBase64(new Uint8Array(input), alphabet);
+ const alphabet = args[0];
+ return toBase64(new Uint8Array(input), alphabet);
}
/**
diff --git a/src/core/operations/index.mjs b/src/core/operations/index.mjs
index 84aadaaf..3f3675ed 100644
--- a/src/core/operations/index.mjs
+++ b/src/core/operations/index.mjs
@@ -1,7 +1,13 @@
import ToBase64 from "./ToBase64";
import FromBase64 from "./FromBase64";
+import ToBase32 from "./ToBase32";
+import FromBase32 from "./FromBase32";
+import ShowBase64Offsets from "./ShowBase64Offsets";
-export default [
+export {
ToBase64,
- FromBase64
-];
+ FromBase64,
+ ToBase32,
+ FromBase32,
+ ShowBase64Offsets
+};
diff --git a/src/core/operations/legacy/Cipher.js b/src/core/operations/legacy/Cipher.js
index f44aca20..b343f218 100755
--- a/src/core/operations/legacy/Cipher.js
+++ b/src/core/operations/legacy/Cipher.js
@@ -1,4 +1,5 @@
import Utils from "../Utils.js";
+import {toBase64} from "../lib/Base64";
import CryptoJS from "crypto-js";
import forge from "imports-loader?jQuery=>null!node-forge/dist/forge.min.js";
import {blowfish as Blowfish} from "sladex-blowfish";
@@ -366,7 +367,7 @@ DES uses a key length of 8 bytes (64 bits).`;
input = Utils.convertToByteString(input, inputType);
- Blowfish.setIV(Utils.toBase64(iv), 0);
+ Blowfish.setIV(toBase64(iv), 0);
const enc = Blowfish.encrypt(input, key, {
outputType: Cipher._BLOWFISH_OUTPUT_TYPE_LOOKUP[outputType],
@@ -395,7 +396,7 @@ DES uses a key length of 8 bytes (64 bits).`;
input = inputType === "Raw" ? Utils.strToByteArray(input) : input;
- Blowfish.setIV(Utils.toBase64(iv), 0);
+ Blowfish.setIV(toBase64(iv), 0);
const result = Blowfish.decrypt(input, key, {
outputType: Cipher._BLOWFISH_OUTPUT_TYPE_LOOKUP[inputType], // This actually means inputType. The library is weird.
diff --git a/src/core/operations/legacy/Image.js b/src/core/operations/legacy/Image.js
index 4ddffe74..cf3c568f 100644
--- a/src/core/operations/legacy/Image.js
+++ b/src/core/operations/legacy/Image.js
@@ -2,6 +2,7 @@ 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";
/**
@@ -96,7 +97,7 @@ const Image = {
case "Base64":
// Don't trust the Base64 entered by the user.
// Unwrap it first, then re-encode later.
- input = Utils.fromBase64(input, null, "byteArray");
+ input = fromBase64(input, null, "byteArray");
break;
case "Raw":
default:
@@ -113,7 +114,7 @@ const Image = {
}
// Add image data to URI
- dataURI += "base64," + Utils.toBase64(input);
+ dataURI += "base64," + toBase64(input);
return "";
},
diff --git a/src/core/operations/legacy/PublicKey.js b/src/core/operations/legacy/PublicKey.js
index 66b177a5..c0512705 100755
--- a/src/core/operations/legacy/PublicKey.js
+++ b/src/core/operations/legacy/PublicKey.js
@@ -1,4 +1,5 @@
import Utils from "../Utils.js";
+import {fromBase64} from "../lib/Base64";
import * as r from "jsrsasign";
@@ -43,7 +44,7 @@ const PublicKey = {
cert.readCertPEM(input);
break;
case "Base64":
- cert.readCertHex(Utils.toHex(Utils.fromBase64(input, null, "byteArray"), ""));
+ cert.readCertHex(Utils.toHex(fromBase64(input, null, "byteArray"), ""));
break;
case "Raw":
cert.readCertHex(Utils.toHex(Utils.strToByteArray(input), ""));
diff --git a/src/web/App.js b/src/web/App.js
index d38658ea..29b858f2 100755
--- a/src/web/App.js
+++ b/src/web/App.js
@@ -1,4 +1,5 @@
import Utils from "../core/Utils";
+import {fromBase64} from "../core/lib/Base64";
import Manager from "./Manager.js";
import HTMLCategory from "./HTMLCategory.js";
import HTMLOperation from "./HTMLOperation.js";
@@ -193,12 +194,12 @@ App.prototype.populateOperationsList = function() {
let i;
for (i = 0; i < this.categories.length; i++) {
- let catConf = this.categories[i],
+ const catConf = this.categories[i],
selected = i === 0,
cat = new HTMLCategory(catConf.name, selected);
for (let j = 0; j < catConf.ops.length; j++) {
- let opName = catConf.ops[j],
+ const opName = catConf.ops[j],
op = new HTMLOperation(opName, this.operations[opName], this, this.manager);
cat.addOperation(op);
}
@@ -405,7 +406,7 @@ App.prototype.loadURIParams = function() {
// Read in input data from URI params
if (this.uriParams.input) {
try {
- const inputData = Utils.fromBase64(this.uriParams.input);
+ const inputData = fromBase64(this.uriParams.input);
this.setInput(inputData);
} catch (err) {}
}
@@ -503,11 +504,11 @@ App.prototype.resetLayout = function() {
*/
App.prototype.setCompileMessage = function() {
// Display time since last build and compile message
- let now = new Date(),
+ const now = new Date(),
timeSinceCompile = Utils.fuzzyTime(now.getTime() - window.compileTime);
// Calculate previous version to compare to
- let prev = PKG_VERSION.split(".").map(n => {
+ const prev = PKG_VERSION.split(".").map(n => {
return parseInt(n, 10);
});
if (prev[2] > 0) prev[2]--;
@@ -574,7 +575,7 @@ App.prototype.alert = function(str, style, timeout, silent) {
style = style || "danger";
timeout = timeout || 0;
- let alertEl = document.getElementById("alert"),
+ const alertEl = document.getElementById("alert"),
alertContent = document.getElementById("alert-content");
alertEl.classList.remove("alert-danger");
diff --git a/src/web/ControlsWaiter.js b/src/web/ControlsWaiter.js
index 15d4c095..bf3082cd 100755
--- a/src/web/ControlsWaiter.js
+++ b/src/web/ControlsWaiter.js
@@ -1,4 +1,5 @@
import Utils from "../core/Utils";
+import {toBase64} from "../core/lib/Base64";
/**
@@ -169,7 +170,7 @@ ControlsWaiter.prototype.generateStateUrl = function(includeRecipe, includeInput
window.location.host +
window.location.pathname;
const recipeStr = Utils.generatePrettyRecipe(recipeConfig);
- const inputStr = Utils.toBase64(this.app.getInput(), "A-Za-z0-9+/"); // B64 alphabet with no padding
+ const inputStr = toBase64(this.app.getInput(), "A-Za-z0-9+/"); // B64 alphabet with no padding
includeRecipe = includeRecipe && (recipeConfig.length > 0);
// Only inlcude input if it is less than 50KB (51200 * 4/3 as it is Base64 encoded)
@@ -271,9 +272,9 @@ ControlsWaiter.prototype.saveButtonClick = function() {
return;
}
- let savedRecipes = localStorage.savedRecipes ?
- JSON.parse(localStorage.savedRecipes) : [],
- recipeId = localStorage.recipeId || 0;
+ const savedRecipes = localStorage.savedRecipes ?
+ JSON.parse(localStorage.savedRecipes) : [];
+ let recipeId = localStorage.recipeId || 0;
savedRecipes.push({
id: ++recipeId,
diff --git a/src/web/HTMLIngredient.js b/src/web/HTMLIngredient.js
index 3f360f04..ca28ab01 100755
--- a/src/web/HTMLIngredient.js
+++ b/src/web/HTMLIngredient.js
@@ -32,12 +32,14 @@ const HTMLIngredient = function(config, app, manager) {
* @returns {string}
*/
HTMLIngredient.prototype.toHtml = function() {
- let inline = (this.type === "boolean" ||
- this.type === "number" ||
- this.type === "option" ||
- this.type === "shortString" ||
- this.type === "binaryShortString"),
- html = inline ? "" : "