mirror of
https://github.com/gchq/CyberChef
synced 2025-12-05 23:53:27 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa6890432c | ||
|
|
192d0ed8a6 | ||
|
|
fff188eb30 | ||
|
|
71067939e3 | ||
|
|
b07c014b48 | ||
|
|
f2c073798b | ||
|
|
4cc38db895 | ||
|
|
2762319dbb | ||
|
|
8e5d43dfa8 | ||
|
|
82ad8cc444 | ||
|
|
1d6bf39548 | ||
|
|
ec02b7deda | ||
|
|
fa938f832f | ||
|
|
6f59d9217c | ||
|
|
7176e5ca6e | ||
|
|
429829471f | ||
|
|
4760e539b7 | ||
|
|
f53e7ad617 | ||
|
|
28c83fa921 | ||
|
|
4588cd151c | ||
|
|
2d9f87abef | ||
|
|
491e6f5f5f | ||
|
|
ab7c05284d | ||
|
|
0586fa0e01 | ||
|
|
53eba2337c | ||
|
|
283d3e1e7b | ||
|
|
7992a540ae | ||
|
|
3f3e7a78eb | ||
|
|
8d3d39acd3 | ||
|
|
7b20aba2ff | ||
|
|
bbfb732d8f | ||
|
|
566adbcda5 | ||
|
|
1bff490fa4 | ||
|
|
d38375a08c | ||
|
|
56551712d6 | ||
|
|
c241d2f90b | ||
|
|
0ba28dc891 |
21
README.md
21
README.md
@@ -27,7 +27,7 @@ Cryptographic operations in CyberChef should not be relied upon to provide secur
|
||||
|
||||
There are four main areas in CyberChef:
|
||||
|
||||
1. The **input** box in the top right, where you can paste, type or drag the data you want to operate on.
|
||||
1. The **input** box in the top right, where you can paste, type or drag the text or file you want to operate on.
|
||||
2. The **output** box in the bottom right, where the outcome of your processing will be displayed.
|
||||
3. The **operations** list on the far left, where you can find all the operations that CyberChef is capable of in categorised lists, or by searching.
|
||||
4. The **recipe** area in the middle, where you can drag the operations that you want to use and specify arguments and options.
|
||||
@@ -42,32 +42,32 @@ You can use as many operations as you like in simple or complex ways. Some examp
|
||||
- [Display multiple timestamps as full dates][7]
|
||||
- [Carry out different operations on data of different types][8]
|
||||
- [Use parts of the input as arguments to operations][9]
|
||||
- [Perform AES decryption, extracting the IV from the beginning of the cipher stream][10]
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- Drag and drop
|
||||
- Operations can be dragged in and out of the recipe list, or reorganised.
|
||||
- Files can be dragged over the input box to load them directly.
|
||||
- Files can be dragged over the input box to load them directly into the browser.
|
||||
- Auto Bake
|
||||
- Whenever you modify the input or the recipe, CyberChef will automatically “bake” for you and produce the output immediately.
|
||||
- Whenever you modify the input or the recipe, CyberChef will automatically "bake" for you and produce the output immediately.
|
||||
- This can be turned off and operated manually if it is affecting performance (if the input is very large, for instance).
|
||||
- If any bake takes longer than 200 milliseconds, auto bake will be switched off automatically to prevent further performance issues.
|
||||
- Breakpoints
|
||||
- You can set breakpoints on any operation in your recipe to pause execution before running it.
|
||||
- You can also step through the recipe one operation at a time to see what the data looks like at each stage.
|
||||
- Save and load recipes
|
||||
- If you come up with an awesome recipe that you know you’ll want to use again, just click save and add it to your local storage. It'll be waiting for you next time you visit CyberChef.
|
||||
- You can also copy a URL which includes your recipe and input which can be shared with others.
|
||||
- If you come up with an awesome recipe that you know you’ll want to use again, just click "Save recipe" and add it to your local storage. It'll be waiting for you next time you visit CyberChef.
|
||||
- You can also copy the URL, which includes your recipe and input, to easily share it with others.
|
||||
- Search
|
||||
- If you know the name of the operation you want or a word associated with it, start typing it into the search field and any matching operations will immediately be shown.
|
||||
- Highlighting
|
||||
- When you highlight text in the input or output, the offset and length values will be displayed and, if possible, the corresponding data will be highlighted in the output or input respectively (example: [highlight the word 'question' in the input to see where it appears in the output][10]).
|
||||
- When you highlight text in the input or output, the offset and length values will be displayed and, if possible, the corresponding data will be highlighted in the output or input respectively (example: [highlight the word 'question' in the input to see where it appears in the output][11]).
|
||||
- Save to file and load from file
|
||||
- You can save the output to a file at any time or load a file by dragging and dropping it into the input field. Files up to around 500MB are supported (depending on your browser), however some operations may take a very long time to run over this much data.
|
||||
- CyberChef is entirely client-side
|
||||
- It should be noted that none of your recipe configuration or input (either text or files) is ever sent to the CyberChef web server - all processing is carried out within your browser, on your own computer.
|
||||
- Due to this feature, CyberChef can be compiled into a single HTML file. You can download this file and drop it into a virtual machine, share it with other people, or use it independently on your desktop.
|
||||
- Due to this feature, CyberChef can be compiled into a single HTML file. You can download this file and drop it into a virtual machine, share it with other people, or use it independently on your local machine.
|
||||
|
||||
|
||||
## Browser support
|
||||
@@ -100,6 +100,7 @@ CyberChef is released under the [Apache 2.0 Licence](https://www.apache.org/lice
|
||||
[5]: https://gchq.github.io/CyberChef/#recipe=From_Hexdump()Gunzip()&input=MDAwMDAwMDAgIDFmIDhiIDA4IDAwIDEyIGJjIGYzIDU3IDAwIGZmIDBkIGM3IGMxIDA5IDAwIDIwICB8Li4uLi6881cu/y7HwS4uIHwKMDAwMDAwMTAgIDA4IDA1IGQwIDU1IGZlIDA0IDJkIGQzIDA0IDFmIGNhIDhjIDQ0IDIxIDViIGZmICB8Li7QVf4uLdMuLsouRCFb/3wKMDAwMDAwMjAgIDYwIGM3IGQ3IDAzIDE2IGJlIDQwIDFmIDc4IDRhIDNmIDA5IDg5IDBiIDlhIDdkICB8YMfXLi6%2BQC54Sj8uLi4ufXwKMDAwMDAwMzAgIDRlIGM4IDRlIDZkIDA1IDFlIDAxIDhiIDRjIDI0IDAwIDAwIDAwICAgICAgICAgICB8TshObS4uLi5MJC4uLnw
|
||||
[6]: https://gchq.github.io/CyberChef/#recipe=RC4(%7B'option':'UTF8','string':'secret'%7D,'Hex','Hex')Disassemble_x86('64','Full%20x86%20architecture',16,0,true,true)&input=MjFkZGQyNTQwMTYwZWU2NWZlMDc3NzEwM2YyYTM5ZmJlNWJjYjZhYTBhYWJkNDE0ZjkwYzZjYWY1MzEyNzU0YWY3NzRiNzZiM2JiY2QxOTNjYjNkZGZkYmM1YTI2NTMzYTY4NmI1OWI4ZmVkNGQzODBkNDc0NDIwMWFlYzIwNDA1MDcxMzhlMmZlMmIzOTUwNDQ2ZGIzMWQyYmM2MjliZTRkM2YyZWIwMDQzYzI5M2Q3YTVkMjk2MmMwMGZlNmRhMzAwNzJkOGM1YTZiNGZlN2Q4NTlhMDQwZWVhZjI5OTczMzYzMDJmNWEwZWMxOQ
|
||||
[7]: https://gchq.github.io/CyberChef/#recipe=Fork('%5C%5Cn','%5C%5Cn',false)From_UNIX_Timestamp('Seconds%20(s)')&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA
|
||||
[8]: https://gchq.github.ioeCyberChef/#recipe=Fork('%5C%5Cn','%5C%5Cn',false)Conditional_Jump('1',false,'base64',10)To_Hex('Space')Return()Label('base64')To_Base64('A-Za-z0-9%2B/%3D')&input=U29tZSBkYXRhIHdpdGggYSAxIGluIGl0ClNvbWUgZGF0YSB3aXRoIGEgMiBpbiBpdA
|
||||
[8]: https://gchq.github.io/CyberChef/#recipe=Fork('%5C%5Cn','%5C%5Cn',false)Conditional_Jump('1',false,'base64',10)To_Hex('Space')Return()Label('base64')To_Base64('A-Za-z0-9%2B/%3D')&input=U29tZSBkYXRhIHdpdGggYSAxIGluIGl0ClNvbWUgZGF0YSB3aXRoIGEgMiBpbiBpdA
|
||||
[9]: https://gchq.github.io/CyberChef/#recipe=Register('key%3D(%5B%5C%5Cda-f%5D*)',true,false)Find_/_Replace(%7B'option':'Regex','string':'.*data%3D(.*)'%7D,'$1',true,false,true)RC4(%7B'option':'Hex','string':'$R0'%7D,'Hex','Latin1')&input=aHR0cDovL21hbHdhcmV6LmJpei9iZWFjb24ucGhwP2tleT0wZTkzMmE1YyZkYXRhPThkYjdkNWViZTM4NjYzYTU0ZWNiYjMzNGUzZGIxMQ
|
||||
[10]: https://gchq.github.io/CyberChef/#recipe=XOR(%7B'option':'Hex','string':'3a'%7D,'',false)To_Hexdump(16,false,false)&input=VGhlIGFuc3dlciB0byB0aGUgdWx0aW1hdGUgcXVlc3Rpb24gb2YgbGlmZSwgdGhlIFVuaXZlcnNlLCBhbmQgZXZlcnl0aGluZyBpcyA0Mi4
|
||||
[10]: https://gchq.github.io/CyberChef/#recipe=Register('(.%7B32%7D)',true,false)Drop_bytes(0,32,false)AES_Decrypt(%7B'option':'Hex','string':'1748e7179bd56570d51fa4ba287cc3e5'%7D,%7B'option':'Hex','string':'$R0'%7D,'CTR','Hex','Raw',%7B'option':'Hex','string':''%7D)&input=NTFlMjAxZDQ2MzY5OGVmNWY3MTdmNzFmNWI0NzEyYWYyMGJlNjc0YjNiZmY1M2QzODU0NjM5NmVlNjFkYWFjNDkwOGUzMTljYTNmY2Y3MDg5YmZiNmIzOGVhOTllNzgxZDI2ZTU3N2JhOWRkNmYzMTFhMzk0MjBiODk3OGU5MzAxNGIwNDJkNDQ3MjZjYWVkZjU0MzZlYWY2NTI0MjljMGRmOTRiNTIxNjc2YzdjMmNlODEyMDk3YzI3NzI3M2M3YzcyY2Q4OWFlYzhkOWZiNGEyNzU4NmNjZjZhYTBhZWUyMjRjMzRiYTNiZmRmN2FlYjFkZGQ0Nzc2MjJiOTFlNzJjOWU3MDlhYjYwZjhkYWY3MzFlYzBjYzg1Y2UwZjc0NmZmMTU1NGE1YTNlYzI5MWNhNDBmOWU2MjlhODcyNTkyZDk4OGZkZDgzNDUzNGFiYTc5YzFhZDE2NzY3NjlhN2MwMTBiZjA0NzM5ZWNkYjY1ZDk1MzAyMzcxZDYyOWQ5ZTM3ZTdiNGEzNjFkYTQ2OGYxZWQ1MzU4OTIyZDJlYTc1MmRkMTFjMzY2ZjMwMTdiMTRhYTAxMWQyYWYwM2M0NGY5NTU3OTA5OGExNWUzY2Y5YjQ0ODZmOGZmZTljMjM5ZjM0ZGU3MTUxZjZjYTY1MDBmZTRiODUwYzNmMWMwMmU4MDFjYWYzYTI0NDY0NjE0ZTQyODAxNjE1YjhmZmFhMDdhYzgyNTE0OTNmZmRhN2RlNWRkZjMzNjg4ODBjMmI5NWIwMzBmNDFmOGYxNTA2NmFkZDA3MWE2NmNmNjBlNWY0NmYzYTIzMGQzOTdiNjUyOTYzYTIxYTUzZg
|
||||
[11]: https://gchq.github.io/CyberChef/#recipe=XOR(%7B'option':'Hex','string':'3a'%7D,'',false)To_Hexdump(16,false,false)&input=VGhlIGFuc3dlciB0byB0aGUgdWx0aW1hdGUgcXVlc3Rpb24gb2YgbGlmZSwgdGhlIFVuaXZlcnNlLCBhbmQgZXZlcnl0aGluZyBpcyA0Mi4
|
||||
|
||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "7.2.1",
|
||||
"version": "7.5.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -1053,6 +1053,11 @@
|
||||
"integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
|
||||
"dev": true
|
||||
},
|
||||
"bignumber.js": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-5.0.0.tgz",
|
||||
"integrity": "sha512-KWTu6ZMVk9sxlDJQh2YH1UOnfDP8O8TpxUxgQG/vKASoSnEjK9aVuOueFaPcQEYQ5fyNXNTOYwYw3099RYebWg=="
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
|
||||
@@ -10251,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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "7.2.1",
|
||||
"version": "7.5.1",
|
||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||
"author": "n1474335 <n1474335@gmail.com>",
|
||||
"homepage": "https://gchq.github.io/CyberChef",
|
||||
@@ -68,6 +68,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"bignumber.js": "^5.0.0",
|
||||
"bootstrap": "^3.3.7",
|
||||
"bootstrap-colorpicker": "^2.5.2",
|
||||
"bootstrap-switch": "^3.3.4",
|
||||
@@ -102,6 +103,7 @@
|
||||
"vkbeautify": "^0.99.3",
|
||||
"xmldom": "^0.1.27",
|
||||
"xpath": "0.0.27",
|
||||
"xregexp": "^4.0.0",
|
||||
"zlibjs": "^0.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -35,10 +35,11 @@ const Chef = function() {
|
||||
*/
|
||||
Chef.prototype.bake = async function(input, recipeConfig, options, progress, step) {
|
||||
log.debug("Chef baking");
|
||||
let startTime = new Date().getTime(),
|
||||
const startTime = new Date().getTime(),
|
||||
recipe = new Recipe(recipeConfig),
|
||||
containsFc = recipe.containsFlowControl(),
|
||||
error = false;
|
||||
notUTF8 = options && options.hasOwnProperty("treatAsUtf8") && !options.treatAsUtf8;
|
||||
let error = false;
|
||||
|
||||
if (containsFc && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
|
||||
|
||||
@@ -80,13 +81,13 @@ Chef.prototype.bake = async function(input, recipeConfig, options, progress, ste
|
||||
// Depending on the size of the output, we may send it back as a string or an ArrayBuffer.
|
||||
// This can prevent unnecessary casting as an ArrayBuffer can be easily downloaded as a file.
|
||||
// The threshold is specified in KiB.
|
||||
const threshold = (options.outputFileThreshold || 1024) * 1024;
|
||||
const threshold = (options.ioDisplayThreshold || 1024) * 1024;
|
||||
const returnType = this.dish.size() > threshold ? Dish.ARRAY_BUFFER : Dish.STRING;
|
||||
|
||||
return {
|
||||
result: this.dish.type === Dish.HTML ?
|
||||
this.dish.get(Dish.HTML) :
|
||||
this.dish.get(returnType),
|
||||
this.dish.get(Dish.HTML, notUTF8) :
|
||||
this.dish.get(returnType, notUTF8),
|
||||
type: Dish.enumLookup(this.dish.type),
|
||||
progress: progress,
|
||||
duration: new Date().getTime() - startTime,
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import Utils from "./Utils.js";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
/**
|
||||
* The data being operated on by each operation.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @author Matt C [matt@artemisbot.uk]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @class
|
||||
* @param {byteArray|string|number|ArrayBuffer} value - The value of the input data.
|
||||
* @param {byteArray|string|number|ArrayBuffer|BigNumber} value - The value of the input data.
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
*/
|
||||
const Dish = function(value, type) {
|
||||
@@ -47,6 +49,12 @@ Dish.HTML = 3;
|
||||
* @enum
|
||||
*/
|
||||
Dish.ARRAY_BUFFER = 4;
|
||||
/**
|
||||
* Dish data type enum for BigNumbers.
|
||||
* @readonly
|
||||
* @enum
|
||||
*/
|
||||
Dish.BIG_NUMBER = 5;
|
||||
|
||||
|
||||
/**
|
||||
@@ -57,22 +65,22 @@ Dish.ARRAY_BUFFER = 4;
|
||||
* @returns {number} The data type enum value.
|
||||
*/
|
||||
Dish.typeEnum = function(typeStr) {
|
||||
switch (typeStr) {
|
||||
case "byteArray":
|
||||
case "Byte array":
|
||||
switch (typeStr.toLowerCase()) {
|
||||
case "bytearray":
|
||||
case "byte array":
|
||||
return Dish.BYTE_ARRAY;
|
||||
case "string":
|
||||
case "String":
|
||||
return Dish.STRING;
|
||||
case "number":
|
||||
case "Number":
|
||||
return Dish.NUMBER;
|
||||
case "html":
|
||||
case "HTML":
|
||||
return Dish.HTML;
|
||||
case "arrayBuffer":
|
||||
case "ArrayBuffer":
|
||||
case "arraybuffer":
|
||||
case "array buffer":
|
||||
return Dish.ARRAY_BUFFER;
|
||||
case "bignumber":
|
||||
case "big number":
|
||||
return Dish.BIG_NUMBER;
|
||||
default:
|
||||
throw "Invalid data type string. No matching enum.";
|
||||
}
|
||||
@@ -83,8 +91,8 @@ Dish.typeEnum = function(typeStr) {
|
||||
* Returns the data type string for the given type enum.
|
||||
*
|
||||
* @static
|
||||
* @param {string} typeEnum - The enum value of the data type.
|
||||
* @returns {number} The data type as a string.
|
||||
* @param {number} typeEnum - The enum value of the data type.
|
||||
* @returns {string} The data type as a string.
|
||||
*/
|
||||
Dish.enumLookup = function(typeEnum) {
|
||||
switch (typeEnum) {
|
||||
@@ -98,6 +106,8 @@ Dish.enumLookup = function(typeEnum) {
|
||||
return "html";
|
||||
case Dish.ARRAY_BUFFER:
|
||||
return "ArrayBuffer";
|
||||
case Dish.BIG_NUMBER:
|
||||
return "BigNumber";
|
||||
default:
|
||||
throw "Invalid data type enum. No matching type.";
|
||||
}
|
||||
@@ -107,7 +117,7 @@ Dish.enumLookup = function(typeEnum) {
|
||||
/**
|
||||
* Sets the data value and type and then validates them.
|
||||
*
|
||||
* @param {byteArray|string|number|ArrayBuffer} value - The value of the input data.
|
||||
* @param {byteArray|string|number|ArrayBuffer|BigNumber} value - The value of the input data.
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
*/
|
||||
Dish.prototype.set = function(value, type) {
|
||||
@@ -126,11 +136,12 @@ Dish.prototype.set = function(value, type) {
|
||||
* Returns the value of the data in the type format specified.
|
||||
*
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
* @returns {byteArray|string|number|ArrayBuffer} The value of the output data.
|
||||
* @param {boolean} [notUTF8] - Do not treat strings as UTF8.
|
||||
* @returns {byteArray|string|number|ArrayBuffer|BigNumber} The value of the output data.
|
||||
*/
|
||||
Dish.prototype.get = function(type) {
|
||||
Dish.prototype.get = function(type, notUTF8) {
|
||||
if (this.type !== type) {
|
||||
this.translate(type);
|
||||
this.translate(type, notUTF8);
|
||||
}
|
||||
return this.value;
|
||||
};
|
||||
@@ -140,9 +151,11 @@ Dish.prototype.get = function(type) {
|
||||
* Translates the data to the given type format.
|
||||
*
|
||||
* @param {number} toType - The data type of value, see Dish enums.
|
||||
* @param {boolean} [notUTF8] - Do not treat strings as UTF8.
|
||||
*/
|
||||
Dish.prototype.translate = function(toType) {
|
||||
Dish.prototype.translate = function(toType, notUTF8) {
|
||||
log.debug(`Translating Dish from ${Dish.enumLookup(this.type)} to ${Dish.enumLookup(toType)}`);
|
||||
const byteArrayToStr = notUTF8 ? Utils.byteArrayToChars : Utils.byteArrayToUtf8;
|
||||
|
||||
// Convert data to intermediate byteArray type
|
||||
switch (this.type) {
|
||||
@@ -159,6 +172,9 @@ Dish.prototype.translate = function(toType) {
|
||||
// Array.from() would be nicer here, but it's slightly slower
|
||||
this.value = Array.prototype.slice.call(new Uint8Array(this.value));
|
||||
break;
|
||||
case Dish.BIG_NUMBER:
|
||||
this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toFixed()) : [];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -169,17 +185,25 @@ Dish.prototype.translate = function(toType) {
|
||||
switch (toType) {
|
||||
case Dish.STRING:
|
||||
case Dish.HTML:
|
||||
this.value = this.value ? Utils.byteArrayToUtf8(this.value) : "";
|
||||
this.value = this.value ? byteArrayToStr(this.value) : "";
|
||||
this.type = Dish.STRING;
|
||||
break;
|
||||
case Dish.NUMBER:
|
||||
this.value = this.value ? parseFloat(Utils.byteArrayToUtf8(this.value)) : 0;
|
||||
this.value = this.value ? parseFloat(byteArrayToStr(this.value)) : 0;
|
||||
this.type = Dish.NUMBER;
|
||||
break;
|
||||
case Dish.ARRAY_BUFFER:
|
||||
this.value = new Uint8Array(this.value).buffer;
|
||||
this.type = Dish.ARRAY_BUFFER;
|
||||
break;
|
||||
case Dish.BIG_NUMBER:
|
||||
try {
|
||||
this.value = new BigNumber(byteArrayToStr(this.value));
|
||||
} catch (err) {
|
||||
this.value = new BigNumber(NaN);
|
||||
}
|
||||
this.type = Dish.BIG_NUMBER;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -215,6 +239,8 @@ Dish.prototype.valid = function() {
|
||||
return typeof this.value === "number";
|
||||
case Dish.ARRAY_BUFFER:
|
||||
return this.value instanceof ArrayBuffer;
|
||||
case Dish.BIG_NUMBER:
|
||||
return this.value instanceof BigNumber;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -235,6 +261,7 @@ Dish.prototype.size = function() {
|
||||
case Dish.HTML:
|
||||
return this.value.length;
|
||||
case Dish.NUMBER:
|
||||
case Dish.BIG_NUMBER:
|
||||
return this.value.toString().length;
|
||||
case Dish.ARRAY_BUFFER:
|
||||
return this.value.byteLength;
|
||||
|
||||
@@ -201,7 +201,7 @@ const Utils = {
|
||||
* Utils.parseEscapedChars("\\n");
|
||||
*/
|
||||
parseEscapedChars: function(str) {
|
||||
return str.replace(/(\\)?\\([nrtbf]|x[\da-f]{2})/g, function(m, a, b) {
|
||||
return str.replace(/(\\)?\\([nrtbf]|x[\da-fA-F]{2})/g, function(m, a, b) {
|
||||
if (a === "\\") return "\\"+b;
|
||||
switch (b[0]) {
|
||||
case "n":
|
||||
|
||||
@@ -189,6 +189,7 @@ const Categories = [
|
||||
"Find / Replace",
|
||||
"Regular expression",
|
||||
"Offset checker",
|
||||
"Hamming Distance",
|
||||
"Convert distance",
|
||||
"Convert area",
|
||||
"Convert mass",
|
||||
|
||||
@@ -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";
|
||||
@@ -524,7 +525,7 @@ const OperationConfig = {
|
||||
module: "Default",
|
||||
description: "Adds together a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>18.5</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
@@ -537,7 +538,7 @@ const OperationConfig = {
|
||||
module: "Default",
|
||||
description: "Subtracts a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>1.5</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
@@ -550,7 +551,7 @@ const OperationConfig = {
|
||||
module: "Default",
|
||||
description: "Multiplies a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>40</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
@@ -563,7 +564,7 @@ const OperationConfig = {
|
||||
module: "Default",
|
||||
description: "Divides a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>2.5</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
@@ -576,7 +577,7 @@ const OperationConfig = {
|
||||
module: "Default",
|
||||
description: "Computes the mean (average) of a number list. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5 .5</code> becomes <code>4.75</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
@@ -589,7 +590,7 @@ const OperationConfig = {
|
||||
module: "Default",
|
||||
description: "Computes the median of a number list. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 1 .5</code> becomes <code>4.5</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
@@ -602,7 +603,7 @@ const OperationConfig = {
|
||||
module: "Default",
|
||||
description: "Computes the standard deviation of a number list. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>4.089281382128433</code>",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
@@ -806,7 +807,7 @@ const OperationConfig = {
|
||||
module: "Default",
|
||||
description: "Converts a number to decimal from a given numerical base.",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Radix",
|
||||
@@ -818,7 +819,7 @@ const OperationConfig = {
|
||||
"To Base": {
|
||||
module: "Default",
|
||||
description: "Converts a decimal number to a given numerical base.",
|
||||
inputType: "number",
|
||||
inputType: "BigNumber",
|
||||
outputType: "string",
|
||||
args: [
|
||||
{
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
]
|
||||
@@ -2138,7 +2138,6 @@ const OperationConfig = {
|
||||
"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: [
|
||||
@@ -2160,16 +2159,26 @@ const OperationConfig = {
|
||||
]
|
||||
},
|
||||
"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
|
||||
},
|
||||
]
|
||||
},
|
||||
@@ -2515,8 +2538,8 @@ const OperationConfig = {
|
||||
"Convert distance": {
|
||||
module: "Default",
|
||||
description: "Converts a unit of distance to another format.",
|
||||
inputType: "number",
|
||||
outputType: "number",
|
||||
inputType: "BigNumber",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Input units",
|
||||
@@ -2533,8 +2556,8 @@ const OperationConfig = {
|
||||
"Convert area": {
|
||||
module: "Default",
|
||||
description: "Converts a unit of area to another format.",
|
||||
inputType: "number",
|
||||
outputType: "number",
|
||||
inputType: "BigNumber",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Input units",
|
||||
@@ -2551,8 +2574,8 @@ const OperationConfig = {
|
||||
"Convert mass": {
|
||||
module: "Default",
|
||||
description: "Converts a unit of mass to another format.",
|
||||
inputType: "number",
|
||||
outputType: "number",
|
||||
inputType: "BigNumber",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Input units",
|
||||
@@ -2569,8 +2592,8 @@ const OperationConfig = {
|
||||
"Convert speed": {
|
||||
module: "Default",
|
||||
description: "Converts a unit of speed to another format.",
|
||||
inputType: "number",
|
||||
outputType: "number",
|
||||
inputType: "BigNumber",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Input units",
|
||||
@@ -2587,8 +2610,8 @@ const OperationConfig = {
|
||||
"Convert data units": {
|
||||
module: "Default",
|
||||
description: "Converts a unit of data to another format.",
|
||||
inputType: "number",
|
||||
outputType: "number",
|
||||
inputType: "BigNumber",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Input units",
|
||||
@@ -3478,14 +3501,14 @@ const OperationConfig = {
|
||||
},
|
||||
"Escape string": {
|
||||
module: "Default",
|
||||
description: "Escapes special characters in a string so that they do not cause conflicts. For example, <code>Don't stop me now</code> becomes <code>Don\\'t stop me now</code>.",
|
||||
description: "Escapes special characters in a string so that they do not cause conflicts. For example, <code>Don't stop me now</code> becomes <code>Don\\'t stop me now</code>.<br><br>Supports the following escape sequences:<ul><li><code>\\n</code> (Line feed/newline)</li><li><code>\\r</code> (Carriage return)</li><li><code>\\t</code> (Horizontal tab)</li><li><code>\\b</code> (Backspace)</li><li><code>\\f</code> (Form feed)</li><li><code>\\xnn</code> (Hex, where n is 0-f)</li><li><code>\\\\</code> (Backslash)</li><li><code>\\'</code> (Single quote)</li><li><code>\\"</code> (Double quote)</li></ul>",
|
||||
inputType: "string",
|
||||
outputType: "string",
|
||||
args: []
|
||||
},
|
||||
"Unescape string": {
|
||||
module: "Default",
|
||||
description: "Unescapes characters in a string that have been escaped. For example, <code>Don\\'t stop me now</code> becomes <code>Don't stop me now</code>.",
|
||||
description: "Unescapes characters in a string that have been escaped. For example, <code>Don\\'t stop me now</code> becomes <code>Don't stop me now</code>.<br><br>Supports the following escape sequences:<ul><li><code>\\n</code> (Line feed/newline)</li><li><code>\\r</code> (Carriage return)</li><li><code>\\t</code> (Horizontal tab)</li><li><code>\\b</code> (Backspace)</li><li><code>\\f</code> (Form feed)</li><li><code>\\xnn</code> (Hex, where n is 0-f)</li><li><code>\\\\</code> (Backslash)</li><li><code>\\'</code> (Single quote)</li><li><code>\\"</code> (Double quote)</li></ul>",
|
||||
inputType: "string",
|
||||
outputType: "string",
|
||||
args: []
|
||||
@@ -3750,7 +3773,7 @@ const OperationConfig = {
|
||||
module: "Default",
|
||||
description: "Binary-Coded Decimal (BCD) is a class of binary encodings of decimal numbers where each decimal digit is represented by a fixed number of bits, usually four or eight. Special bit patterns are sometimes used for a sign.",
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
outputType: "BigNumber",
|
||||
args: [
|
||||
{
|
||||
name: "Scheme",
|
||||
@@ -3778,7 +3801,7 @@ const OperationConfig = {
|
||||
"To BCD": {
|
||||
module: "Default",
|
||||
description: "Binary-Coded Decimal (BCD) is a class of binary encodings of decimal numbers where each decimal digit is represented by a fixed number of bits, usually four or eight. Special bit patterns are sometimes used for a sign",
|
||||
inputType: "number",
|
||||
inputType: "BigNumber",
|
||||
outputType: "string",
|
||||
args: [
|
||||
{
|
||||
@@ -3913,6 +3936,29 @@ const OperationConfig = {
|
||||
}
|
||||
]
|
||||
},
|
||||
"Hamming Distance": {
|
||||
module: "Default",
|
||||
description: "In information theory, the Hamming distance between two strings of equal length is the number of positions at which the corresponding symbols are different. In other words, it measures the minimum number of substitutions required to change one string into the other, or the minimum number of errors that could have transformed one string into the other. In a more general context, the Hamming distance is one of several string metrics for measuring the edit distance between two sequences.",
|
||||
inputType: "string",
|
||||
outputType: "string",
|
||||
args: [
|
||||
{
|
||||
name: "Delimiter",
|
||||
type: "binaryShortString",
|
||||
value: StrUtils.HAMMING_DELIM
|
||||
},
|
||||
{
|
||||
name: "Unit",
|
||||
type: "option",
|
||||
value: StrUtils.HAMMING_UNIT
|
||||
},
|
||||
{
|
||||
name: "Input type",
|
||||
type: "option",
|
||||
value: StrUtils.HAMMING_INPUT_TYPE
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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,17 +98,16 @@ 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,
|
||||
"Unescape string": StrUtils.runUnescape,
|
||||
"Head": StrUtils.runHead,
|
||||
"Tail": StrUtils.runTail,
|
||||
"Hamming Distance": StrUtils.runHamming,
|
||||
"Remove whitespace": Tidy.runRemoveWhitespace,
|
||||
"Remove null bytes": Tidy.runRemoveNulls,
|
||||
"Drop bytes": Tidy.runDropBytes,
|
||||
@@ -132,14 +130,6 @@ 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,
|
||||
"Microsoft Script Decoder": MS.runDecodeScript,
|
||||
"Entropy": Entropy.runEntropy,
|
||||
"Frequency distribution": Entropy.runFreqDistrib,
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
30
src/core/config/modules/Regex.js
Normal file
30
src/core/config/modules/Regex.js
Normal 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;
|
||||
@@ -1,4 +1,5 @@
|
||||
import Utils from "../Utils.js";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
|
||||
/**
|
||||
@@ -24,11 +25,11 @@ const Arithmetic = {
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runSum: function(input, args) {
|
||||
const val = Arithmetic._sum(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
@@ -37,11 +38,11 @@ const Arithmetic = {
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runSub: function(input, args) {
|
||||
let val = Arithmetic._sub(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
@@ -50,11 +51,11 @@ const Arithmetic = {
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runMulti: function(input, args) {
|
||||
let val = Arithmetic._multi(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
@@ -63,11 +64,11 @@ const Arithmetic = {
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runDiv: function(input, args) {
|
||||
let val = Arithmetic._div(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
@@ -76,11 +77,11 @@ const Arithmetic = {
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runMean: function(input, args) {
|
||||
let val = Arithmetic._mean(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
@@ -89,11 +90,11 @@ const Arithmetic = {
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runMedian: function(input, args) {
|
||||
let val = Arithmetic._median(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
@@ -102,11 +103,11 @@ const Arithmetic = {
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runStdDev: function(input, args) {
|
||||
let val = Arithmetic._stdDev(Arithmetic._createNumArray(input, args[0]));
|
||||
return typeof(val) === "number" ? val : NaN;
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
@@ -116,7 +117,7 @@ const Arithmetic = {
|
||||
* @private
|
||||
* @param {string[]} input
|
||||
* @param {string} delim
|
||||
* @returns {number[]}
|
||||
* @returns {BigNumber[]}
|
||||
*/
|
||||
_createNumArray: function(input, delim) {
|
||||
delim = Utils.charRep[delim || "Space"];
|
||||
@@ -125,13 +126,13 @@ const Arithmetic = {
|
||||
num;
|
||||
|
||||
for (let i = 0; i < splitNumbers.length; i++) {
|
||||
if (splitNumbers[i].indexOf(".") >= 0) {
|
||||
num = parseFloat(splitNumbers[i].trim());
|
||||
} else {
|
||||
num = parseInt(splitNumbers[i].trim(), 0);
|
||||
}
|
||||
if (!isNaN(num)) {
|
||||
numbers.push(num);
|
||||
try {
|
||||
num = BigNumber(splitNumbers[i].trim());
|
||||
if (!num.isNaN()) {
|
||||
numbers.push(num);
|
||||
}
|
||||
} catch (err) {
|
||||
// This line is not a valid number
|
||||
}
|
||||
}
|
||||
return numbers;
|
||||
@@ -142,12 +143,12 @@ const Arithmetic = {
|
||||
* Adds an array of numbers and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_sum: function(data) {
|
||||
if (data.length > 0) {
|
||||
return data.reduce((acc, curr) => acc + curr);
|
||||
return data.reduce((acc, curr) => acc.plus(curr));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -156,12 +157,12 @@ const Arithmetic = {
|
||||
* Subtracts an array of numbers and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_sub: function(data) {
|
||||
if (data.length > 0) {
|
||||
return data.reduce((acc, curr) => acc - curr);
|
||||
return data.reduce((acc, curr) => acc.minus(curr));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -170,12 +171,12 @@ const Arithmetic = {
|
||||
* Multiplies an array of numbers and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_multi: function(data) {
|
||||
if (data.length > 0) {
|
||||
return data.reduce((acc, curr) => acc * curr);
|
||||
return data.reduce((acc, curr) => acc.times(curr));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -184,12 +185,12 @@ const Arithmetic = {
|
||||
* Divides an array of numbers and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_div: function(data) {
|
||||
if (data.length > 0) {
|
||||
return data.reduce((acc, curr) => acc / curr);
|
||||
return data.reduce((acc, curr) => acc.div(curr));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -198,12 +199,12 @@ const Arithmetic = {
|
||||
* Computes mean of a number array and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_mean: function(data) {
|
||||
if (data.length > 0) {
|
||||
return Arithmetic._sum(data) / data.length;
|
||||
return Arithmetic._sum(data).div(data.length);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -212,14 +213,14 @@ const Arithmetic = {
|
||||
* Computes median of a number array and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_median: function (data) {
|
||||
if ((data.length % 2) === 0) {
|
||||
if ((data.length % 2) === 0 && data.length > 0) {
|
||||
let first, second;
|
||||
data.sort(function(a, b){
|
||||
return a - b;
|
||||
return a.minus(b);
|
||||
});
|
||||
first = data[Math.floor(data.length / 2)];
|
||||
second = data[Math.floor(data.length / 2) - 1];
|
||||
@@ -234,17 +235,17 @@ const Arithmetic = {
|
||||
* Computes standard deviation of a number array and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {number[]} data
|
||||
* @returns {number}
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_stdDev: function (data) {
|
||||
if (data.length > 0) {
|
||||
let avg = Arithmetic._mean(data);
|
||||
let devSum = 0;
|
||||
let devSum = new BigNumber(0);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
devSum += (data[i] - avg) ** 2;
|
||||
devSum = devSum.plus(data[i].minus(avg).pow(2));
|
||||
}
|
||||
return Math.sqrt(devSum / data.length);
|
||||
return devSum.div(data.length).sqrt();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Utils from "../Utils.js";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
|
||||
/**
|
||||
@@ -61,14 +62,14 @@ const BCD = {
|
||||
/**
|
||||
* To BCD operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runToBCD: function(input, args) {
|
||||
if (isNaN(input))
|
||||
if (input.isNaN())
|
||||
return "Invalid input";
|
||||
if (Math.floor(input) !== input)
|
||||
if (!input.floor().equals(input))
|
||||
return "Fractional values are not supported by BCD";
|
||||
|
||||
const encoding = BCD.ENCODING_LOOKUP[args[0]],
|
||||
@@ -77,7 +78,7 @@ const BCD = {
|
||||
outputFormat = args[3];
|
||||
|
||||
// Split input number up into separate digits
|
||||
const digits = input.toString().split("");
|
||||
const digits = input.toFixed().split("");
|
||||
|
||||
if (digits[0] === "-" || digits[0] === "+") {
|
||||
digits.shift();
|
||||
@@ -152,7 +153,7 @@ const BCD = {
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runFromBCD: function(input, args) {
|
||||
const encoding = BCD.ENCODING_LOOKUP[args[0]],
|
||||
@@ -206,7 +207,7 @@ const BCD = {
|
||||
output += val.toString();
|
||||
});
|
||||
|
||||
return parseInt(output, 10);
|
||||
return new BigNumber(output);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
/**
|
||||
* Numerical base operations.
|
||||
*
|
||||
@@ -18,7 +20,7 @@ const Base = {
|
||||
/**
|
||||
* To Base operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -39,7 +41,7 @@ const Base = {
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runFrom: function(input, args) {
|
||||
const radix = args[0] || Base.DEFAULT_RADIX;
|
||||
@@ -48,14 +50,14 @@ const Base = {
|
||||
}
|
||||
|
||||
let number = input.replace(/\s/g, "").split("."),
|
||||
result = parseInt(number[0], radix) || 0;
|
||||
result = new BigNumber(number[0], radix) || 0;
|
||||
|
||||
if (number.length === 1) return result;
|
||||
|
||||
// Fractional part
|
||||
for (let i = 0; i < number[1].length; i++) {
|
||||
const digit = parseInt(number[1][i], radix);
|
||||
result += digit / Math.pow(radix, i+1);
|
||||
const digit = new BigNumber(number[1][i], radix);
|
||||
result += digit.div(Math.pow(radix, i+1));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -2,6 +2,7 @@ import Utils from "../Utils.js";
|
||||
import CryptoJS from "crypto-js";
|
||||
import forge from "imports-loader?jQuery=>null!node-forge/dist/forge.min.js";
|
||||
import {blowfish as Blowfish} from "sladex-blowfish";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
|
||||
/**
|
||||
@@ -520,7 +521,11 @@ DES uses a key length of 8 bytes (64 bits).`;
|
||||
* @default
|
||||
*/
|
||||
PRNG_BYTES: 32,
|
||||
PRNG_OUTPUT: ["Hex", "Number", "Byte array", "Raw"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
PRNG_OUTPUT: ["Hex", "Integer", "Byte array", "Raw"],
|
||||
|
||||
/**
|
||||
* Pseudo-Random Number Generator operation.
|
||||
@@ -542,17 +547,17 @@ DES uses a key length of 8 bytes (64 bits).`;
|
||||
bytes = forge.random.getBytesSync(numBytes);
|
||||
}
|
||||
|
||||
let value = 0,
|
||||
let value = new BigNumber(0),
|
||||
i;
|
||||
|
||||
switch (outputAs) {
|
||||
case "Hex":
|
||||
return forge.util.bytesToHex(bytes);
|
||||
case "Number":
|
||||
case "Integer":
|
||||
for (i = bytes.length - 1; i >= 0; i--) {
|
||||
value = (value * 256) + bytes.charCodeAt(i);
|
||||
value = value.mul(256).plus(bytes.charCodeAt(i));
|
||||
}
|
||||
return value.toString();
|
||||
return value.toFixed();
|
||||
case "Byte array":
|
||||
return JSON.stringify(Utils.strToCharcode(bytes));
|
||||
case "Raw":
|
||||
|
||||
@@ -60,17 +60,16 @@ const Convert = {
|
||||
/**
|
||||
* Convert distance operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runDistance: function (input, args) {
|
||||
let inputUnits = args[0],
|
||||
outputUnits = args[1];
|
||||
|
||||
input = input * Convert.DISTANCE_FACTOR[inputUnits];
|
||||
return input / Convert.DISTANCE_FACTOR[outputUnits];
|
||||
// TODO Remove rounding errors (e.g. 1.000000000001)
|
||||
input = input.mul(Convert.DISTANCE_FACTOR[inputUnits]);
|
||||
return input.div(Convert.DISTANCE_FACTOR[outputUnits]);
|
||||
},
|
||||
|
||||
|
||||
@@ -141,16 +140,16 @@ const Convert = {
|
||||
/**
|
||||
* Convert data units operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runDataSize: function (input, args) {
|
||||
let inputUnits = args[0],
|
||||
outputUnits = args[1];
|
||||
|
||||
input = input * Convert.DATA_FACTOR[inputUnits];
|
||||
return input / Convert.DATA_FACTOR[outputUnits];
|
||||
input = input.mul(Convert.DATA_FACTOR[inputUnits]);
|
||||
return input.div(Convert.DATA_FACTOR[outputUnits]);
|
||||
},
|
||||
|
||||
|
||||
@@ -221,16 +220,16 @@ const Convert = {
|
||||
/**
|
||||
* Convert area operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runArea: function (input, args) {
|
||||
let inputUnits = args[0],
|
||||
outputUnits = args[1];
|
||||
|
||||
input = input * Convert.AREA_FACTOR[inputUnits];
|
||||
return input / Convert.AREA_FACTOR[outputUnits];
|
||||
input = input.mul(Convert.AREA_FACTOR[inputUnits]);
|
||||
return input.div(Convert.AREA_FACTOR[outputUnits]);
|
||||
},
|
||||
|
||||
|
||||
@@ -332,16 +331,16 @@ const Convert = {
|
||||
/**
|
||||
* Convert mass operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runMass: function (input, args) {
|
||||
let inputUnits = args[0],
|
||||
outputUnits = args[1];
|
||||
|
||||
input = input * Convert.MASS_FACTOR[inputUnits];
|
||||
return input / Convert.MASS_FACTOR[outputUnits];
|
||||
input = input.mul(Convert.MASS_FACTOR[inputUnits]);
|
||||
return input.div(Convert.MASS_FACTOR[outputUnits]);
|
||||
},
|
||||
|
||||
|
||||
@@ -397,16 +396,16 @@ const Convert = {
|
||||
/**
|
||||
* Convert speed operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runSpeed: function (input, args) {
|
||||
let inputUnits = args[0],
|
||||
outputUnits = args[1];
|
||||
|
||||
input = input * Convert.SPEED_FACTOR[inputUnits];
|
||||
return input / Convert.SPEED_FACTOR[outputUnits];
|
||||
input = input.mul(Convert.SPEED_FACTOR[inputUnits]);
|
||||
return input.div(Convert.SPEED_FACTOR[outputUnits]);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
278
src/core/operations/Regex.js
Normal file
278
src/core/operations/Regex.js
Normal 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;
|
||||
@@ -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
|
||||
@@ -183,62 +61,6 @@ const StrUtils = {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @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);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
@@ -510,80 +332,71 @@ const StrUtils = {
|
||||
|
||||
|
||||
/**
|
||||
* Adds HTML highlights to matches within a string.
|
||||
*
|
||||
* @private
|
||||
* @param {string} input
|
||||
* @param {RegExp} regex
|
||||
* @param {boolean} displayTotal
|
||||
* @returns {string}
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
_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;
|
||||
},
|
||||
|
||||
HAMMING_DELIM: "\\n\\n",
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
HAMMING_INPUT_TYPE: ["Raw string", "Hex"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
HAMMING_UNIT: ["Byte", "Bit"],
|
||||
|
||||
/**
|
||||
* Creates a string listing the matches within a string.
|
||||
* Hamming Distance operation.
|
||||
*
|
||||
* @author GCHQ Contributor [2]
|
||||
*
|
||||
* @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
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
_regexList: function(input, regex, displayTotal, matches, captureGroups) {
|
||||
let output = "",
|
||||
total = 0,
|
||||
match;
|
||||
runHamming: function(input, args) {
|
||||
const delim = args[0],
|
||||
byByte = args[1] === "Byte",
|
||||
inputType = args[2],
|
||||
samples = input.split(delim);
|
||||
|
||||
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 (samples.length !== 2) {
|
||||
return "Error: You can only calculae the edit distance between 2 strings. Please ensure exactly two inputs are provided, separated by the specified delimiter.";
|
||||
}
|
||||
|
||||
if (samples[0].length !== samples[1].length) {
|
||||
return "Error: Both inputs must be of the same length.";
|
||||
}
|
||||
|
||||
if (inputType === "Hex") {
|
||||
samples[0] = Utils.fromHex(samples[0]);
|
||||
samples[1] = Utils.fromHex(samples[1]);
|
||||
} else {
|
||||
samples[0] = Utils.strToByteArray(samples[0]);
|
||||
samples[1] = Utils.strToByteArray(samples[1]);
|
||||
}
|
||||
|
||||
let dist = 0;
|
||||
|
||||
for (let i = 0; i < samples[0].length; i++) {
|
||||
const lhs = samples[0][i],
|
||||
rhs = samples[1][i];
|
||||
|
||||
if (byByte && lhs !== rhs) {
|
||||
dist++;
|
||||
} else if (!byByte) {
|
||||
let xord = lhs ^ rhs;
|
||||
|
||||
while (xord) {
|
||||
dist++;
|
||||
xord &= xord - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displayTotal)
|
||||
output = "Total found: " + total + "\n\n" + output;
|
||||
|
||||
return output;
|
||||
return dist.toString();
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -61,9 +61,13 @@ InputWaiter.prototype.set = function(input) {
|
||||
if (input instanceof File) {
|
||||
this.setFile(input);
|
||||
inputText.value = "";
|
||||
this.setInputInfo(input.size, null);
|
||||
} else {
|
||||
inputText.value = input;
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
const lines = input.length < (this.app.options.ioDisplayThreshold * 1024) ?
|
||||
input.count("\n") + 1 : null;
|
||||
this.setInputInfo(input.length, lines);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -81,6 +85,7 @@ InputWaiter.prototype.setFile = function(file) {
|
||||
fileType = document.getElementById("input-file-type"),
|
||||
fileLoaded = document.getElementById("input-file-loaded");
|
||||
|
||||
this.fileBuffer = new ArrayBuffer();
|
||||
fileOverlay.style.display = "block";
|
||||
fileName.textContent = file.name;
|
||||
fileSize.textContent = file.size.toLocaleString() + " bytes";
|
||||
@@ -100,21 +105,28 @@ InputWaiter.prototype.setInputInfo = function(length, lines) {
|
||||
width = width < 2 ? 2 : width;
|
||||
|
||||
const lengthStr = length.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
const linesStr = lines.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
let msg = "length: " + lengthStr;
|
||||
|
||||
document.getElementById("input-info").innerHTML = "length: " + lengthStr + "<br>lines: " + linesStr;
|
||||
if (typeof lines === "number") {
|
||||
const linesStr = lines.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
msg += "<br>lines: " + linesStr;
|
||||
}
|
||||
|
||||
document.getElementById("input-info").innerHTML = msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for input scroll events.
|
||||
* Scrolls the highlighter pane to match the input textarea position and updates history state.
|
||||
* Handler for input change events.
|
||||
*
|
||||
* @param {event} e
|
||||
*
|
||||
* @fires Manager#statechange
|
||||
*/
|
||||
InputWaiter.prototype.inputChange = function(e) {
|
||||
// Ignore this function if the input is a File
|
||||
if (this.fileBuffer) return;
|
||||
|
||||
// Remove highlighting from input and output panes as the offsets might be different now
|
||||
this.manager.highlighter.removeHighlights();
|
||||
|
||||
@@ -123,18 +135,47 @@ InputWaiter.prototype.inputChange = function(e) {
|
||||
|
||||
// Update the input metadata info
|
||||
const inputText = this.get();
|
||||
const lines = inputText.count("\n") + 1;
|
||||
const lines = inputText.length < (this.app.options.ioDisplayThreshold * 1024) ?
|
||||
inputText.count("\n") + 1 : null;
|
||||
|
||||
this.setInputInfo(inputText.length, lines);
|
||||
|
||||
|
||||
if (this.badKeys.indexOf(e.keyCode) < 0) {
|
||||
if (e && this.badKeys.indexOf(e.keyCode) < 0) {
|
||||
// Fire the statechange event as the input has been modified
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for input paste events.
|
||||
* Checks that the size of the input is below the display limit, otherwise treats it as a file/blob.
|
||||
*
|
||||
* @param {event} e
|
||||
*/
|
||||
InputWaiter.prototype.inputPaste = function(e) {
|
||||
const pastedData = e.clipboardData.getData("Text");
|
||||
|
||||
if (pastedData.length < (this.app.options.ioDisplayThreshold * 1024)) {
|
||||
this.inputChange(e);
|
||||
} else {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const file = new File([pastedData], "PastedData", {
|
||||
type: "text/plain",
|
||||
lastModified: Date.now()
|
||||
});
|
||||
|
||||
this.loaderWorker = new LoaderWorker();
|
||||
this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this));
|
||||
this.loaderWorker.postMessage({"file": file});
|
||||
this.set(file);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for input dragover events.
|
||||
* Gives the user a visual cue to show that items can be dropped here.
|
||||
@@ -214,6 +255,10 @@ InputWaiter.prototype.handleLoaderMessage = function(e) {
|
||||
fileLoaded.textContent = r.progress + "%";
|
||||
}
|
||||
|
||||
if (r.hasOwnProperty("error")) {
|
||||
this.app.alert(r.error, "danger", 10000);
|
||||
}
|
||||
|
||||
if (r.hasOwnProperty("fileBuffer")) {
|
||||
log.debug("Input file loaded");
|
||||
this.fileBuffer = r.fileBuffer;
|
||||
|
||||
@@ -46,5 +46,9 @@ self.loadFile = function(file) {
|
||||
seek();
|
||||
};
|
||||
|
||||
reader.onerror = function(e) {
|
||||
self.postMessage({"error": file.error.message});
|
||||
};
|
||||
|
||||
seek();
|
||||
};
|
||||
|
||||
@@ -132,7 +132,8 @@ Manager.prototype.initialiseEventListeners = function() {
|
||||
this.addDynamicListener("#rec-list", "operationremove", this.recipe.opRemove.bind(this.recipe));
|
||||
|
||||
// Input
|
||||
this.addMultiEventListener("#input-text", "keyup paste", this.input.inputChange, this.input);
|
||||
this.addMultiEventListener("#input-text", "keyup", this.input.inputChange, this.input);
|
||||
this.addMultiEventListener("#input-text", "paste", this.input.inputPaste, this.input);
|
||||
document.getElementById("reset-layout").addEventListener("click", this.app.resetLayout.bind(this.app));
|
||||
document.getElementById("clr-io").addEventListener("click", this.input.clearIoClick.bind(this.input));
|
||||
this.addListeners("#input-text,#input-file", "dragover", this.input.inputDragover, this.input);
|
||||
|
||||
@@ -59,6 +59,9 @@
|
||||
"for i in range(additional): Pylon()",
|
||||
"(creating unresolved tension...",
|
||||
"Symlinking emacs and vim to ed...",
|
||||
"Training branch predictor...",
|
||||
"Timing cache hits...",
|
||||
"Speculatively executing recipes..."
|
||||
];
|
||||
|
||||
// Shuffle array using Durstenfeld algorithm
|
||||
@@ -115,7 +118,7 @@
|
||||
<span id="notice">
|
||||
<script type="text/javascript">
|
||||
// Must be text/javascript rather than application/javascript otherwise IE won't recognise it...
|
||||
if (navigator.userAgent && navigator.userAgent.match(/MSIE \d\d?\./)) {
|
||||
if (navigator.userAgent && navigator.userAgent.match(/Trident/)) {
|
||||
document.write("Internet Explorer is not supported, please use Firefox or Chrome instead");
|
||||
alert("Internet Explorer is not supported, please use Firefox or Chrome instead");
|
||||
}
|
||||
@@ -369,8 +372,8 @@
|
||||
<label for="errorTimeout"> Operation error timeout in ms (0 for never)</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="number" option="outputFileThreshold" id="outputFileThreshold" />
|
||||
<label for="outputFileThreshold"> Size threshold for treating the output as a file (KiB)</label>
|
||||
<input type="number" option="ioDisplayThreshold" id="ioDisplayThreshold" />
|
||||
<label for="ioDisplayThreshold"> Size threshold for treating the input and output as a file (KiB)</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<select option="logLevel" id="logLevel">
|
||||
|
||||
@@ -47,7 +47,7 @@ function main() {
|
||||
attemptHighlight: true,
|
||||
theme: "classic",
|
||||
useMetaKey: false,
|
||||
outputFileThreshold: 1024,
|
||||
ioDisplayThreshold: 512,
|
||||
logLevel: "info"
|
||||
};
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ TestRegister.addTests([
|
||||
},
|
||||
{
|
||||
name: "Fork, (expect) Error, Merge",
|
||||
input: "1\n2\na\n4",
|
||||
input: "1.1\n2.5\na\n3.4",
|
||||
expectedError: true,
|
||||
recipeConfig: [
|
||||
{
|
||||
@@ -45,8 +45,8 @@ TestRegister.addTests([
|
||||
args: ["\n", "\n", false],
|
||||
},
|
||||
{
|
||||
op: "To Base",
|
||||
args: [16],
|
||||
op: "Object Identifier to Hex",
|
||||
args: [],
|
||||
},
|
||||
{
|
||||
op: "Merge",
|
||||
|
||||
59
test/tests/operations/Regex.js
Normal file
59
test/tests/operations/Regex.js
Normal 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"]
|
||||
}
|
||||
],
|
||||
}
|
||||
]);
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user