2
0
mirror of https://github.com/gchq/CyberChef synced 2025-12-05 23:53:27 +00:00

Compare commits

...

19 Commits

Author SHA1 Message Date
Björn Heinrichs
ade572c0db Merge 18d583f46a into 1b8c229df2 2025-02-18 04:32:52 +00:00
a3957273
1b8c229df2 Merge pull request #1976 from evenstensberg/feat/cspell
chore(root): add cspell
2025-02-16 18:48:31 +00:00
a3957273
aa7bbc1a62 Merge pull request #1977 from depperm/CC-1889
CC-1889 add _ option
2025-02-16 18:37:00 +00:00
David Marshall
6ccbc892c7 CC-1889 2025-02-16 09:03:46 -05:00
Even Stensberg
7b3980a5e2 chore(root): add cspell 2025-02-16 13:53:36 +01:00
a3957273
7906f9d560 Merge pull request #630 from MShwed/feature/mime-rfc2047
Feature: MIME RFC2047 Decoding
2025-02-16 12:11:02 +00:00
mshwed
2ae923b73e Updated category and fixed imports 2025-02-15 23:03:05 -05:00
mshwed
15bbed093c fixed formatting 2025-02-15 22:23:49 -05:00
mshwed
23faeadea2 Fixed linting issues 2025-02-15 22:20:42 -05:00
mshwed
50c0d4bcd6 Added test case for ISO and ASCII 2025-02-15 22:20:42 -05:00
mshwed
7f97afd3e0 Added parsing of headers. 2025-02-15 22:20:41 -05:00
mshwed
c3994aa8e3 Added support for converting hex characters 2025-02-15 22:16:58 -05:00
mshwed
8166f981ae in progress 2025-02-15 22:16:58 -05:00
a3957273
1cfbc2babb Merge pull request #1105 from linuxgemini/linuxgemini-patch-modhex
Introduce Yubico's Modhex for Conversion
2025-02-16 02:18:30 +00:00
a3957273
eb912547ac Merge branch 'master' into linuxgemini-patch-modhex 2025-02-16 01:55:11 +00:00
İlteriş Yağıztegin Eroğlu
edd22372d8 feat(Modhex): Introduce basic Modhex conversion
Signed-off-by: İlteriş Yağıztegin Eroğlu <ilteris@asenkron.com.tr>
2024-06-17 23:58:58 +00:00
a3957273
18d583f46a Merge branch 'master' into feature_md_link_blanks 2024-03-31 03:31:00 +01:00
Björn Heinrichs
6402bb6003 Fix linting errors 2019-10-04 15:16:08 +02:00
Björn Heinrichs
e5a903807f Added support to open links in new Tab (Markdown)
Rendered markdown will add target="_blank" if the given option is set.
Fixes #636.
2019-10-04 15:03:12 +02:00
14 changed files with 1924 additions and 8 deletions

17
.cspell.json Normal file
View File

@@ -0,0 +1,17 @@
{
"version": "0.2",
"language": "en,en-gb",
"words": [],
"dictionaries": [
"npm",
"softwareTerms",
"node",
"html",
"css",
"bash",
"en-gb",
"misc"
],
"ignorePaths": ["package.json", "package-lock.json", "node_modules"]
}

1136
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -61,6 +61,7 @@
"compression-webpack-plugin": "^11.1.0",
"copy-webpack-plugin": "^12.0.2",
"core-js": "^3.37.1",
"cspell": "^8.17.3",
"css-loader": "7.1.2",
"eslint": "^9.4.0",
"eslint-plugin-jsdoc": "^48.2.9",
@@ -193,6 +194,7 @@
"testui": "npx grunt testui",
"testuidev": "npx nightwatch --env=dev",
"lint": "npx grunt lint",
"lint:grammar": "cspell ./src",
"postinstall": "npx grunt exec:fixCryptoApiImports && npx grunt exec:fixSnackbarMarkup && npx grunt exec:fixJimpModule",
"newop": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newOperation.mjs",
"minor": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newMinorVersion.mjs",

