mirror of
https://github.com/gchq/CyberChef
synced 2024-12-29 14:03:10 +00:00
pull from master
This commit is contained in:
commit
18f6ab451d
34 changed files with 2164 additions and 1798 deletions
19
.babelrc
19
.babelrc
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"presets": [
|
||||
["@babel/preset-env", {
|
||||
"targets": {
|
||||
"chrome": 40,
|
||||
"firefox": 35,
|
||||
"edge": 14,
|
||||
"node": "6.5"
|
||||
},
|
||||
"modules": false,
|
||||
"useBuiltIns": "entry"
|
||||
}]
|
||||
],
|
||||
"plugins": [
|
||||
["babel-plugin-transform-builtin-extend", {
|
||||
"globals": ["Error"]
|
||||
}]
|
||||
]
|
||||
}
|
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -1,5 +1,11 @@
|
|||
# Changelog
|
||||
All notable changes to CyberChef will be documented in this file.
|
||||
All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master).
|
||||
|
||||
### [8.12.0] - 2018-11-21
|
||||
- 'Citrix CTX1 Encode' and 'Citrix CTX1 Decode' operations added [@bwhitn] | [#428]
|
||||
|
||||
### [8.11.0] - 2018-11-13
|
||||
- 'CSV to JSON' and 'JSON to CSV' operations added [@n1474335] | [#277]
|
||||
|
||||
### [8.10.0] - 2018-11-07
|
||||
- 'Remove Diacritics' operation added [@klaxon1] | [#387]
|
||||
|
@ -49,7 +55,7 @@ All notable changes to CyberChef will be documented in this file.
|
|||
- Added support for loading, processing and downloading files up to 500MB [@n1474335] | [#224]
|
||||
|
||||
## [6.0.0] - 2017-09-19
|
||||
- Threading support added. All recipe processing moved into a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) to increase performance and allowing long-running operations to be cancelled [@n1474335] | [#173]
|
||||
- Threading support added. All recipe processing moved into a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) to increase performance and to allow long-running operations to be cancelled [@n1474335] | [#173]
|
||||
- Module system created so that operations relying on large libraries can be downloaded separately as required, reducing the initial loading time for the app [@n1474335] | [#173]
|
||||
|
||||
## [5.0.0] - 2017-03-30
|
||||
|
@ -59,6 +65,11 @@ All notable changes to CyberChef will be documented in this file.
|
|||
- Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306)
|
||||
|
||||
|
||||
|
||||
[8.12.0]: https://github.com/gchq/CyberChef/releases/tag/v8.12.0
|
||||
[8.11.0]: https://github.com/gchq/CyberChef/releases/tag/v8.11.0
|
||||
[8.10.0]: https://github.com/gchq/CyberChef/releases/tag/v8.10.0
|
||||
[8.9.0]: https://github.com/gchq/CyberChef/releases/tag/v8.9.0
|
||||
[8.8.0]: https://github.com/gchq/CyberChef/releases/tag/v8.8.0
|
||||
[8.7.0]: https://github.com/gchq/CyberChef/releases/tag/v8.7.0
|
||||
[8.6.0]: https://github.com/gchq/CyberChef/releases/tag/v8.6.0
|
||||
|
@ -84,6 +95,7 @@ All notable changes to CyberChef will be documented in this file.
|
|||
[@PenguinGeorge]: https://github.com/PenguinGeorge
|
||||
[@arnydo]: https://github.com/arnydo
|
||||
[@klaxon1]: https://github.com/klaxon1
|
||||
[@bwhitn]: https://github.com/bwhitn
|
||||
|
||||
[#95]: https://github.com/gchq/CyberChef/pull/299
|
||||
[#173]: https://github.com/gchq/CyberChef/pull/173
|
||||
|
@ -91,6 +103,7 @@ All notable changes to CyberChef will be documented in this file.
|
|||
[#239]: https://github.com/gchq/CyberChef/pull/239
|
||||
[#248]: https://github.com/gchq/CyberChef/pull/248
|
||||
[#255]: https://github.com/gchq/CyberChef/issues/255
|
||||
[#277]: https://github.com/gchq/CyberChef/issues/277
|
||||
[#281]: https://github.com/gchq/CyberChef/pull/281
|
||||
[#284]: https://github.com/gchq/CyberChef/pull/284
|
||||
[#294]: https://github.com/gchq/CyberChef/pull/294
|
||||
|
@ -105,3 +118,4 @@ All notable changes to CyberChef will be documented in this file.
|
|||
[#351]: https://github.com/gchq/CyberChef/pull/351
|
||||
[#387]: https://github.com/gchq/CyberChef/pull/387
|
||||
[#394]: https://github.com/gchq/CyberChef/pull/394
|
||||
[#428]: https://github.com/gchq/CyberChef/pull/428
|
||||
|
|
|
@ -144,7 +144,7 @@ module.exports = function (grunt) {
|
|||
options: {
|
||||
configFile: "./.eslintrc.json"
|
||||
},
|
||||
configs: ["Gruntfile.js"],
|
||||
configs: ["*.js"],
|
||||
core: ["src/core/**/*.{js,mjs}", "!src/core/vendor/**/*", "!src/core/operations/legacy/**/*"],
|
||||
web: ["src/web/**/*.{js,mjs}"],
|
||||
node: ["src/node/**/*.{js,mjs}"],
|
||||
|
|
23
babel.config.js
Normal file
23
babel.config.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
module.exports = function(api) {
|
||||
api.cache.forever();
|
||||
|
||||
return {
|
||||
"presets": [
|
||||
["@babel/preset-env", {
|
||||
"targets": {
|
||||
"chrome": 40,
|
||||
"firefox": 35,
|
||||
"edge": 14,
|
||||
"node": "6.5"
|
||||
},
|
||||
"modules": false,
|
||||
"useBuiltIns": "entry"
|
||||
}]
|
||||
],
|
||||
"plugins": [
|
||||
["babel-plugin-transform-builtin-extend", {
|
||||
"globals": ["Error"]
|
||||
}]
|
||||
]
|
||||
};
|
||||
};
|
2895
package-lock.json
generated
2895
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cyberchef",
|
||||
"version": "8.10.1",
|
||||
"version": "8.12.3",
|
||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||
"author": "n1474335 <n1474335@gmail.com>",
|
||||
"homepage": "https://gchq.github.io/CyberChef",
|
||||
|
|
|
@ -9,6 +9,7 @@ import moment from "moment-timezone";
|
|||
import {fromBase64} from "./lib/Base64";
|
||||
import {fromHex} from "./lib/Hex";
|
||||
import {fromDecimal} from "./lib/Decimal";
|
||||
import {fromBinary} from "./lib/Binary";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -298,7 +299,7 @@ class Utils {
|
|||
* Accepts hex, Base64, UTF8 and Latin1 strings.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {string} type - One of "Hex", "Decimal", "Base64", "UTF8" or "Latin1"
|
||||
* @param {string} type - One of "Binary", "Hex", "Decimal", "Base64", "UTF8" or "Latin1"
|
||||
* @returns {byteArray}
|
||||
*
|
||||
* @example
|
||||
|
@ -313,6 +314,8 @@ class Utils {
|
|||
*/
|
||||
static convertToByteArray(str, type) {
|
||||
switch (type.toLowerCase()) {
|
||||
case "binary":
|
||||
return fromBinary(str);
|
||||
case "hex":
|
||||
return fromHex(str);
|
||||
case "decimal":
|
||||
|
@ -333,7 +336,7 @@ class Utils {
|
|||
* Accepts hex, Base64, UTF8 and Latin1 strings.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {string} type - One of "Hex", "Decimal", "Base64", "UTF8" or "Latin1"
|
||||
* @param {string} type - One of "Binary", "Hex", "Decimal", "Base64", "UTF8" or "Latin1"
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
|
@ -348,6 +351,8 @@ class Utils {
|
|||
*/
|
||||
static convertToByteString(str, type) {
|
||||
switch (type.toLowerCase()) {
|
||||
case "binary":
|
||||
return Utils.byteArrayToChars(fromBinary(str));
|
||||
case "hex":
|
||||
return Utils.byteArrayToChars(fromHex(str));
|
||||
case "decimal":
|
||||
|
@ -568,6 +573,10 @@ class Utils {
|
|||
cell = "";
|
||||
lines.push(line);
|
||||
line = [];
|
||||
// Skip next byte if it is also a line delim (e.g. \r\n)
|
||||
if (lineDelims.indexOf(next) >= 0 && next !== b) {
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
cell += b;
|
||||
}
|
||||
|
|
|
@ -54,7 +54,9 @@
|
|||
"From MessagePack",
|
||||
"To Braille",
|
||||
"From Braille",
|
||||
"Parse TLV"
|
||||
"Parse TLV",
|
||||
"CSV to JSON",
|
||||
"JSON to CSV"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -93,6 +95,8 @@
|
|||
"JWT Sign",
|
||||
"JWT Verify",
|
||||
"JWT Decode",
|
||||
"Citrix CTX1 Encode",
|
||||
"Citrix CTX1 Decode",
|
||||
"Pseudo-Random Number Generator"
|
||||
]
|
||||
},
|
||||
|
|
|
@ -126,14 +126,14 @@ export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", r
|
|||
* Base64 alphabets.
|
||||
*/
|
||||
export const ALPHABET_OPTIONS = [
|
||||
{name: "Standard: A-Za-z0-9+/=", value: "A-Za-z0-9+/="},
|
||||
{name: "URL safe: A-Za-z0-9-_", value: "A-Za-z0-9-_"},
|
||||
{name: "Standard (RFC 4648): A-Za-z0-9+/=", value: "A-Za-z0-9+/="},
|
||||
{name: "URL safe (RFC 4648 \u00A75): 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: "Radix-64 (RFC 4880): 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"},
|
||||
|
|
70
src/core/lib/Binary.mjs
Normal file
70
src/core/lib/Binary.mjs
Normal file
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* Binary functions.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "../Utils";
|
||||
|
||||
|
||||
/**
|
||||
* Convert a byte array into a binary string.
|
||||
*
|
||||
* @param {Uint8Array|byteArray} data
|
||||
* @param {string} [delim="Space"]
|
||||
* @param {number} [padding=8]
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
* // returns "00010000 00100000 00110000"
|
||||
* toBinary([10,20,30]);
|
||||
*
|
||||
* // returns "00010000 00100000 00110000"
|
||||
* toBinary([10,20,30], ":");
|
||||
*/
|
||||
export function toBinary(data, delim="Space", padding=8) {
|
||||
if (!data) return "";
|
||||
|
||||
delim = Utils.charRep(delim);
|
||||
let output = "";
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
output += data[i].toString(2).padStart(padding, "0") + delim;
|
||||
}
|
||||
|
||||
if (delim.length) {
|
||||
return output.slice(0, -delim.length);
|
||||
} else {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a binary string into a byte array.
|
||||
*
|
||||
* @param {string} data
|
||||
* @param {string} [delim]
|
||||
* @param {number} [byteLen=8]
|
||||
* @returns {byteArray}
|
||||
*
|
||||
* @example
|
||||
* // returns [10,20,30]
|
||||
* fromBinary("00010000 00100000 00110000");
|
||||
*
|
||||
* // returns [10,20,30]
|
||||
* fromBinary("00010000:00100000:00110000", "Colon");
|
||||
*/
|
||||
export function fromBinary(data, delim="Space", byteLen=8) {
|
||||
const delimRegex = Utils.regexRep(delim);
|
||||
data = data.replace(delimRegex, "");
|
||||
|
||||
const output = [];
|
||||
for (let i = 0; i < data.length; i += byteLen) {
|
||||
output.push(parseInt(data.substr(i, byteLen), 2));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
|
@ -116,3 +116,9 @@ export function sub(operand, key) {
|
|||
const result = operand - key;
|
||||
return (result < 0) ? 256 + result : result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delimiter options for bitwise operations
|
||||
*/
|
||||
export const BITWISE_OP_DELIMS = ["Hex", "Decimal", "Binary", "Base64", "UTF8", "Latin1"];
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import { bitOp, add } from "../lib/BitwiseOp";
|
||||
import { bitOp, add, BITWISE_OP_DELIMS } from "../lib/BitwiseOp";
|
||||
|
||||
/**
|
||||
* ADD operation
|
||||
|
@ -30,7 +30,7 @@ class ADD extends Operation {
|
|||
"name": "Key",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "Decimal", "Base64", "UTF8", "Latin1"]
|
||||
"toggleValues": BITWISE_OP_DELIMS
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import { bitOp, and } from "../lib/BitwiseOp";
|
||||
import { bitOp, and, BITWISE_OP_DELIMS } from "../lib/BitwiseOp";
|
||||
|
||||
/**
|
||||
* AND operation
|
||||
|
@ -30,7 +30,7 @@ class AND extends Operation {
|
|||
"name": "Key",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "Decimal", "Base64", "UTF8", "Latin1"]
|
||||
"toggleValues": BITWISE_OP_DELIMS
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
80
src/core/operations/CSVToJSON.mjs
Normal file
80
src/core/operations/CSVToJSON.mjs
Normal file
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Utils from "../Utils";
|
||||
|
||||
/**
|
||||
* CSV to JSON operation
|
||||
*/
|
||||
class CSVToJSON extends Operation {
|
||||
|
||||
/**
|
||||
* CSVToJSON constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "CSV to JSON";
|
||||
this.module = "Default";
|
||||
this.description = "Converts a CSV file to JSON format.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Comma-separated_values";
|
||||
this.inputType = "string";
|
||||
this.outputType = "JSON";
|
||||
this.args = [
|
||||
{
|
||||
name: "Cell delimiters",
|
||||
type: "binaryShortString",
|
||||
value: ","
|
||||
},
|
||||
{
|
||||
name: "Row delimiters",
|
||||
type: "binaryShortString",
|
||||
value: "\\r\\n"
|
||||
},
|
||||
{
|
||||
name: "Format",
|
||||
type: "option",
|
||||
value: ["Array of dictionaries", "Array of arrays"]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {JSON}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [cellDelims, rowDelims, format] = args;
|
||||
let json, header;
|
||||
|
||||
try {
|
||||
json = Utils.parseCSV(input, cellDelims.split(""), rowDelims.split(""));
|
||||
} catch (err) {
|
||||
throw new OperationError("Unable to parse CSV: " + err);
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case "Array of dictionaries":
|
||||
header = json[0];
|
||||
return json.slice(1).map(row => {
|
||||
const obj = {};
|
||||
header.forEach((h, i) => {
|
||||
obj[h] = row[i];
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
case "Array of arrays":
|
||||
default:
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default CSVToJSON;
|
58
src/core/operations/CitrixCTX1Decode.mjs
Normal file
58
src/core/operations/CitrixCTX1Decode.mjs
Normal file
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* @author bwhitn [brian.m.whitney@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import cptable from "../vendor/js-codepage/cptable.js";
|
||||
|
||||
/**
|
||||
* Citrix CTX1 Decode operation
|
||||
*/
|
||||
class CitrixCTX1Decode extends Operation {
|
||||
|
||||
/**
|
||||
* CitrixCTX1Decode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Citrix CTX1 Decode";
|
||||
this.module = "Encodings";
|
||||
this.description = "Decodes strings in a Citrix CTX1 password format to plaintext.";
|
||||
this.infoURL = "https://www.reddit.com/r/AskNetsec/comments/1s3r6y/citrix_ctx1_hash_decoding/";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
if (input.length % 4 !== 0) {
|
||||
throw new OperationError("Incorrect hash length");
|
||||
}
|
||||
const revinput = input.reverse();
|
||||
const result = [];
|
||||
let temp = 0;
|
||||
for (let i = 0; i < revinput.length; i += 2) {
|
||||
if (i + 2 >= revinput.length) {
|
||||
temp = 0;
|
||||
} else {
|
||||
temp = ((revinput[i + 2] - 0x41) & 0xf) ^ (((revinput[i + 3]- 0x41) << 4) & 0xf0);
|
||||
}
|
||||
temp = (((revinput[i] - 0x41) & 0xf) ^ (((revinput[i + 1] - 0x41) << 4) & 0xf0)) ^ 0xa5 ^ temp;
|
||||
result.push(temp);
|
||||
}
|
||||
// Decodes a utf-16le string
|
||||
return cptable.utils.decode(1200, result.reverse());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default CitrixCTX1Decode;
|
50
src/core/operations/CitrixCTX1Encode.mjs
Normal file
50
src/core/operations/CitrixCTX1Encode.mjs
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* @author bwhitn [brian.m.whitney@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import cptable from "../vendor/js-codepage/cptable.js";
|
||||
|
||||
/**
|
||||
* Citrix CTX1 Encode operation
|
||||
*/
|
||||
class CitrixCTX1Encode extends Operation {
|
||||
|
||||
/**
|
||||
* CitrixCTX1Encode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Citrix CTX1 Encode";
|
||||
this.module = "Encodings";
|
||||
this.description = "Encodes strings to Citrix CTX1 password format.";
|
||||
this.infoURL = "https://www.reddit.com/r/AskNetsec/comments/1s3r6y/citrix_ctx1_hash_decoding/";
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
const utf16pass = Array.from(cptable.utils.encode(1200, input));
|
||||
const result = [];
|
||||
let temp = 0;
|
||||
for (let i = 0; i < utf16pass.length; i++) {
|
||||
temp = utf16pass[i] ^ 0xa5 ^ temp;
|
||||
result.push(((temp >>> 4) & 0xf) + 0x41);
|
||||
result.push((temp & 0xf) + 0x41);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default CitrixCTX1Encode;
|
|
@ -38,44 +38,44 @@ class FromBase64 extends Operation {
|
|||
];
|
||||
this.patterns = [
|
||||
{
|
||||
match: "^(?:[A-Z\\d+/]{4})+(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?$",
|
||||
match: "^\\s*(?:[A-Z\\d+/]{4})+(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9+/=", false]
|
||||
args: ["A-Za-z0-9+/=", true]
|
||||
},
|
||||
{
|
||||
match: "^[A-Z\\d\\-_]{20,}$",
|
||||
match: "^\\s*[A-Z\\d\\-_]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9-_", false]
|
||||
args: ["A-Za-z0-9-_", true]
|
||||
},
|
||||
{
|
||||
match: "^(?:[A-Z\\d+\\-]{4}){5,}(?:[A-Z\\d+\\-]{2}==|[A-Z\\d+\\-]{3}=)?$",
|
||||
match: "^\\s*(?:[A-Z\\d+\\-]{4}){5,}(?:[A-Z\\d+\\-]{2}==|[A-Z\\d+\\-]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9+\\-=", false]
|
||||
args: ["A-Za-z0-9+\\-=", true]
|
||||
},
|
||||
{
|
||||
match: "^(?:[A-Z\\d./]{4}){5,}(?:[A-Z\\d./]{2}==|[A-Z\\d./]{3}=)?$",
|
||||
match: "^\\s*(?:[A-Z\\d./]{4}){5,}(?:[A-Z\\d./]{2}==|[A-Z\\d./]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["./0-9A-Za-z=", false]
|
||||
args: ["./0-9A-Za-z=", true]
|
||||
},
|
||||
{
|
||||
match: "^[A-Z\\d_.]{20,}$",
|
||||
match: "^\\s*[A-Z\\d_.]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9_.", false]
|
||||
args: ["A-Za-z0-9_.", true]
|
||||
},
|
||||
{
|
||||
match: "^(?:[A-Z\\d._]{4}){5,}(?:[A-Z\\d._]{2}--|[A-Z\\d._]{3}-)?$",
|
||||
match: "^\\s*(?:[A-Z\\d._]{4}){5,}(?:[A-Z\\d._]{2}--|[A-Z\\d._]{3}-)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9._-", false]
|
||||
args: ["A-Za-z0-9._-", true]
|
||||
},
|
||||
{
|
||||
match: "^(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?$",
|
||||
match: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["0-9a-zA-Z+/=", false]
|
||||
args: ["0-9a-zA-Z+/=", true]
|
||||
},
|
||||
{
|
||||
match: "^(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?$",
|
||||
match: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["0-9A-Za-z+/=", false]
|
||||
args: ["0-9A-Za-z+/=", true]
|
||||
},
|
||||
{
|
||||
match: "^[ !\"#$%&'()*+,\\-./\\d:;<=>?@A-Z[\\\\\\]^_]{20,}$",
|
||||
|
@ -83,24 +83,24 @@ class FromBase64 extends Operation {
|
|||
args: [" -_", false]
|
||||
},
|
||||
{
|
||||
match: "^[A-Z\\d+\\-]{20,}$",
|
||||
match: "^\\s*[A-Z\\d+\\-]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["+\\-0-9A-Za-z", false]
|
||||
args: ["+\\-0-9A-Za-z", true]
|
||||
},
|
||||
{
|
||||
match: "^[!\"#$%&'()*+,\\-0-689@A-NP-VX-Z[`a-fh-mp-r]{20,}$",
|
||||
match: "^\\s*[!\"#$%&'()*+,\\-0-689@A-NP-VX-Z[`a-fh-mp-r]{20,}\\s*$",
|
||||
flags: "",
|
||||
args: ["!-,-0-689@A-NP-VX-Z[`a-fh-mp-r", false]
|
||||
args: ["!-,-0-689@A-NP-VX-Z[`a-fh-mp-r", true]
|
||||
},
|
||||
{
|
||||
match: "^(?:[N-ZA-M\\d+/]{4}){5,}(?:[N-ZA-M\\d+/]{2}==|[N-ZA-M\\d+/]{3}=)?$",
|
||||
match: "^\\s*(?:[N-ZA-M\\d+/]{4}){5,}(?:[N-ZA-M\\d+/]{2}==|[N-ZA-M\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["N-ZA-Mn-za-m0-9+/=", false]
|
||||
args: ["N-ZA-Mn-za-m0-9+/=", true]
|
||||
},
|
||||
{
|
||||
match: "^[A-Z\\d./]{20,}$",
|
||||
match: "^\\s*[A-Z\\d./]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["./0-9A-Za-z", false]
|
||||
args: ["./0-9A-Za-z", true]
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import {BIN_DELIM_OPTIONS} from "../lib/Delim";
|
||||
import {fromBinary} from "../lib/Binary";
|
||||
|
||||
/**
|
||||
* From Binary operation
|
||||
|
@ -77,15 +78,7 @@ class FromBinary extends Operation {
|
|||
* @returns {byteArray}
|
||||
*/
|
||||
run(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;
|
||||
return fromBinary(input, args[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,7 +30,7 @@ class FromQuotedPrintable extends Operation {
|
|||
this.args = [];
|
||||
this.patterns = [
|
||||
{
|
||||
match: "^[\\x21-\\x3d\\x3f-\\x7e \\t]*(?:=[\\da-f]{2}|=\\r?\\n)(?:[\\x21-\\x3d\\x3f-\\x7e \\t]|=[\\da-f]{2}|=\\r?\\n)*$",
|
||||
match: "^[\\x21-\\x3d\\x3f-\\x7e \\t]{0,76}(?:=[\\da-f]{2}|=\\r?\\n)(?:[\\x21-\\x3d\\x3f-\\x7e \\t]|=[\\da-f]{2}|=\\r?\\n)*$",
|
||||
flags: "i",
|
||||
args: []
|
||||
},
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @author Phillip Nordwall [phillip.nordwall@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
@ -27,7 +28,12 @@ class JSONBeautify extends Operation {
|
|||
{
|
||||
"name": "Indent string",
|
||||
"type": "binaryShortString",
|
||||
"value": "\\t"
|
||||
"value": " "
|
||||
},
|
||||
{
|
||||
"name": "Sort Object Keys",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
@ -38,11 +44,35 @@ class JSONBeautify extends Operation {
|
|||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const indentStr = args[0];
|
||||
const [indentStr, sortBool] = args;
|
||||
|
||||
if (!input) return "";
|
||||
if (sortBool) {
|
||||
input = JSON.stringify(JSONBeautify._sort(JSON.parse(input)));
|
||||
}
|
||||
return vkbeautify.json(input, indentStr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort JSON representation of an object
|
||||
*
|
||||
* @author Phillip Nordwall [phillip.nordwall@gmail.com]
|
||||
* @private
|
||||
* @param {object} o
|
||||
* @returns {object}
|
||||
*/
|
||||
static _sort(o) {
|
||||
if (Array.isArray(o)) {
|
||||
return o.map(JSONBeautify._sort);
|
||||
} else if ("[object Object]" === Object.prototype.toString.call(o)) {
|
||||
return Object.keys(o).sort().reduce(function(a, k) {
|
||||
a[k] = JSONBeautify._sort(o[k]);
|
||||
return a;
|
||||
}, {});
|
||||
}
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
export default JSONBeautify;
|
||||
|
|
111
src/core/operations/JSONToCSV.mjs
Normal file
111
src/core/operations/JSONToCSV.mjs
Normal file
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* JSON to CSV operation
|
||||
*/
|
||||
class JSONToCSV extends Operation {
|
||||
|
||||
/**
|
||||
* JSONToCSV constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "JSON to CSV";
|
||||
this.module = "Default";
|
||||
this.description = "Converts JSON data to a CSV based on the definition in RFC 4180.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Comma-separated_values";
|
||||
this.inputType = "JSON";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Cell delimiter",
|
||||
type: "binaryShortString",
|
||||
value: ","
|
||||
},
|
||||
{
|
||||
name: "Row delimiter",
|
||||
type: "binaryShortString",
|
||||
value: "\\r\\n"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {JSON} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [cellDelim, rowDelim] = args;
|
||||
|
||||
// Record values so they don't have to be passed to other functions explicitly
|
||||
this.cellDelim = cellDelim;
|
||||
this.rowDelim = rowDelim;
|
||||
const self = this;
|
||||
|
||||
try {
|
||||
// If the JSON is an array of arrays, this is easy
|
||||
if (input[0] instanceof Array) {
|
||||
return input
|
||||
.map(row => row
|
||||
.map(self.escapeCellContents.bind(self))
|
||||
.join(cellDelim)
|
||||
)
|
||||
.join(rowDelim) +
|
||||
rowDelim;
|
||||
}
|
||||
|
||||
// If it's an array of dictionaries...
|
||||
const header = Object.keys(input[0]);
|
||||
return header
|
||||
.map(self.escapeCellContents.bind(self))
|
||||
.join(cellDelim) +
|
||||
rowDelim +
|
||||
input
|
||||
.map(row => header
|
||||
.map(h => row[h])
|
||||
.map(self.escapeCellContents.bind(self))
|
||||
.join(cellDelim)
|
||||
)
|
||||
.join(rowDelim) +
|
||||
rowDelim;
|
||||
} catch (err) {
|
||||
throw new OperationError("Unable to parse JSON to CSV: " + err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Correctly escapes a cell's contents based on the cell and row delimiters.
|
||||
*
|
||||
* @param {string} data
|
||||
* @returns {string}
|
||||
*/
|
||||
escapeCellContents(data) {
|
||||
// Double quotes should be doubled up
|
||||
data = data.replace(/"/g, '""');
|
||||
|
||||
// If the cell contains a cell or row delimiter or a double quote, it mut be enclosed in double quotes
|
||||
if (
|
||||
data.indexOf(this.cellDelim) >= 0 ||
|
||||
data.indexOf(this.rowDelim) >= 0 ||
|
||||
data.indexOf("\n") >= 0 ||
|
||||
data.indexOf("\r") >= 0 ||
|
||||
data.indexOf('"') >= 0
|
||||
) {
|
||||
data = `"${data}"`;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default JSONToCSV;
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import { bitOp, or } from "../lib/BitwiseOp";
|
||||
import { bitOp, or, BITWISE_OP_DELIMS } from "../lib/BitwiseOp";
|
||||
|
||||
/**
|
||||
* OR operation
|
||||
|
@ -30,7 +30,7 @@ class OR extends Operation {
|
|||
"name": "Key",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "Decimal", "Base64", "UTF8", "Latin1"]
|
||||
"toggleValues": BITWISE_OP_DELIMS
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import { bitOp, sub } from "../lib/BitwiseOp";
|
||||
import { bitOp, sub, BITWISE_OP_DELIMS } from "../lib/BitwiseOp";
|
||||
|
||||
/**
|
||||
* SUB operation
|
||||
|
@ -30,7 +30,7 @@ class SUB extends Operation {
|
|||
"name": "Key",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "Decimal", "Base64", "UTF8", "Latin1"]
|
||||
"toggleValues": BITWISE_OP_DELIMS
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class Substitute extends Operation {
|
|||
|
||||
this.name = "Substitute";
|
||||
this.module = "Default";
|
||||
this.description = "A substitution cipher allowing you to specify bytes to replace with other byte values. This can be used to create Caesar ciphers but is more powerful as any byte value can be substituted, not just letters, and the substitution values need not be in order.<br><br>Enter the bytes you want to replace in the Plaintext field and the bytes to replace them with in the Ciphertext field.<br><br>Non-printable bytes can be specified using string escape notation. For example, a line feed character can be written as either <code>\n</code> or <code>\x0a</code>.<br><br>Byte ranges can be specified using a hyphen. For example, the sequence <code>0123456789</code> can be written as <code>0-9</code>.";
|
||||
this.description = "A substitution cipher allowing you to specify bytes to replace with other byte values. This can be used to create Caesar ciphers but is more powerful as any byte value can be substituted, not just letters, and the substitution values need not be in order.<br><br>Enter the bytes you want to replace in the Plaintext field and the bytes to replace them with in the Ciphertext field.<br><br>Non-printable bytes can be specified using string escape notation. For example, a line feed character can be written as either <code>\\n</code> or <code>\\x0a</code>.<br><br>Byte ranges can be specified using a hyphen. For example, the sequence <code>0123456789</code> can be written as <code>0-9</code>.<br><br>Note that blackslash characters are used to escape special characters, so will need to be escaped themselves if you want to use them on their own (e.g.<code>\\\\</code>).";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Substitution_cipher";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import {BIN_DELIM_OPTIONS} from "../lib/Delim";
|
||||
import {toBinary} from "../lib/Binary";
|
||||
|
||||
/**
|
||||
* To Binary operation
|
||||
|
@ -40,19 +41,7 @@ class ToBinary extends Operation {
|
|||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const delim = Utils.charRep(args[0] || "Space"),
|
||||
padding = 8;
|
||||
let output = "";
|
||||
|
||||
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;
|
||||
}
|
||||
return toBinary(input, args[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,7 +33,7 @@ class ToTable extends Operation {
|
|||
{
|
||||
"name": "Row delimiters",
|
||||
"type": "binaryShortString",
|
||||
"value": "\\n\\r"
|
||||
"value": "\\r\\n"
|
||||
},
|
||||
{
|
||||
"name": "Make first row header",
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import { bitOp, xor } from "../lib/BitwiseOp";
|
||||
import { bitOp, xor, BITWISE_OP_DELIMS } from "../lib/BitwiseOp";
|
||||
|
||||
/**
|
||||
* XOR operation
|
||||
|
@ -30,7 +30,7 @@ class XOR extends Operation {
|
|||
"name": "Key",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "Decimal", "Base64", "UTF8", "Latin1"]
|
||||
"toggleValues": BITWISE_OP_DELIMS
|
||||
},
|
||||
{
|
||||
"name": "Scheme",
|
||||
|
|
|
@ -39,6 +39,7 @@ import "./tests/operations/Comment";
|
|||
import "./tests/operations/Compress";
|
||||
import "./tests/operations/ConditionalJump";
|
||||
import "./tests/operations/Crypt";
|
||||
import "./tests/operations/CSV";
|
||||
import "./tests/operations/DateTime";
|
||||
import "./tests/operations/ExtractEmailAddresses";
|
||||
import "./tests/operations/Fork";
|
||||
|
@ -49,6 +50,8 @@ import "./tests/operations/HaversineDistance";
|
|||
import "./tests/operations/Hexdump";
|
||||
import "./tests/operations/Image";
|
||||
import "./tests/operations/Jump";
|
||||
import "./tests/operations/JSONBeautify";
|
||||
import "./tests/operations/JSONMinify";
|
||||
import "./tests/operations/JWTDecode";
|
||||
import "./tests/operations/JWTSign";
|
||||
import "./tests/operations/JWTVerify";
|
||||
|
@ -129,12 +132,12 @@ function handleTestResult(testResult) {
|
|||
|
||||
|
||||
/**
|
||||
* Fail if the process takes longer than 10 seconds.
|
||||
* Fail if the process takes longer than 60 seconds.
|
||||
*/
|
||||
setTimeout(function() {
|
||||
console.log("Tests took longer than 10 seconds to run, returning.");
|
||||
console.log("Tests took longer than 60 seconds to run, returning.");
|
||||
process.exit(1);
|
||||
}, 10 * 1000);
|
||||
}, 60 * 1000);
|
||||
|
||||
const start = new Date();
|
||||
|
||||
|
|
|
@ -658,7 +658,7 @@ WWFkYSBZYWRh\r
|
|||
assert.strictEqual(
|
||||
chef.JSONBeautify("{\"key\" : \"value\"}").toString(),
|
||||
`{
|
||||
\\t"key": "value"
|
||||
"key": "value"
|
||||
}`);
|
||||
}),
|
||||
|
||||
|
|
179
test/tests/operations/CSV.mjs
Normal file
179
test/tests/operations/CSV.mjs
Normal file
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
* CSV tests.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister";
|
||||
|
||||
const EXAMPLE_CSV = `A,B,C,D,E,F\r
|
||||
1,2,3,4,5,6\r
|
||||
",",;,',"""",,\r
|
||||
"""hello""","a""1","multi\r
|
||||
line",,,end\r
|
||||
`;
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "CSV to JSON: Array of dictionaries",
|
||||
input: EXAMPLE_CSV,
|
||||
expectedOutput: JSON.stringify([
|
||||
{
|
||||
"A": "1",
|
||||
"B": "2",
|
||||
"C": "3",
|
||||
"D": "4",
|
||||
"E": "5",
|
||||
"F": "6"
|
||||
},
|
||||
{
|
||||
"A": ",",
|
||||
"B": ";",
|
||||
"C": "'",
|
||||
"D": "\"",
|
||||
"E": "",
|
||||
"F": ""
|
||||
},
|
||||
{
|
||||
"A": "\"hello\"",
|
||||
"B": "a\"1",
|
||||
"C": "multi\r\nline",
|
||||
"D": "",
|
||||
"E": "",
|
||||
"F": "end"
|
||||
}
|
||||
], null, 4),
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "CSV to JSON",
|
||||
args: [",", "\r\n", "Array of dictionaries"],
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "CSV to JSON: Array of arrays",
|
||||
input: EXAMPLE_CSV,
|
||||
expectedOutput: JSON.stringify([
|
||||
[
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F"
|
||||
],
|
||||
[
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6"
|
||||
],
|
||||
[
|
||||
",",
|
||||
";",
|
||||
"'",
|
||||
"\"",
|
||||
"",
|
||||
""
|
||||
],
|
||||
[
|
||||
"\"hello\"",
|
||||
"a\"1",
|
||||
"multi\r\nline",
|
||||
"",
|
||||
"",
|
||||
"end"
|
||||
]
|
||||
], null, 4),
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "CSV to JSON",
|
||||
args: [",", "\r\n", "Array of arrays"],
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON to CSV: Array of dictionaries",
|
||||
input: JSON.stringify([
|
||||
{
|
||||
"A": "1",
|
||||
"B": "2",
|
||||
"C": "3",
|
||||
"D": "4",
|
||||
"E": "5",
|
||||
"F": "6"
|
||||
},
|
||||
{
|
||||
"A": ",",
|
||||
"B": ";",
|
||||
"C": "'",
|
||||
"D": "\"",
|
||||
"E": "",
|
||||
"F": ""
|
||||
},
|
||||
{
|
||||
"A": "\"hello\"",
|
||||
"B": "a\"1",
|
||||
"C": "multi\r\nline",
|
||||
"D": "",
|
||||
"E": "",
|
||||
"F": "end"
|
||||
}
|
||||
]),
|
||||
expectedOutput: EXAMPLE_CSV,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON to CSV",
|
||||
args: [",", "\r\n"],
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON to CSV: Array of arrays",
|
||||
input: JSON.stringify([
|
||||
[
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F"
|
||||
],
|
||||
[
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6"
|
||||
],
|
||||
[
|
||||
",",
|
||||
";",
|
||||
"'",
|
||||
"\"",
|
||||
"",
|
||||
""
|
||||
],
|
||||
[
|
||||
"\"hello\"",
|
||||
"a\"1",
|
||||
"multi\r\nline",
|
||||
"",
|
||||
"",
|
||||
"end"
|
||||
]
|
||||
]),
|
||||
expectedOutput: EXAMPLE_CSV,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON to CSV",
|
||||
args: [",", "\r\n"],
|
||||
}
|
||||
],
|
||||
},
|
||||
]);
|
|
@ -220,6 +220,39 @@ TestRegister.addTests([
|
|||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Citrix CTX1 Encode",
|
||||
input: "Password1",
|
||||
expectedOutput: "PFFAJEDBOHECJEDBODEGIMCJPOFLJKDPKLAO",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Citrix CTX1 Encode",
|
||||
"args": []
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Citrix CTX1 Decode: normal",
|
||||
input: "PFFAJEDBOHECJEDBODEGIMCJPOFLJKDPKLAO",
|
||||
expectedOutput: "Password1",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Citrix CTX1 Decode",
|
||||
"args": []
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Citrix CTX1 Decode: invalid length",
|
||||
input: "PFFAJEDBOHECJEDBODEGIMCJPOFLJKDPKLA",
|
||||
expectedOutput: "Incorrect hash length",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Citrix CTX1 Decode",
|
||||
"args": []
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Vigenère Encode: no input",
|
||||
input: "",
|
||||
|
|
122
test/tests/operations/JSONBeautify.mjs
Normal file
122
test/tests/operations/JSONBeautify.mjs
Normal file
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* JSONBeautify tests.
|
||||
*
|
||||
* @author Phillip Nordwall [Phillip.Nordwall@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "JSON Beautify: space, ''",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Beautify",
|
||||
args: [" ", false],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Beautify: space, number",
|
||||
input: "42",
|
||||
expectedOutput: "42",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Beautify",
|
||||
args: [" ", false],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Beautify: space, string",
|
||||
input: "\"string\"",
|
||||
expectedOutput: "\"string\"",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Beautify",
|
||||
args: [" ", false],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Beautify: space, boolean",
|
||||
input: "false",
|
||||
expectedOutput: "false",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Beautify",
|
||||
args: [" ", false],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Beautify: space, emptyList",
|
||||
input: "[]",
|
||||
expectedOutput: "[]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Beautify",
|
||||
args: [" ", false],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Beautify: space, list",
|
||||
input: "[2,1]",
|
||||
expectedOutput: "[\n 2,\n 1\n]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Beautify",
|
||||
args: [" ", false],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Beautify: tab, list",
|
||||
input: "[2,1]",
|
||||
expectedOutput: "[\n\t2,\n\t1\n]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Beautify",
|
||||
args: ["\t", false],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Beautify: space, object",
|
||||
input: "{\"second\":2,\"first\":3}",
|
||||
expectedOutput: "{\n \"second\": 2,\n \"first\": 3\n}",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Beautify",
|
||||
args: [" ", false],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Beautify: tab, nested",
|
||||
input: "[2,{\"second\":2,\"first\":3,\"beginning\":{\"j\":\"3\",\"i\":[2,3,false]}},1,2,3]",
|
||||
expectedOutput: "[\n\t2,\n\t{\n\t\t\"second\": 2,\n\t\t\"first\": 3,\n\t\t\"beginning\": {\n\t\t\t\"j\": \"3\",\n\t\t\t\"i\": [\n\t\t\t\t2,\n\t\t\t\t3,\n\t\t\t\tfalse\n\t\t\t]\n\t\t}\n\t},\n\t1,\n\t2,\n\t3\n]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Beautify",
|
||||
args: ["\t", false],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Beautify: tab, nested, sorted",
|
||||
input: "[2,{\"second\":2,\"first\":3,\"beginning\":{\"j\":\"3\",\"i\":[2,3,false]}},1,2,3]",
|
||||
expectedOutput: "[\n\t2,\n\t{\n\t\t\"beginning\": {\n\t\t\t\"i\": [\n\t\t\t\t2,\n\t\t\t\t3,\n\t\t\t\tfalse\n\t\t\t],\n\t\t\t\"j\": \"3\"\n\t\t},\n\t\t\"first\": 3,\n\t\t\"second\": 2\n\t},\n\t1,\n\t2,\n\t3\n]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Beautify",
|
||||
args: ["\t", true],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
111
test/tests/operations/JSONMinify.mjs
Normal file
111
test/tests/operations/JSONMinify.mjs
Normal file
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* JSONMinify tests.
|
||||
*
|
||||
* @author Phillip Nordwall [Phillip.Nordwall@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "JSON Minify: ''",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Minify",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Minify: number",
|
||||
input: "42",
|
||||
expectedOutput: "42",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Minify",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Minify: number",
|
||||
input: "4.2",
|
||||
expectedOutput: "4.2",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Minify",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Minify: string",
|
||||
input: "\"string\"",
|
||||
expectedOutput: "\"string\"",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Minify",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Minify: boolean",
|
||||
input: "false",
|
||||
expectedOutput: "false",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Minify",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Minify: emptyList",
|
||||
input: "[\n \n \t]",
|
||||
expectedOutput: "[]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Minify",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Minify: list",
|
||||
input: "[2,\n \t1]",
|
||||
expectedOutput: "[2,1]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Minify",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Minify: object",
|
||||
input: "{\n \"second\": 2,\n \"first\": 3\n}",
|
||||
expectedOutput: "{\"second\":2,\"first\":3}",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Minify",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON Minify: tab, nested",
|
||||
input: "[\n\t2,\n\t{\n\t\t\"second\": 2,\n\t\t\"first\": 3,\n\t\t\"beginning\": {\n\t\t\t\"j\": \"3\",\n\t\t\t\"i\": [\n\t\t\t\t2,\n\t\t\t\t3,\n\t\t\t\tfalse\n\t\t\t]\n\t\t}\n\t},\n\t1,\n\t2,\n\t3\n]",
|
||||
expectedOutput: "[2,{\"second\":2,\"first\":3,\"beginning\":{\"j\":\"3\",\"i\":[2,3,false]}},1,2,3]",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON Minify",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
|
@ -1,5 +1,6 @@
|
|||
const webpack = require("webpack");
|
||||
const ExtractTextPlugin = require("extract-text-webpack-plugin");
|
||||
const path = require("path");
|
||||
|
||||
/**
|
||||
* Webpack configuration details for use with Grunt.
|
||||
|
@ -58,13 +59,13 @@ module.exports = {
|
|||
{
|
||||
test: /\.m?js$/,
|
||||
exclude: /node_modules\/(?!jsesc|crypto-api)/,
|
||||
options: {
|
||||
configFile: path.resolve(__dirname, "babel.config.js"),
|
||||
cacheDirectory: true,
|
||||
compact: false
|
||||
},
|
||||
type: "javascript/auto",
|
||||
loader: "babel-loader?compact=false",
|
||||
query: {
|
||||
presets: [
|
||||
require.resolve("@babel/preset-env"),
|
||||
]
|
||||
}
|
||||
loader: "babel-loader"
|
||||
},
|
||||
{
|
||||
test: /forge.min.js$/,
|
||||
|
|
Loading…
Reference in a new issue