Compare commits

..

31 Commits

Author SHA1 Message Date
n1474335
614af0602a 7.6.2 2018-01-25 18:41:53 +00:00
n1474335
e55cfe0bc1 Fixed 'Syntax highlighter' operation. Using highlight.js instead of google-code-prettify. 2018-01-25 18:41:47 +00:00
n1474335
2b703b2b9b HTML outputs are now unescaped correctly when converted to a string 2018-01-25 16:25:19 +00:00
n1474335
170feaaff2 7.6.1 2018-01-25 14:03:19 +00:00
n1474335
870c2b6d8b Fixed deep copy bug with Fork/Register ingredient values. 2018-01-25 14:03:13 +00:00
n1474335
eee8b7db56 Fixed dispatchEvent call in recipe loading chain. 2018-01-25 13:46:06 +00:00
n1474335
3c669a075e 7.6.0 2018-01-25 13:45:05 +00:00
n1474335
f528930ad2 Added 'Sleep' operation. 2018-01-25 13:44:39 +00:00
n1474335
231322eddf 7.5.6 2018-01-24 16:54:42 +00:00
n1474335
8e6763c165 'Register' and 'Fork' now play well together. 2018-01-24 16:54:37 +00:00
n1474335
f091918575 7.5.5 2018-01-24 15:50:10 +00:00
n1474335
bb077c87b3 'Extract file paths' operation now handles 8.3 windows file paths correctly. 2018-01-24 15:50:05 +00:00
n1474335
fe8f8bc712 Setting a text value in the input now closes any open files. 2018-01-22 19:58:21 +00:00
n1474335
abe87830cd Operation tooltips now disappear if you hover over them while dragging an opertion. 2018-01-22 17:51:04 +00:00
n1474335
7490651a06 7.5.4 2018-01-22 17:10:02 +00:00
n1474335
6220128a74 Fixed delimiter options in StrUtils. Closes #238. 2018-01-22 17:09:58 +00:00
n1474335
ec205f4f7d 7.5.3 2018-01-18 19:53:01 +00:00
n1474335
512487328d Fixed bugs in pretty recipe format generation 2018-01-18 18:35:17 +00:00
n1474335
6fbb2f26d1 7.5.2 2018-01-18 15:27:26 +00:00
n1474335
c91bfeaa81 Merge branch 'qistoph-SplitAndJoin' 2018-01-18 15:26:34 +00:00
n1474335
aa2b3b2843 Changed order of split delimiters, placing comma first. 2018-01-18 15:26:09 +00:00
Chris van Marle
90d8be48d4 Make Split more flexible so it can be used to join 2018-01-17 15:52:25 +01:00
n1474335
aa6890432c 7.5.1 2018-01-12 23:59:03 +00:00
n1474335
192d0ed8a6 Merge branch 'feature-unicode-strings' 2018-01-12 23:57:20 +00:00
n1474335
fff188eb30 Merged master into feature-unicode-strings 2018-01-12 23:57:02 +00:00
n1474335
71067939e3 Added Regex tests and updated description 2018-01-12 23:51:51 +00:00
n1474335
b07c014b48 Added more modifiers to the Regex operation 2018-01-12 23:42:48 +00:00
n1474335
f2c073798b 'Strings' now supports various different match types in ASCII and Unicode 2018-01-12 23:09:27 +00:00
n1474335
4cc38db895 Added documentation. 2018-01-12 22:14:06 +00:00
n1474335
ec02b7deda Regexes are now checked for 0-length matches and incremented manually to avoid infinite loops 2018-01-10 19:44:25 +00:00
n1474335
56551712d6 Began implementing UTF-16 support in the 'Strings' operation. 2018-01-03 16:51:10 +00:00
26 changed files with 627 additions and 368 deletions

View File