View File

@@ -74,7 +74,10 @@
"CBOR Decode",
"Caret/M-decode",
"Rison Encode",
"Rison Decode"
"Rison Decode",
"To Modhex",
"From Modhex",
"MIME Decoding"
]
},
{

View File

@@ -62,3 +62,9 @@ export const URL_REGEX = new RegExp(protocol + hostname + "(?:" + port + ")?(?:"
* Domain name regular expression
*/
export const DOMAIN_REGEX = /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b/ig;
/**
* DMARC Domain name regular expression
*/
export const DMARC_DOMAIN_REGEX = /\b((?=[a-z0-9_-]{1,63}\.)(xn--)?[a-z0-9_]+(-[a-z0-9_]+)*\.)+[a-z]{2,63}\b/ig;

165
src/core/lib/Modhex.mjs Normal file
View File

@@ -0,0 +1,165 @@
/**
* @author linuxgemini [ilteris@asenkron.com.tr]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
import { fromHex, toHex } from "./Hex.mjs";
/**
* Modhex alphabet.
*/
const MODHEX_ALPHABET = "cbdefghijklnrtuv";
/**
* Modhex alphabet map.
*/
const MODHEX_ALPHABET_MAP = MODHEX_ALPHABET.split("");
/**
* Hex alphabet to substitute Modhex.
*/
const HEX_ALPHABET = "0123456789abcdef";
/**
* Hex alphabet map to substitute Modhex.
*/
const HEX_ALPHABET_MAP = HEX_ALPHABET.split("");
/**
* Convert a byte array into a modhex string.
*
* @param {byteArray|Uint8Array|ArrayBuffer} data
* @param {string} [delim=" "]
* @param {number} [padding=2]
* @returns {string}
*
* @example
* // returns "cl bf bu"
* toModhex([10,20,30]);
*
* // returns "cl:bf:bu"
* toModhex([10,20,30], ":");
*/
export function toModhex(data, delim=" ", padding=2, extraDelim="", lineSize=0) {
if (!data) return "";
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
const regularHexString = toHex(data, "", padding, "", 0);
let modhexString = "";
for (const letter of regularHexString.split("")) {
modhexString += MODHEX_ALPHABET_MAP[HEX_ALPHABET_MAP.indexOf(letter)];
}
let output = "";
const groupingRegexp = new RegExp(`.{1,${padding}}`, "g");
const groupedModhex = modhexString.match(groupingRegexp);
for (let i = 0; i < groupedModhex.length; i++) {
const group = groupedModhex[i];
output += group + delim;
if (extraDelim) {
output += extraDelim;
}
// Add LF after each lineSize amount of bytes but not at the end
if ((i !== groupedModhex.length - 1) && ((i + 1) % lineSize === 0)) {
output += "\n";
}
}
// Remove the extraDelim at the end (if there is one)
// and remove the delim at the end, but if it's prepended there's nothing to remove
const rTruncLen = extraDelim.length + delim.length;
if (rTruncLen) {
// If rTruncLen === 0 then output.slice(0,0) will be returned, which is nothing
return output.slice(0, -rTruncLen);
} else {
return output;
}
}
/**
* Convert a byte array into a modhex string as efficiently as possible with no options.
*
* @param {byteArray|Uint8Array|ArrayBuffer} data
* @returns {string}
*
* @example
* // returns "clbfbu"
* toModhexFast([10,20,30]);
*/
export function toModhexFast(data) {
if (!data) return "";
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
const output = [];
for (let i = 0; i < data.length; i++) {
output.push(MODHEX_ALPHABET_MAP[(data[i] >> 4) & 0xf]);
output.push(MODHEX_ALPHABET_MAP[data[i] & 0xf]);
}
return output.join("");
}
/**
* Convert a modhex string into a byte array.
*
* @param {string} data
* @param {string} [delim]
* @param {number} [byteLen=2]
* @returns {byteArray}
*
* @example
* // returns [10,20,30]
* fromModhex("cl bf bu");
*
* // returns [10,20,30]
* fromModhex("cl:bf:bu", "Colon");
*/
export function fromModhex(data, delim="Auto", byteLen=2) {
if (byteLen < 1 || Math.round(byteLen) !== byteLen)
throw new OperationError("Byte length must be a positive integer");
// The `.replace(/\s/g, "")` an interesting workaround: Hex "multiline" tests aren't actually
// multiline. Tests for Modhex fixes that, thus exposing the issue.
data = data.toLowerCase().replace(/\s/g, "");
if (delim !== "None") {
const delimRegex = delim === "Auto" ? /[^cbdefghijklnrtuv]/gi : Utils.regexRep(delim);
data = data.split(delimRegex);
} else {
data = [data];
}
let regularHexString = "";
for (let i = 0; i < data.length; i++) {
for (const letter of data[i].split("")) {
regularHexString += HEX_ALPHABET_MAP[MODHEX_ALPHABET_MAP.indexOf(letter)];
}
}
const output = fromHex(regularHexString, "None", byteLen);
return output;
}
/**
* To Modhex delimiters.
*/
export const TO_MODHEX_DELIM_OPTIONS = ["Space", "Percent", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "None"];
/**
* From Modhex delimiters.
*/
export const FROM_MODHEX_DELIM_OPTIONS = ["Auto"].concat(TO_MODHEX_DELIM_OPTIONS);

View File

@@ -5,7 +5,7 @@
*/
import Operation from "../Operation.mjs";
import { search, DOMAIN_REGEX } from "../lib/Extract.mjs";
import { search, DOMAIN_REGEX, DMARC_DOMAIN_REGEX } from "../lib/Extract.mjs";
import { caseInsensitiveSort } from "../lib/Sort.mjs";
/**
@@ -39,6 +39,11 @@ class ExtractDomains extends Operation {
name: "Unique",
type: "boolean",
value: false
},
{
name: "Underscore (DMARC, DKIM, etc)",
type: "boolean",
value: false
}
];
}
@@ -49,11 +54,11 @@ class ExtractDomains extends Operation {
* @returns {string}
*/
run(input, args) {
const [displayTotal, sort, unique] = args;
const [displayTotal, sort, unique, dmarc] = args;
const results = search(
input,
DOMAIN_REGEX,
dmarc ? DMARC_DOMAIN_REGEX : DOMAIN_REGEX,
null,
sort ? caseInsensitiveSort : null,
unique

View File

@@ -0,0 +1,84 @@
/**
* @author linuxgemini [ilteris@asenkron.com.tr]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import { FROM_MODHEX_DELIM_OPTIONS, fromModhex } from "../lib/Modhex.mjs";
/**
* From Modhex operation
*/
class FromModhex extends Operation {
/**
* FromModhex constructor
*/
constructor() {
super();
this.name = "From Modhex";
this.module = "Default";
this.description = "Converts a modhex byte string back into its raw value.";
this.infoURL = "https://en.wikipedia.org/wiki/YubiKey#ModHex";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
name: "Delimiter",
type: "option",
value: FROM_MODHEX_DELIM_OPTIONS
}
];
this.checks = [
{
pattern: "^(?:[cbdefghijklnrtuv]{2})+$",
flags: "i",
args: ["None"]
},
{
pattern: "^[cbdefghijklnrtuv]{2}(?: [cbdefghijklnrtuv]{2})*$",
flags: "i",
args: ["Space"]
},
{
pattern: "^[cbdefghijklnrtuv]{2}(?:,[cbdefghijklnrtuv]{2})*$",
flags: "i",
args: ["Comma"]
},
{
pattern: "^[cbdefghijklnrtuv]{2}(?:;[cbdefghijklnrtuv]{2})*$",
flags: "i",
args: ["Semi-colon"]
},
{
pattern: "^[cbdefghijklnrtuv]{2}(?::[cbdefghijklnrtuv]{2})*$",
flags: "i",
args: ["Colon"]
},
{
pattern: "^[cbdefghijklnrtuv]{2}(?:\\n[cbdefghijklnrtuv]{2})*$",
flags: "i",
args: ["Line feed"]
},
{
pattern: "^[cbdefghijklnrtuv]{2}(?:\\r\\n[cbdefghijklnrtuv]{2})*$",
flags: "i",
args: ["CRLF"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const delim = args[0] || "Auto";
return fromModhex(input, delim, 2);
}
}
export default FromModhex;

View File

@@ -0,0 +1,171 @@
/**
* @author mshwed [m@ttshwed.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
import { fromHex } from "../lib/Hex.mjs";
import { fromBase64 } from "../lib/Base64.mjs";
import cptable from "codepage";
/**
* MIME Decoding operation
*/
class MIMEDecoding extends Operation {
/**
* MIMEDecoding constructor
*/
constructor() {
super();
this.name = "MIME Decoding";
this.module = "Default";
this.description = "Enables the decoding of MIME message header extensions for non-ASCII text";
this.infoURL = "https://tools.ietf.org/html/rfc2047";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const mimeEncodedText = Utils.byteArrayToUtf8(input);
const encodedHeaders = mimeEncodedText.replace(/\r\n/g, "\n");
const decodedHeader = this.decodeHeaders(encodedHeaders);
return decodedHeader;
}
/**
* Decode MIME header strings
*
* @param headerString
*/
decodeHeaders(headerString) {
// No encoded words detected
let i = headerString.indexOf("=?");
if (i === -1) return headerString;
let decodedHeaders = headerString.slice(0, i);
let header = headerString.slice(i);
let isBetweenWords = false;
let start, cur, charset, encoding, j, end, text;
while (header.length > -1) {
start = header.indexOf("=?");
if (start === -1) break;
cur = start + "=?".length;
i = header.slice(cur).indexOf("?");
if (i === -1) break;
charset = header.slice(cur, cur + i);
cur += i + "?".length;
if (header.length < cur + "Q??=".length) break;
encoding = header[cur];
cur += 1;
if (header[cur] !== "?") break;
cur += 1;
j = header.slice(cur).indexOf("?=");
if (j === -1) break;
text = header.slice(cur, cur + j);
end = cur + j + "?=".length;
if (encoding.toLowerCase() === "b") {
text = fromBase64(text);
} else if (encoding.toLowerCase() === "q") {
text = this.parseQEncodedWord(text);
} else {
isBetweenWords = false;
decodedHeaders += header.slice(0, start + 2);
header = header.slice(start + 2);
}
if (start > 0 && (!isBetweenWords || header.slice(0, start).search(/\S/g) > -1)) {
decodedHeaders += header.slice(0, start);
}
decodedHeaders += this.convertFromCharset(charset, text);
header = header.slice(end);
isBetweenWords = true;
}
if (header.length > 0) {
decodedHeaders += header;
}
return decodedHeaders;
}
/**
* Converts decoded text for supported charsets.
* Supports UTF-8, US-ASCII, ISO-8859-*
*
* @param encodedWord
*/
convertFromCharset(charset, encodedText) {
charset = charset.toLowerCase();
const parsedCharset = charset.split("-");
if (parsedCharset.length === 2 && parsedCharset[0] === "utf" && charset === "utf-8") {
return cptable.utils.decode(65001, encodedText);
} else if (parsedCharset.length === 2 && charset === "us-ascii") {
return cptable.utils.decode(20127, encodedText);
} else if (parsedCharset.length === 3 && parsedCharset[0] === "iso" && parsedCharset[1] === "8859") {
const isoCharset = parseInt(parsedCharset[2], 10);
if (isoCharset >= 1 && isoCharset <= 16) {
return cptable.utils.decode(28590 + isoCharset, encodedText);
}
}
throw new OperationError("Unhandled Charset");
}
/**
* Parses a Q encoded word
*
* @param encodedWord
*/
parseQEncodedWord(encodedWord) {
let decodedWord = "";
for (let i = 0; i < encodedWord.length; i++) {
if (encodedWord[i] === "_") {
decodedWord += " ";
// Parse hex encoding
} else if (encodedWord[i] === "=") {
if ((i + 2) >= encodedWord.length) throw new OperationError("Incorrectly Encoded Word");
const decodedHex = Utils.byteArrayToChars(fromHex(encodedWord.substring(i + 1, i + 3)));
decodedWord += decodedHex;
i += 2;
} else if (
(encodedWord[i].charCodeAt(0) >= " ".charCodeAt(0) && encodedWord[i].charCodeAt(0) <= "~".charCodeAt(0)) ||
encodedWord[i] === "\n" ||
encodedWord[i] === "\r" ||
encodedWord[i] === "\t") {
decodedWord += encodedWord[i];
} else {
throw new OperationError("Incorrectly Encoded Word");
}
}
return decodedWord;
}
}
export default MIMEDecoding;

View File

@@ -35,6 +35,11 @@ class RenderMarkdown extends Operation {
name: "Enable syntax highlighting",
type: "boolean",
value: true
},
{
name: "Open links in new tab.",
type: "boolean",
value: false
}
];
}
@@ -45,7 +50,7 @@ class RenderMarkdown extends Operation {
* @returns {html}
*/
run(input, args) {
const [convertLinks, enableHighlighting] = args,
const [convertLinks, enableHighlighting, openLinksBlank] = args,
md = new MarkdownIt({
linkify: convertLinks,
html: false, // Explicitly disable HTML rendering
@@ -58,12 +63,38 @@ class RenderMarkdown extends Operation {
return "";
}
}),
rendered = md.render(input);
});
if (openLinksBlank) {
this.makeLinksOpenInNewTab(md);
}
const rendered = md.render(input);
return `<div style="font-family: var(--primary-font-family)">${rendered}</div>`;
}
/**
* Adds target="_blank" to links.
* @param {MarkdownIt} md
*/
makeLinksOpenInNewTab(md) {
// Adapted from: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer
// Remember old renderer, if overridden, or proxy to default renderer
const defaultRender = md.renderer.rules.link_open || function(tokens, idx, options, env, self) {
return self.renderToken(tokens, idx, options);
};
// eslint-disable-next-line camelcase
md.renderer.rules.link_open = function (tokens, idx, options, env, self) {
const token = tokens[idx];
if (token.attrIndex("target") >= 0) {
// Target attribute already set, do not replace.
return;
}
token.attrPush(["target", "_blank"]); // add new attribute
// pass token to default renderer.
return defaultRender(tokens, idx, options, env, self);
};
}
}
export default RenderMarkdown;

View File

@@ -0,0 +1,55 @@
/**
* @author linuxgemini [ilteris@asenkron.com.tr]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import { TO_MODHEX_DELIM_OPTIONS, toModhex } from "../lib/Modhex.mjs";
import Utils from "../Utils.mjs";
/**
* To Modhex operation
*/
class ToModhex extends Operation {
/**
* ToModhex constructor
*/
constructor() {
super();
this.name = "To Modhex";
this.module = "Default";
this.description = "Converts the input string to modhex bytes separated by the specified delimiter.";
this.infoURL = "https://en.wikipedia.org/wiki/YubiKey#ModHex";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
name: "Delimiter",
type: "option",
value: TO_MODHEX_DELIM_OPTIONS
},
{
name: "Bytes per line",
type: "number",
value: 0
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const delim = Utils.charRep(args[0]);
const lineSize = args[1];
return toModhex(new Uint8Array(input), delim, 2, "", lineSize);
}
}
export default ToModhex;

View File

@@ -104,6 +104,8 @@ import "./tests/LZNT1Decompress.mjs";
import "./tests/LZString.mjs";
import "./tests/Magic.mjs";
import "./tests/Media.mjs";
import "./tests/MIMEDecoding.mjs";
import "./tests/Modhex.mjs";
import "./tests/MorseCode.mjs";
import "./tests/MS.mjs";
import "./tests/MultipleBombe.mjs";

View File

@@ -0,0 +1,89 @@
/**
* MIME Header Decoding tests
*
* @author mshwed [m@ttshwed.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Encoded comments",
input: "(=?ISO-8859-1?Q?a?=)",
expectedOutput: "(a)",
recipeConfig: [
{
"op": "MIME Decoding",
"args": []
}
]
},
{
name: "Encoded adjacent comments whitespace",
input: "(=?ISO-8859-1?Q?a?= b)",
expectedOutput: "(a b)",
recipeConfig: [
{
"op": "MIME Decoding",
"args": []
}
]
},
{
name: "Encoded adjacent single whitespace ignored",
input: "(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)",
expectedOutput: "(ab)",
recipeConfig: [
{
"op": "MIME Decoding",
"args": []
}
]
},
{
name: "Encoded adjacent double whitespace ignored",
input: "(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)",
expectedOutput: "(ab)",
recipeConfig: [
{
"op": "MIME Decoding",
"args": []
}
]
},
{
name: "Encoded adjacent CRLF whitespace ignored",
input: "(=?ISO-8859-1?Q?a?=\r\n =?ISO-8859-1?Q?b?=)",
expectedOutput: "(ab)",
recipeConfig: [
{
"op": "MIME Decoding",
"args": []
}
]
},
{
name: "UTF-8 Encodings Multiple Headers",
input: "=?utf-8?q?=C3=89ric?= <eric@example.org>, =?utf-8?q?Ana=C3=AFs?= <anais@example.org>",
expectedOutput: "Éric <eric@example.org>, Anaïs <anais@example.org>",
recipeConfig: [
{
"op": "MIME Decoding",
"args": []
}
]
},
{
name: "ISO Decoding",
input: "From: =?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>\nTo: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>\nCC: =?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>\nSubject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=\n=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=",
expectedOutput: "From: Keith Moore <moore@cs.utk.edu>\nTo: Keld Jørn Simonsen <keld@dkuug.dk>\nCC: André Pirard <PIRARD@vm1.ulg.ac.be>\nSubject: If you can read this you understand the example.",
recipeConfig: [
{
"op": "MIME Decoding",
"args": []
}
]
}
]);

View File

@@ -0,0 +1,150 @@
/**
* Modhex operation tests.
* @author linuxgemini [ilteris@asenkron.com.tr]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "ASCII to Modhex stream",
input: "aberystwyth",
expectedOutput: "hbhdhgidikieifiiikifhj",
recipeConfig: [
{
"op": "To Modhex",
"args": [
"None",
0
]
},
]
},
{
name: "ASCII to Modhex with colon deliminator",
input: "aberystwyth",
expectedOutput: "hb:hd:hg:id:ik:ie:if:ii:ik:if:hj",
recipeConfig: [
{
"op": "To Modhex",
"args": [
"Colon",
0
]
}
]
},
{
name: "Modhex stream to UTF-8",
input: "uhkgkbuhkgkbugltlkugltkc",
expectedOutput: "救救孩子",
recipeConfig: [
{
"op": "From Modhex",
"args": [
"Auto"
]
}
]
},
{
name: "Mixed case Modhex stream to UTF-8",
input: "uhKGkbUHkgkBUGltlkugltkc",
expectedOutput: "救救孩子",
recipeConfig: [
{
"op": "From Modhex",
"args": [
"Auto"
]
}
]
},
{
name: "Mutiline Modhex with comma to ASCII (Auto Mode)",
input: "fk,dc,ie,hb,ii,dc,ht,ik,ie,hg,hr,hh,dc,ie,hk,\n\
if,if,hk,hu,hi,dc,hk,hu,dc,if,hj,hg,dc,he,id,\n\
hv,if,he,hj,dc,hv,hh,dc,if,hj,hg,dc,if,hj,hk,\n\
ie,dc,hh,hk,hi,dc,if,id,hg,hg,dr,dc,ie,if,hb,\n\
id,ih,hk,hu,hi,dc,if,hv,dc,hf,hg,hb,if,hj,dr,\n\
dc,hl,ig,ie,if,dc,hd,hg,he,hb,ig,ie,hg,dc,fk,\n\
dc,he,hv,ig,hr,hf,hu,di,if,dc,ht,hb,hn,hg,dc,\n\
ig,ic,dc,ht,ik,dc,ht,hk,hu,hf,dc,ii,hj,hk,he,\n\
hj,dc,hv,hh,dc,if,hj,hg,dc,hh,hk,hi,ie,dc,fk,\n\
dc,ii,hv,ig,hr,hf,dc,he,hj,hv,hv,ie,hg,du",
expectedOutput: "I saw myself sitting in the crotch of the this fig tree, starving to death, just because I couldn't make up my mind which of the figs I would choose.",
recipeConfig: [
{
"op": "From Modhex",
"args": [
"Auto"
]
}
]
},
{
name: "Mutiline Modhex with percent to ASCII (Percent Mode)",
input: "fk%dc%ie%hb%ii%dc%ht%ik%ie%hg%hr%hh%dc%ie%hk%\n\
if%if%hk%hu%hi%dc%hk%hu%dc%if%hj%hg%dc%he%id%\n\
hv%if%he%hj%dc%hv%hh%dc%if%hj%hg%dc%if%hj%hk%\n\
ie%dc%hh%hk%hi%dc%if%id%hg%hg%dr%dc%ie%if%hb%\n\
id%ih%hk%hu%hi%dc%if%hv%dc%hf%hg%hb%if%hj%dr%\n\
dc%hl%ig%ie%if%dc%hd%hg%he%hb%ig%ie%hg%dc%fk%\n\
dc%he%hv%ig%hr%hf%hu%di%if%dc%ht%hb%hn%hg%dc%\n\
ig%ic%dc%ht%ik%dc%ht%hk%hu%hf%dc%ii%hj%hk%he%\n\
hj%dc%hv%hh%dc%if%hj%hg%dc%hh%hk%hi%ie%dc%fk%\n\
dc%ii%hv%ig%hr%hf%dc%he%hj%hv%hv%ie%hg%du",
expectedOutput: "I saw myself sitting in the crotch of the this fig tree, starving to death, just because I couldn't make up my mind which of the figs I would choose.",
recipeConfig: [
{
"op": "From Modhex",
"args": [
"Percent"
]
}
]
},
{
name: "Mutiline Modhex with semicolon to ASCII (Semi-colon Mode)",
input: "fk;dc;ie;hb;ii;dc;ht;ik;ie;hg;hr;hh;dc;ie;hk;\n\
if;if;hk;hu;hi;dc;hk;hu;dc;if;hj;hg;dc;he;id;\n\
hv;if;he;hj;dc;hv;hh;dc;if;hj;hg;dc;if;hj;hk;\n\
ie;dc;hh;hk;hi;dc;if;id;hg;hg;dr;dc;ie;if;hb;\n\
id;ih;hk;hu;hi;dc;if;hv;dc;hf;hg;hb;if;hj;dr;\n\
dc;hl;ig;ie;if;dc;hd;hg;he;hb;ig;ie;hg;dc;fk;\n\
dc;he;hv;ig;hr;hf;hu;di;if;dc;ht;hb;hn;hg;dc;\n\
ig;ic;dc;ht;ik;dc;ht;hk;hu;hf;dc;ii;hj;hk;he;\n\
hj;dc;hv;hh;dc;if;hj;hg;dc;hh;hk;hi;ie;dc;fk;\n\
dc;ii;hv;ig;hr;hf;dc;he;hj;hv;hv;ie;hg;du",
expectedOutput: "I saw myself sitting in the crotch of the this fig tree, starving to death, just because I couldn't make up my mind which of the figs I would choose.",
recipeConfig: [
{
"op": "From Modhex",
"args": [
"Semi-colon"
]
}
]
},
{
name: "ASCII to Modhex with comma and line breaks",
input: "aberystwyth",
expectedOutput: "hb,hd,hg,id,\nik,ie,if,ii,\nik,if,hj",
recipeConfig: [
{
"op": "To Modhex",
"args": [
"Comma",
4
]
}
]
},
]);