@@ -186,7 +186,10 @@ module.exports = function (grunt) {
options: webpackConfig,
metaConf: {
target: "node",
entry: "./src/core/config/OperationConfig.js",
entry: [
"babel-polyfill",
"./src/core/config/OperationConfig.js"
],
output: {
filename: "MetaConfig.js",
path: __dirname + "/src/core/config/",
@@ -198,7 +201,10 @@ module.exports = function (grunt) {
},
metaConfDev: {
target: "node",
entry: "./src/core/config/OperationConfig.js",
entry: [
"babel-polyfill",
"./src/core/config/OperationConfig.js"
],
output: {
filename: "MetaConfig.js",
path: __dirname + "/src/core/config/",

17
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "7.5.0",
"version": "7.6.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -4341,11 +4341,6 @@
"pinkie-promise": "2.0.1"
}
},
"google-code-prettify": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/google-code-prettify/-/google-code-prettify-1.0.5.tgz",
"integrity": "sha1-n0d/Ik2/piNy5e+AOn4VdBBAAIQ="
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
@@ -4745,6 +4740,11 @@
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
"dev": true
},
"highlight.js": {
"version": "9.12.0",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz",
"integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4="
},
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -10256,6 +10256,11 @@
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz",
"integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ=="
},
"xregexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz",
"integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg=="
},
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "7.5.0",
"version": "7.6.2",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef",
@@ -80,7 +80,7 @@
"esprima": "^4.0.0",
"exif-parser": "^0.1.12",
"file-saver": "^1.3.3",
"google-code-prettify": "^1.0.5",
"highlight.js": "^9.12.0",
"jquery": "^3.2.1",
"js-crc": "^0.2.0",
"js-sha3": "^0.7.0",
@@ -103,6 +103,7 @@
"vkbeautify": "^0.99.3",
"xmldom": "^0.1.27",
"xpath": "0.0.27",
"xregexp": "^4.0.0",
"zlibjs": "^0.3.1"
},
"scripts": {

View File

@@ -52,14 +52,25 @@ const FlowControl = {
output = "",
progress = 0;
state.forkOffset += state.progress + 1;
recipe.addOperations(subOpList);
// Take a deep(ish) copy of the ingredient values
const ingValues = subOpList.map(op => JSON.parse(JSON.stringify(op.getIngValues())));
// Run recipe over each tranche
for (i = 0; i < inputs.length; i++) {
log.debug(`Entering tranche ${i + 1} of ${inputs.length}`);
// Baseline ing values for each tranche so that registers are reset
subOpList.forEach((op, i) => {
op.setIngValues(JSON.parse(JSON.stringify(ingValues[i])));
});
const dish = new Dish(inputs[i], inputType);
try {
progress = await recipe.execute(dish, 0);
progress = await recipe.execute(dish, 0, state);
} catch (err) {
if (!ignoreErrors) {
throw err;
@@ -117,7 +128,7 @@ const FlowControl = {
if (!registers) return state;
if (ENVIRONMENT_IS_WORKER()) {
self.setRegisters(state.progress, state.numRegisters, registers.slice(1));
self.setRegisters(state.forkOffset + state.progress, state.numRegisters, registers.slice(1));
}
/**

View File

@@ -141,11 +141,14 @@ Recipe.prototype.lastOpIndex = function(startIndex) {
*
* @param {Dish} dish
* @param {number} [startFrom=0] - The index of the Operation to start executing from
* @param {number} [forkState={}] - If this is a forked recipe, the state of the recipe up to this point
* @returns {number} - The final progress through the recipe
*/
Recipe.prototype.execute = async function(dish, startFrom) {
startFrom = startFrom || 0;
let op, input, output, numJumps = 0, numRegisters = 0;
Recipe.prototype.execute = async function(dish, startFrom = 0, forkState = {}) {
let op, input, output,
numJumps = 0,
numRegisters = forkState.numRegisters || 0;
log.debug(`[*] Executing recipe of ${this.opList.length} operations, starting at ${startFrom}`);
for (let i = startFrom; i < this.opList.length; i++) {
@@ -171,7 +174,8 @@ Recipe.prototype.execute = async function(dish, startFrom) {
"dish": dish,
"opList": this.opList,
"numJumps": numJumps,
"numRegisters": numRegisters
"numRegisters": numRegisters,
"forkOffset": forkState.forkOffset || 0
};
state = await op.run(state);
@@ -256,4 +260,5 @@ Recipe.prototype.generateHighlightList = function() {
return highlights;
};
export default Recipe;

View File

@@ -903,9 +903,9 @@ const Utils = {
* "Pretty" CyberChef recipe formats are designed to be included in the fragment (#) or query (?)
* parts of the URL. They can also be loaded into CyberChef through the 'Load' interface. In order
* to make this format as readable as possible, various special characters are used unescaped. This
* reduces the amount of percent-encoding included in the URL which is typically difficult to read,
* as well as substantially increasing the overall length. These characteristics can be quite
* offputting for users.
* reduces the amount of percent-encoding included in the URL which is typically difficult to read
* and substantially increases the overall length. These characteristics can be quite off-putting
* for users.
*
* @param {Object[]} recipeConfig
* @param {boolean} newline - whether to add a newline after each operation
@@ -922,12 +922,11 @@ const Utils = {
name = op.op.replace(/ /g, "_");
args = JSON.stringify(op.args)
.slice(1, -1) // Remove [ and ] as they are implied
// We now need to switch double-quoted (") strings to single-quotes (') as these do not
// need to be percent-encoded.
// We now need to switch double-quoted (") strings to single quotes (') as single quotes
// do not need to be percent-encoded.
.replace(/'/g, "\\'") // Escape single quotes
.replace(/\\"/g, '"') // Unescape double quotes
.replace(/(^|,|{|:)"/g, "$1'") // Replace opening " with '
.replace(/"(,|:|}|$)/g, "'$1"); // Replace closing " with '
.replace(/"((?:[^"\\]|\\.)*)"/g, "'$1'") // Replace opening and closing " with '
.replace(/\\"/g, '"'); // Unescape double quotes
disabled = op.disabled ? "/disabled": "";
bp = op.breakpoint ? "/breakpoint" : "";

View File

@@ -201,6 +201,7 @@ const Categories = [
"Escape string",
"Unescape string",
"Pseudo-Random Number Generator",
"Sleep",
]
},
{
@@ -213,6 +214,7 @@ const Categories = [
"Windows Filetime to UNIX Timestamp",
"UNIX Timestamp to Windows Filetime",
"Extract dates",
"Sleep",
]
},
{

View File

@@ -30,6 +30,7 @@ import NetBIOS from "../operations/NetBIOS.js";
import PHP from "../operations/PHP.js";
import PublicKey from "../operations/PublicKey.js";
import Punycode from "../operations/Punycode.js";
import Regex from "../operations/Regex.js";
import Rotate from "../operations/Rotate.js";
import SeqUtils from "../operations/SeqUtils.js";
import Shellcode from "../operations/Shellcode.js";
@@ -2058,9 +2059,8 @@ const OperationConfig = {
args: []
},
"Find / Replace": {
module: "Default",
module: "Regex",
description: "Replaces all occurrences of the first string with the second.<br><br> Includes support for regular expressions (regex), simple strings and extended strings (which support \\n, \\r, \\t, \\b, \\f and escaped hex bytes using \\x notation, e.g. \\x00 for a null byte).",
manualBake: true,
inputType: "string",
outputType: "string",
args: [
@@ -2068,7 +2068,7 @@ const OperationConfig = {
name: "Find",
type: "toggleString",
value: "",
toggleValues: StrUtils.SEARCH_TYPE
toggleValues: Regex.SEARCH_TYPE
},
{
name: "Replace",
@@ -2078,17 +2078,17 @@ const OperationConfig = {
{
name: "Global match",
type: "boolean",
value: StrUtils.FIND_REPLACE_GLOBAL,
value: Regex.FIND_REPLACE_GLOBAL,
},
{
name: "Case insensitive",
type: "boolean",
value: StrUtils.FIND_REPLACE_CASE,
value: Regex.FIND_REPLACE_CASE,
},
{
name: "Multiline matching",
type: "boolean",
value: StrUtils.FIND_REPLACE_MULTILINE,
value: Regex.FIND_REPLACE_MULTILINE,
},
]
@@ -2125,20 +2125,19 @@ const OperationConfig = {
args: [
{
name: "Split delimiter",
type: "binaryShortString",
value: StrUtils.SPLIT_DELIM
type: "editableOption",
value: StrUtils.SPLIT_DELIM_OPTIONS
},
{
name: "Join delimiter",
type: "option",
value: StrUtils.DELIMITER_OPTIONS
type: "editableOption",
value: StrUtils.JOIN_DELIM_OPTIONS
}
]
},
"Filter": {
module: "Default",
description: "Splits up the input using the specified delimiter and then filters each branch based on a regular expression.",
manualBake: true,
inputType: "string",
outputType: "string",
args: [
@@ -2155,21 +2154,31 @@ const OperationConfig = {
{
name: "Invert condition",
type: "boolean",
value: SeqUtils.SORT_REVERSE
value: false
},
]
},
"Strings": {
module: "Default",
module: "Regex",
description: "Extracts all strings from the input.",
inputType: "string",
outputType: "string",
args: [
{
name: "Encoding",
type: "option",
value: Extract.ENCODING_LIST
},
{
name: "Minimum length",
type: "number",
value: Extract.MIN_STRING_LEN
},
{
name: "Match",
type: "option",
value: Extract.STRING_MATCH_TYPE
},
{
name: "Display total",
type: "boolean",
@@ -2178,7 +2187,7 @@ const OperationConfig = {
]
},
"Extract IP addresses": {
module: "Default",
module: "Regex",
description: "Extracts all IPv4 and IPv6 addresses.<br><br>Warning: Given a string <code>710.65.0.456</code>, this will match <code>10.65.0.45</code> so always check the original input!",
inputType: "string",
outputType: "string",
@@ -2206,7 +2215,7 @@ const OperationConfig = {
]
},
"Extract email addresses": {
module: "Default",
module: "Regex",
description: "Extracts all email addresses from the input.",
inputType: "string",
outputType: "string",
@@ -2219,7 +2228,7 @@ const OperationConfig = {
]
},
"Extract MAC addresses": {
module: "Default",
module: "Regex",
description: "Extracts all Media Access Control (MAC) addresses from the input.",
inputType: "string",
outputType: "string",
@@ -2232,7 +2241,7 @@ const OperationConfig = {
]
},
"Extract URLs": {
module: "Default",
module: "Regex",
description: "Extracts Uniform Resource Locators (URLs) from the input. The protocol (http, ftp etc.) is required otherwise there will be far too many false positives.",
inputType: "string",
outputType: "string",
@@ -2245,7 +2254,7 @@ const OperationConfig = {
]
},
"Extract domains": {
module: "Default",
module: "Regex",
description: "Extracts domain names.<br>Note that this will not include paths. Use <strong>Extract URLs</strong> to find entire URLs.",
inputType: "string",
outputType: "string",
@@ -2258,7 +2267,7 @@ const OperationConfig = {
]
},
"Extract file paths": {
module: "Default",
module: "Regex",
description: "Extracts anything that looks like a Windows or UNIX file path.<br><br>Note that if UNIX is selected, there will likely be a lot of false positives.",
inputType: "string",
outputType: "string",
@@ -2281,7 +2290,7 @@ const OperationConfig = {
]
},
"Extract dates": {
module: "Default",
module: "Regex",
description: "Extracts dates in the following formats<ul><li><code>yyyy-mm-dd</code></li><li><code>dd/mm/yyyy</code></li><li><code>mm/dd/yyyy</code></li></ul>Dividers can be any of /, -, . or space",
inputType: "string",
outputType: "string",
@@ -2294,16 +2303,15 @@ const OperationConfig = {
]
},
"Regular expression": {
module: "Default",
description: "Define your own regular expression (regex) to search the input data with, optionally choosing from a list of pre-defined patterns.",
manualBake: true,
module: "Regex",
description: "Define your own regular expression (regex) to search the input data with, optionally choosing from a list of pre-defined patterns.<br><br>Supports extended regex syntax including the 'dot matches all' flag, named capture groups, full unicode coverage (including <code>\\p{}</code> categories and scripts as well as astral codes) and recursive matching.",
inputType: "string",
outputType: "html",
args: [
{
name: "Built in regexes",
type: "populateOption",
value: StrUtils.REGEX_PRE_POPULATE,
value: Regex.REGEX_PRE_POPULATE,
target: 1,
},
{
@@ -2314,22 +2322,37 @@ const OperationConfig = {
{
name: "Case insensitive",
type: "boolean",
value: StrUtils.REGEX_CASE_INSENSITIVE
value: true
},
{
name: "Multiline matching",
name: "^ and $ match at newlines",
type: "boolean",
value: StrUtils.REGEX_MULTILINE_MATCHING
value: true
},
{
name: "Dot matches all",
type: "boolean",
value: false
},
{
name: "Unicode support",
type: "boolean",
value: false
},
{
name: "Astral support",
type: "boolean",
value: false
},
{
name: "Display total",
type: "boolean",
value: StrUtils.DISPLAY_TOTAL
value: Regex.DISPLAY_TOTAL
},
{
name: "Output format",
type: "option",
value: StrUtils.OUTPUT_FORMAT
value: Regex.OUTPUT_FORMAT
},
]
},
@@ -2418,6 +2441,19 @@ const OperationConfig = {
}
]
},
"Sleep": {
module: "Default",
description: "Sleep causes the recipe to wait for a specified number of milliseconds before continuing execution.",
inputType: "ArrayBuffer",
outputType: "ArrayBuffer",
args: [
{
name: "Time (ms)",
type: "number",
value: 1000
}
]
},
"Windows Filetime to UNIX Timestamp": {
module: "JSBN",
description: "Converts a Windows Filetime value to a UNIX timestamp.<br><br>A Windows Filetime is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 UTC.<br><br>A UNIX timestamp is a 32-bit value representing the number of seconds since January 1, 1970 UTC (the UNIX epoch).<br><br>This operation also supports UNIX timestamps in milliseconds, microseconds and nanoseconds.",
@@ -3426,15 +3462,10 @@ const OperationConfig = {
outputType: "html",
args: [
{
name: "Language/File extension",
name: "Language",
type: "option",
value: Code.LANGUAGES
},
{
name: "Display line numbers",
type: "boolean",
value: Code.LINE_NUMS
}
]
},
"TCP/IP Checksum": {

View File

@@ -11,7 +11,7 @@ import Code from "../../operations/Code.js";
* - xmldom
* - xpath
* - jpath
* - googlecodeprettify
* - highlight.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017

View File

@@ -10,7 +10,6 @@ import Convert from "../../operations/Convert.js";
import DateTime from "../../operations/DateTime.js";
import Endian from "../../operations/Endian.js";
import Entropy from "../../operations/Entropy.js";
import Extract from "../../operations/Extract.js";
import FileType from "../../operations/FileType.js";
import Hexdump from "../../operations/Hexdump.js";
import HTML from "../../operations/HTML.js";
@@ -99,11 +98,9 @@ OpModules.Default = {
"Format MAC addresses": MAC.runFormat,
"Encode NetBIOS Name": NetBIOS.runEncodeName,
"Decode NetBIOS Name": NetBIOS.runDecodeName,
"Regular expression": StrUtils.runRegex,
"Offset checker": StrUtils.runOffsetChecker,
"To Upper case": StrUtils.runUpper,
"To Lower case": StrUtils.runLower,
"Find / Replace": StrUtils.runFindReplace,
"Split": StrUtils.runSplit,
"Filter": StrUtils.runFilter,
"Escape string": StrUtils.runEscape,
@@ -133,14 +130,7 @@ OpModules.Default = {
"Translate DateTime Format": DateTime.runTranslateFormat,
"From UNIX Timestamp": DateTime.runFromUnixTimestamp,
"To UNIX Timestamp": DateTime.runToUnixTimestamp,
"Strings": Extract.runStrings,
"Extract IP addresses": Extract.runIp,
"Extract email addresses": Extract.runEmail,
"Extract MAC addresses": Extract.runMac,
"Extract URLs": Extract.runUrls,
"Extract domains": Extract.runDomains,
"Extract file paths": Extract.runFilePaths,
"Extract dates": Extract.runDates,
"Sleep": DateTime.runSleep,
"Microsoft Script Decoder": MS.runDecodeScript,
"Entropy": Entropy.runEntropy,
"Frequency distribution": Entropy.runFreqDistrib,

View File

@@ -18,6 +18,7 @@ import HTTPModule from "./HTTP.js";
import ImageModule from "./Image.js";
import JSBNModule from "./JSBN.js";
import PublicKeyModule from "./PublicKey.js";
import RegexModule from "./Regex.js";
import ShellcodeModule from "./Shellcode.js";
import URLModule from "./URL.js";
@@ -34,6 +35,7 @@ Object.assign(
ImageModule,
JSBNModule,
PublicKeyModule,
RegexModule,
ShellcodeModule,
URLModule
);

View File

@@ -0,0 +1,30 @@
import Extract from "../../operations/Extract.js";
import Regex from "../../operations/Regex.js";
/**
* Regex module.
*
* Libraries:
* - XRegExp
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Regex = {
"Regular expression": Regex.runRegex,
"Find / Replace": Regex.runFindReplace,
"Strings": Extract.runStrings,
"Extract IP addresses": Extract.runIp,
"Extract email addresses": Extract.runEmail,
"Extract MAC addresses": Extract.runMac,
"Extract URLs": Extract.runUrls,
"Extract domains": Extract.runDomains,
"Extract file paths": Extract.runFilePaths,
"Extract dates": Extract.runDates,
};
export default OpModules;

View File

@@ -521,6 +521,10 @@ DES uses a key length of 8 bytes (64 bits).`;
* @default
*/
PRNG_BYTES: 32,
/**
* @constant
* @default
*/
PRNG_OUTPUT: ["Hex", "Integer", "Byte array", "Raw"],
/**

View File

@@ -1,12 +1,10 @@
import {camelCase, kebabCase, snakeCase} from "lodash";
import Utils from "../Utils.js";
import vkbeautify from "vkbeautify";
import {DOMParser} from "xmldom";
import xpath from "xpath";
import jpath from "jsonpath";
import nwmatcher from "nwmatcher";
import prettyPrintOne from "imports-loader?window=>global!exports-loader?prettyPrintOne!google-code-prettify/bin/prettify.min.js";
import hljs from "highlight.js";
/**
@@ -24,12 +22,7 @@ const Code = {
* @constant
* @default
*/
LANGUAGES: ["default-code", "default-markup", "bash", "bsh", "c", "cc", "coffee", "cpp", "cs", "csh", "cv", "cxx", "cyc", "htm", "html", "in.tag", "java", "javascript", "js", "json", "m", "mxml", "perl", "pl", "pm", "py", "python", "rb", "rc", "rs", "ruby", "rust", "sh", "uq.val", "xhtml", "xml", "xsl"],
/**
* @constant
* @default
*/
LINE_NUMS: false,
LANGUAGES: ["auto detect"].concat(hljs.listLanguages()),
/**
* Syntax highlighter operation.
@@ -39,9 +32,13 @@ const Code = {
* @returns {html}
*/
runSyntaxHighlight: function(input, args) {
let language = args[0],
lineNums = args[1];
return "<code class='prettyprint'>" + prettyPrintOne(Utils.escapeHtml(input), language, lineNums) + "</code>";
const language = args[0];
if (language === "auto detect") {
return hljs.highlightAuto(input).value;
}
return hljs.highlight(language, input, true).value;
},

View File

@@ -189,6 +189,20 @@ const DateTime = {
},
/**
* Sleep operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
runSleep: async function(input, args) {
const ms = args[0];
await new Promise(r => setTimeout(r, ms));
return input;
},
/**
* @constant
*/

View File

@@ -1,3 +1,6 @@
import XRegExp from "xregexp";
/**
* Identifier extraction operations.
*
@@ -26,6 +29,11 @@ const Extract = {
match;
while ((match = searchRegex.exec(input))) {
// Moves pointer when an empty string is matched (prevents infinite loop)
if (match.index === searchRegex.lastIndex) {
searchRegex.lastIndex++;
}
if (removeRegex && removeRegex.test(match[0]))
continue;
total++;
@@ -43,7 +51,20 @@ const Extract = {
* @constant
* @default
*/
MIN_STRING_LEN: 3,
MIN_STRING_LEN: 4,
/**
* @constant
* @default
*/
STRING_MATCH_TYPE: [
"[ASCII]", "Alphanumeric + punctuation (A)", "All printable chars (A)", "Null-terminated strings (A)",
"[Unicode]", "Alphanumeric + punctuation (U)", "All printable chars (U)", "Null-terminated strings (U)"
],
/**
* @constant
* @default
*/
ENCODING_LIST: ["Single byte", "16-bit littleendian", "16-bit bigendian", "All"],
/**
* @constant
* @default
@@ -58,10 +79,59 @@ const Extract = {
* @returns {string}
*/
runStrings: function(input, args) {
let minLen = args[0] || Extract.MIN_STRING_LEN,
displayTotal = args[1],
strings = "[A-Z\\d/\\-:.,_$%'\"()<>= !\\[\\]{}@]",
regex = new RegExp(strings + "{" + minLen + ",}", "ig");
const encoding = args[0],
minLen = args[1],
matchType = args[2],
displayTotal = args[3],
alphanumeric = "A-Z\\d",
punctuation = "/\\-:.,_$%'\"()<>= !\\[\\]{}@",
printable = "\x20-\x7e",
uniAlphanumeric = "\\pL\\pN",
uniPunctuation = "\\pP\\pZ",
uniPrintable = "\\pL\\pM\\pZ\\pS\\pN\\pP";
let strings = "";
switch (matchType) {
case "Alphanumeric + punctuation (A)":
strings = `[${alphanumeric + punctuation}]`;
break;
case "All printable chars (A)":
case "Null-terminated strings (A)":
strings = `[${printable}]`;
break;
case "Alphanumeric + punctuation (U)":
strings = `[${uniAlphanumeric + uniPunctuation}]`;
break;
case "All printable chars (U)":
case "Null-terminated strings (U)":
strings = `[${uniPrintable}]`;
break;
}
// UTF-16 support is hacked in by allowing null bytes on either side of the matched chars
switch (encoding) {
case "All":
strings = `(\x00?${strings}\x00?)`;
break;
case "16-bit littleendian":
strings = `(${strings}\x00)`;
break;
case "16-bit bigendian":
strings = `(\x00${strings})`;
break;
case "Single byte":
default:
break;
}
strings = `${strings}{${minLen},}`;
if (matchType.includes("Null-terminated")) {
strings += "\x00";
}
const regex = new XRegExp(strings, "ig");
return Extract._search(input, regex, null, displayTotal);
},
@@ -217,7 +287,7 @@ const Extract = {
includeUnixPath = args[1],
displayTotal = args[2],
winDrive = "[A-Z]:\\\\",
winName = "[A-Z\\d][A-Z\\d\\- '_\\(\\)]{0,61}",
winName = "[A-Z\\d][A-Z\\d\\- '_\\(\\)~]{0,61}",
winExt = "[A-Z\\d]{1,6}",
winPath = winDrive + "(?:" + winName + "\\\\?)*" + winName +
"(?:\\." + winExt + ")?",

View File

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

View File

@@ -12,128 +12,6 @@ import Utils from "../Utils.js";
*/
const StrUtils = {
/**
* @constant
* @default
*/
REGEX_PRE_POPULATE: [
{
name: "User defined",
value: ""
},
{
name: "IPv4 address",
value: "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?"
},
{
name: "IPv6 address",
value: "((?=.*::)(?!.*::.+::)(::)?([\\dA-Fa-f]{1,4}:(:|\\b)|){5}|([\\dA-Fa-f]{1,4}:){6})((([\\dA-Fa-f]{1,4}((?!\\3)::|:\\b|(?![\\dA-Fa-f])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})"
},
{
name: "Email address",
value: "(\\w[-.\\w]*)@([-\\w]+(?:\\.[-\\w]+)*)\\.([A-Za-z]{2,4})"
},
{
name: "URL",
value: "([A-Za-z]+://)([-\\w]+(?:\\.\\w[-\\w]*)+)(:\\d+)?(/[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]*(?:[.!,?]+[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]+)*)?"
},
{
name: "Domain",
value: "\\b((?=[a-z0-9-]{1,63}\\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,63}\\b"
},
{
name: "Windows file path",
value: "([A-Za-z]):\\\\((?:[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)]{0,61}\\\\?)*[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)]{0,61})(\\.[A-Za-z\\d]{1,6})?"
},
{
name: "UNIX file path",
value: "(?:/[A-Za-z\\d.][A-Za-z\\d\\-.]{0,61})+"
},
{
name: "MAC address",
value: "[A-Fa-f\\d]{2}(?:[:-][A-Fa-f\\d]{2}){5}"
},
{
name: "Date (yyyy-mm-dd)",
value: "((?:19|20)\\d\\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])"
},
{
name: "Date (dd/mm/yyyy)",
value: "(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.]((?:19|20)\\d\\d)"
},
{
name: "Date (mm/dd/yyyy)",
value: "(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.]((?:19|20)\\d\\d)"
},
{
name: "Strings",
value: "[A-Za-z\\d/\\-:.,_$%\\x27\"()<>= !\\[\\]{}@]{4,}"
},
],
/**
* @constant
* @default
*/
REGEX_CASE_INSENSITIVE: true,
/**
* @constant
* @default
*/
REGEX_MULTILINE_MATCHING: true,
/**
* @constant
* @default
*/
OUTPUT_FORMAT: ["Highlight matches", "List matches", "List capture groups", "List matches with capture groups"],
/**
* @constant
* @default
*/
DISPLAY_TOTAL: false,
/**
* Regular expression operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runRegex: function(input, args) {
let userRegex = args[1],
i = args[2],
m = args[3],
displayTotal = args[4],
outputFormat = args[5],
modifiers = "g";
if (i) modifiers += "i";
if (m) modifiers += "m";
if (userRegex && userRegex !== "^" && userRegex !== "$") {
try {
const regex = new RegExp(userRegex, modifiers);
switch (outputFormat) {
case "Highlight matches":
return StrUtils._regexHighlight(input, regex, displayTotal);
case "List matches":
return Utils.escapeHtml(StrUtils._regexList(input, regex, displayTotal, true, false));
case "List capture groups":
return Utils.escapeHtml(StrUtils._regexList(input, regex, displayTotal, false, true));
case "List matches with capture groups":
return Utils.escapeHtml(StrUtils._regexList(input, regex, displayTotal, true, true));
default:
return "Error: Invalid output format";
}
} catch (err) {
return "Invalid regex. Details: " + err.message;
}
} else {
return Utils.escapeHtml(input);
}
},
/**
* @constant
* @default
@@ -187,68 +65,28 @@ const StrUtils = {
* @constant
* @default
*/
SEARCH_TYPE: ["Regex", "Extended (\\n, \\t, \\x...)", "Simple string"],
SPLIT_DELIM_OPTIONS: [
{name: "Comma", value: ","},
{name: "Space", value: " "},
{name: "Line feed", value: "\\n"},
{name: "CRLF", value: "\\r\\n"},
{name: "Semi-colon", value: ";"},
{name: "Colon", value: ":"},
{name: "Nothing (separate chars)", value: ""}
],
/**
* @constant
* @default
*/
FIND_REPLACE_GLOBAL: true,
/**
* @constant
* @default
*/
FIND_REPLACE_CASE: false,
/**
* @constant
* @default
*/
FIND_REPLACE_MULTILINE: true,
/**
* Find / Replace operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runFindReplace: function(input, args) {
let find = args[0].string,
type = args[0].option,
replace = args[1],
g = args[2],
i = args[3],
m = args[4],
modifiers = "";
if (g) modifiers += "g";
if (i) modifiers += "i";
if (m) modifiers += "m";
if (type === "Regex") {
find = new RegExp(find, modifiers);
return input.replace(find, replace);
}
if (type.indexOf("Extended") === 0) {
find = Utils.parseEscapedChars(find);
}
find = new RegExp(Utils.escapeRegex(find), modifiers);
return input.replace(find, replace);
},
/**
* @constant
* @default
*/
SPLIT_DELIM: ",",
/**
* @constant
* @default
*/
DELIMITER_OPTIONS: ["Line feed", "CRLF", "Space", "Comma", "Semi-colon", "Colon", "Nothing (separate chars)"],
JOIN_DELIM_OPTIONS: [
{name: "Line feed", value: "\\n"},
{name: "CRLF", value: "\\r\\n"},
{name: "Space", value: " "},
{name: "Comma", value: ","},
{name: "Semi-colon", value: ";"},
{name: "Colon", value: ":"},
{name: "Nothing (join chars)", value: ""}
],
/**
* Split operation.
@@ -258,14 +96,20 @@ const StrUtils = {
* @returns {string}
*/
runSplit: function(input, args) {
let splitDelim = args[0] || StrUtils.SPLIT_DELIM,
joinDelim = Utils.charRep[args[1]],
let splitDelim = args[0],
joinDelim = args[1],
sections = input.split(splitDelim);
return sections.join(joinDelim);
},
/**
* @constant
* @default
*/
DELIMITER_OPTIONS: ["Line feed", "CRLF", "Space", "Comma", "Semi-colon", "Colon", "Nothing (separate chars)"],
/**
* Filter operation.
*
@@ -576,84 +420,6 @@ const StrUtils = {
return dist.toString();
},
/**
* Adds HTML highlights to matches within a string.
*
* @private
* @param {string} input
* @param {RegExp} regex
* @param {boolean} displayTotal
* @returns {string}
*/
_regexHighlight: function(input, regex, displayTotal) {
let output = "",
m,
hl = 1,
i = 0,
total = 0;
while ((m = regex.exec(input))) {
// Add up to match
output += Utils.escapeHtml(input.slice(i, m.index));
// Add match with highlighting
output += "<span class='hl"+hl+"'>" + Utils.escapeHtml(m[0]) + "</span>";
// Switch highlight
hl = hl === 1 ? 2 : 1;
i = regex.lastIndex;
total++;
}
// Add all after final match
output += Utils.escapeHtml(input.slice(i, input.length));
if (displayTotal)
output = "Total found: " + total + "\n\n" + output;
return output;
},
/**
* Creates a string listing the matches within a string.
*
* @private
* @param {string} input
* @param {RegExp} regex
* @param {boolean} displayTotal
* @param {boolean} matches - Display full match
* @param {boolean} captureGroups - Display each of the capture groups separately
* @returns {string}
*/
_regexList: function(input, regex, displayTotal, matches, captureGroups) {
let output = "",
total = 0,
match;
while ((match = regex.exec(input))) {
total++;
if (matches) {
output += match[0] + "\n";
}
if (captureGroups) {
for (let i = 1; i < match.length; i++) {
if (matches) {
output += " Group " + i + ": ";
}
output += match[i] + "\n";
}
}
}
if (displayTotal)
output = "Total found: " + total + "\n\n" + output;
return output;
},
};
export default StrUtils;

View File

@@ -443,6 +443,7 @@ App.prototype.getRecipeConfig = function() {
/**
* Given a recipe configuration, sets the recipe to that configuration.
*
* @fires Manager#statechange
* @param {Object[]} recipeConfig - The recipe configuration
*/
App.prototype.setRecipeConfig = function(recipeConfig) {

View File

@@ -64,6 +64,7 @@ InputWaiter.prototype.set = function(input) {
this.setInputInfo(input.size, null);
} else {
inputText.value = input;
this.closeFile();
window.dispatchEvent(this.manager.statechange);
const lines = input.length < (this.app.options.ioDisplayThreshold * 1024) ?
input.count("\n") + 1 : null;

View File

@@ -167,7 +167,8 @@ OperationsWaiter.prototype.opListCreate = function(e) {
OperationsWaiter.prototype.enableOpsListPopovers = function(el) {
$(el).find("[data-toggle=popover]").addBack("[data-toggle=popover]")
.popover({trigger: "manual"})
.on("mouseenter", function() {
.on("mouseenter", function(e) {
if (e.buttons > 0) return; // Mouse button held down - likely dragging an opertion
const _this = this;
$(this).popover("show");
$(".popover").on("mouseleave", function () {
@@ -178,7 +179,7 @@ OperationsWaiter.prototype.enableOpsListPopovers = function(el) {
setTimeout(function() {
// Determine if the popover associated with this element is being hovered over
if ($(_this).data("bs.popover") &&
!$(_this).data("bs.popover").$tip.is(":hover")) {
($(_this).data("bs.popover").$tip && !$(_this).data("bs.popover").$tip.is(":hover"))) {
$(_this).popover("hide");
}
}, 50);

View File

@@ -64,7 +64,7 @@ OutputWaiter.prototype.set = function(data, type, duration, preserveBuffer) {
outputText.value = "";
outputHtml.innerHTML = data;
this.dishStr = Utils.stripHtmlTags(data, true);
this.dishStr = Utils.unescapeHtml(Utils.stripHtmlTags(data, true));
length = data.length;
lines = this.dishStr.count("\n") + 1;

View File

@@ -7,7 +7,7 @@
*/
/* Libraries */
import "google-code-prettify/src/prettify.css";
import "highlight.js/styles/vs.css";
/* Frameworks */
import "./vendors/bootstrap.less";

View File

@@ -30,6 +30,7 @@ import "./tests/operations/MS.js";
import "./tests/operations/PHP.js";
import "./tests/operations/NetBIOS.js";
import "./tests/operations/OTP.js";
import "./tests/operations/Regex.js";
import "./tests/operations/StrUtils.js";
import "./tests/operations/SeqUtils.js";

View File

@@ -0,0 +1,59 @@
/**
* StrUtils tests.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import TestRegister from "../../TestRegister.js";
TestRegister.addTests([
{
name: "Regex: non-HTML op",
input: "/<>",
expectedOutput: "/<>",
recipeConfig: [
{
"op": "Regular expression",
"args": ["User defined", "", true, true, false, false, false, false, "Highlight matches"]
},
{
"op": "Remove whitespace",
"args": [true, true, true, true, true, false]
}
],
},
{
name: "Regex: Dot matches all",
input: "Hello\nWorld",
expectedOutput: "Hello\nWorld",
recipeConfig: [
{
"op": "Regular expression",
"args": ["User defined", ".+", true, true, true, false, false, false, "List matches"]
}
],
},
{
name: "Regex: Astral off",
input: "𝌆😆",
expectedOutput: "",
recipeConfig: [
{
"op": "Regular expression",
"args": ["User defined", "\\pS", true, true, false, false, false, false, "List matches"]
}
],
},
{
name: "Regex: Astral on",
input: "𝌆😆",
expectedOutput: "𝌆\n😆",
recipeConfig: [
{
"op": "Regular expression",
"args": ["User defined", "\\pS", true, true, false, false, true, false, "List matches"]
}
],
}
]);

View File

@@ -8,21 +8,6 @@
import TestRegister from "../../TestRegister.js";
TestRegister.addTests([
{
name: "Regex, non-HTML op",
input: "/<>",
expectedOutput: "/<>",
recipeConfig: [
{
"op": "Regular expression",
"args": ["User defined", "", true, true, false, "Highlight matches"]
},
{
"op": "Remove whitespace",
"args": [true, true, true, true, true, false]
}
],
},
{
name: "Diff, basic usage",
input: "testing23\n\ntesting123",