Compare commits

...
43 Commits
Author SHA1 Message Date
n1474335 32455cd20f 9.27.4 2021-02-22 19:13:47 +00:00
n1474335 1e0e7f16a7 Added numeric validation for arguments in Binary and Hex operattions. Fixes #1178 2021-02-22 19:13:38 +00:00
n1474335 95884d77cf Extractable file formats are now listed properly in the 'Extract Files' description 2021-02-17 15:01:42 +00:00
n1474335 b69373f5e7 Fixed 'JSON to CSV' data flattening. 2021-02-16 14:48:56 +00:00
n1474335 61e85474d3 9.27.3 2021-02-16 14:36:36 +00:00
n1474335 3a9bdc58af Fixed 'JSON to CSV' handling of complex structures. Closes #637 2021-02-16 14:36:31 +00:00
n1474335 59c1c45d78 Updated dependencies 2021-02-16 14:17:09 +00:00
n1474335 b5f6cedd30 9.27.2 2021-02-16 14:12:18 +00:00
n1474335 c879af6860 Fixed 'Save recipe' URL generation issue. Closes #1176 2021-02-16 14:12:14 +00:00
n1474335 22fe5a6ae7 9.27.1 2021-02-12 17:55:36 +00:00
n1474335 57714c86a6 Escape HTML input in Fuzzy Match operation 2021-02-12 17:55:28 +00:00
n1474335 70cd375049 9.27.0 2021-02-12 13:54:52 +00:00
n1474335 e27e1dd42f Updated CHANGELOG 2021-02-12 13:53:59 +00:00
n1474335 8ad18bc7db Added 'Fuzzy Match' operation 2021-02-12 13:51:51 +00:00
n1474335 5893ac1a37 9.26.3 2021-02-12 12:12:08 +00:00
n1474335 83c3ab97f9 Merge branch 'n1073645-base64Alphabets' 2021-02-12 12:11:53 +00:00
n1474335 9b6be140fa Merge branch 'base64Alphabets' of https://github.com/n1073645/CyberChef into n1073645-base64Alphabets 2021-02-12 12:08:56 +00:00
n1474335 a6a60392c2 9.26.2 2021-02-12 12:05:03 +00:00
n1474335 ccfa0b991e Updated dependencies 2021-02-12 12:04:59 +00:00
n1474335 73b0e68993 Added code quality badge to README 2021-02-12 11:54:54 +00:00
n1474335 31a4eef001 9.26.1 2021-02-11 19:06:58 +00:00
n1474335 d6e2c9a6b9 Merge branch 'n1073645-HexdumpAsciiFix' 2021-02-11 19:06:47 +00:00
n1474335 e069f5db13 Tidied up hexdump UNIX format 2021-02-11 19:06:35 +00:00
n1474335 96b59cf0df Merge branch 'HexdumpAsciiFix' of https://github.com/n1073645/CyberChef into n1073645-HexdumpAsciiFix 2021-02-11 18:59:51 +00:00
n1474335 c1e1d4b7e3 9.26.0 2021-02-11 18:50:09 +00:00
n1474335 32d869231e Updated CHANGELOG 2021-02-11 18:50:03 +00:00
n1474335 6f95f01dda Merge branch 'n1073645-EPOCH' 2021-02-11 18:47:59 +00:00
n1474335 61a1c44f26 Renamed 'Generate Current Epoch' to 'Get Time'. It now uses the W3C High Resolution Time API and supports microsecond granularity 2021-02-11 18:47:44 +00:00
n1474335 e6c7899569 Merge branch 'EPOCH' of https://github.com/n1073645/CyberChef into n1073645-EPOCH 2021-02-11 18:08:55 +00:00
n1474335 a74a14145e 9.25.0 2021-02-11 18:03:45 +00:00
n1474335 04022b22be Updated CHANGELOG 2021-02-11 18:03:40 +00:00
n1474335 4f3010691c Merge branch 'n1073645-ID3Tags' 2021-02-11 18:01:24 +00:00
n1474335 672b477751 Extract ID3 operation now returns a JSON blob and presents an HTML table 2021-02-11 18:01:08 +00:00
n1474335 19360391a6 Merge branch 'ID3Tags' of https://github.com/n1073645/CyberChef into n1073645-ID3Tags 2021-02-11 16:16:33 +00:00
n1073645 7526f4d7b1 Generate Epoch Time Operation Added 2020-06-01 13:47:51 +01:00
n1073645 cd8a85975c Info and description 2020-04-02 15:59:58 +01:00
n1073645 2f94ec20b0 Added to categories 2020-04-02 15:58:03 +01:00
n1073645 09d9deae43 ID3 Extractor Added. 2020-04-02 15:43:55 +01:00
n1073645 bda36e508a Regexes for magic for the new alphabets 2020-03-27 13:27:56 +00:00
n1073645 d2ea1273da Merge remote-tracking branch 'upstream/master' into base64Alphabets 2020-03-27 13:09:03 +00:00
n1073645 30bc8dfbe9 UNIX Format Added for ToHexdump 2020-03-13 10:38:37 +00:00
n1073645 53a579028c Added only ASCII flag to ToHexdump 2020-03-12 09:30:48 +00:00
n1073645 3a2580fbc2 Extra Base64 Alphabets 2020-01-22 10:35:11 +00:00
26 changed files with 2722 additions and 752 deletions

No files matched your search

+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
- name: Install
run: |
npm install
export NODE_OPTIONS=--max_old_space_size=2048
npm run setheapsize
- name: Lint
run: npx grunt lint
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
- name: Install
run: |
npm install
export NODE_OPTIONS=--max_old_space_size=2048
npm run setheapsize
- name: Lint
run: npx grunt lint
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
- name: Install
run: |
npm install
export NODE_OPTIONS=--max_old_space_size=2048
npm run setheapsize
- name: Lint
run: npx grunt lint
+16
View File
@@ -13,6 +13,15 @@ All major and minor version changes will be documented in this file. Details of
## Details
### [9.27.0] - 2021-02-12
- 'Fuzzy Match' operation added [@n1474335] | [8ad18b]
### [9.26.0] - 2021-02-11
- 'Get Time' operation added [@n1073645] [@n1474335] | [#1045]
### [9.25.0] - 2021-02-11
- 'Extract ID3' operation added [@n1073645] [@n1474335] | [#1006]
### [9.24.0] - 2021-02-02
- 'SM3' hashing function added along with more configuration options for other hashing operations [@n1073645] [@n1474335] | [#1022]
@@ -247,6 +256,9 @@ All major and minor version changes will be documented in this file. Details of
[9.27.0]: https://github.com/gchq/CyberChef/releases/tag/v9.27.0
[9.26.0]: https://github.com/gchq/CyberChef/releases/tag/v9.26.0
[9.25.0]: https://github.com/gchq/CyberChef/releases/tag/v9.25.0
[9.24.0]: https://github.com/gchq/CyberChef/releases/tag/v9.24.0
[9.23.0]: https://github.com/gchq/CyberChef/releases/tag/v9.23.0
[9.22.0]: https://github.com/gchq/CyberChef/releases/tag/v9.22.0
@@ -352,6 +364,8 @@ All major and minor version changes will be documented in this file. Details of
[@dmfj]: https://github.com/dmfj
[@mattnotmitt]: https://github.com/mattnotmitt
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7
[#95]: https://github.com/gchq/CyberChef/pull/299
[#173]: https://github.com/gchq/CyberChef/pull/173
[#143]: https://github.com/gchq/CyberChef/pull/143
@@ -424,6 +438,8 @@ All major and minor version changes will be documented in this file. Details of
[#965]: https://github.com/gchq/CyberChef/pull/965
[#966]: https://github.com/gchq/CyberChef/pull/966
[#987]: https://github.com/gchq/CyberChef/pull/987
[#1006]: https://github.com/gchq/CyberChef/pull/1006
[#1022]: https://github.com/gchq/CyberChef/pull/1022
[#1045]: https://github.com/gchq/CyberChef/pull/1045
[#1049]: https://github.com/gchq/CyberChef/pull/1049
[#1083]: https://github.com/gchq/CyberChef/pull/1083
+2 -1
View File
@@ -1,6 +1,7 @@
# CyberChef
[![Build Status](https://travis-ci.org/gchq/CyberChef.svg?branch=master)](https://travis-ci.org/gchq/CyberChef)
[![](https://github.com/gchq/CyberChef/workflows/Master%20Build,%20Test%20&%20Deploy/badge.svg)](https://github.com/gchq/CyberChef/actions?query=workflow%3A%22Master+Build%2C+Test+%26+Deploy%22)
[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/gchq/CyberChef.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/gchq/CyberChef/context:javascript)
[![dependencies Status](https://david-dm.org/gchq/CyberChef/status.svg)](https://david-dm.org/gchq/CyberChef)
[![npm](https://img.shields.io/npm/v/cyberchef.svg)](https://www.npmjs.com/package/cyberchef)
[![](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/gchq/CyberChef/blob/master/LICENSE)
+2003 -667
View File
File diff suppressed because it is too large. Load diff
+27 -25
View File
@@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "9.24.8",
"version": "9.27.4",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef",
@@ -36,9 +36,9 @@
"node >= 10"
],
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/core": "^7.12.16",
"@babel/plugin-transform-runtime": "^7.12.15",
"@babel/preset-env": "^7.12.16",
"autoprefixer": "^10.2.4",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.2.2",
@@ -47,8 +47,8 @@
"cli-progress": "^3.9.0",
"colors": "^1.4.0",
"copy-webpack-plugin": "^7.0.0",
"css-loader": "^5.0.1",
"eslint": "^7.19.0",
"css-loader": "^5.0.2",
"eslint": "^7.20.0",
"exports-loader": "^2.0.0",
"file-loader": "^6.2.0",
"grunt": "^1.3.0",
@@ -62,30 +62,30 @@
"grunt-exec": "~3.0.0",
"grunt-webpack": "^4.0.2",
"grunt-zip": "^0.18.2",
"html-webpack-plugin": "^4.5.1",
"html-webpack-plugin": "^5.1.0",
"imports-loader": "^2.0.0",
"mini-css-extract-plugin": "^1.3.5",
"mini-css-extract-plugin": "^1.3.7",
"nightwatch": "^1.5.1",
"node-sass": "^5.0.0",
"postcss": "^8.2.4",
"postcss": "^8.2.6",
"postcss-css-variables": "^0.17.0",
"postcss-import": "^14.0.0",
"postcss-loader": "^4.2.0",
"postcss-loader": "^5.0.0",
"prompt": "^1.1.0",
"sass-loader": "^10.1.1",
"sitemap": "^6.3.5",
"sass-loader": "^11.0.1",
"sitemap": "^6.3.6",
"style-loader": "^2.0.0",
"svg-url-loader": "^7.1.1",
"url-loader": "^4.1.1",
"webpack": "^5.19.0",
"webpack": "^5.22.0",
"webpack-bundle-analyzer": "^4.4.0",
"webpack-dev-server": "^3.11.2",
"webpack-node-externals": "^2.5.2",
"worker-loader": "^3.0.7"
"worker-loader": "^3.0.8"
},
"dependencies": {
"@babel/polyfill": "^7.12.1",
"@babel/runtime": "^7.12.5",
"@babel/runtime": "^7.12.13",
"arrive": "^2.4.1",
"avsc": "^5.5.3",
"babel-plugin-transform-builtin-extend": "1.1.2",
@@ -115,8 +115,8 @@
"exif-parser": "^0.1.12",
"file-saver": "^2.0.5",
"flat": "^5.0.2",
"geodesy": "^1.1.3",
"highlight.js": "^10.5.0",
"geodesy": "1.1.3",
"highlight.js": "^10.6.0",
"jimp": "^0.16.1",
"jquery": "3.5.1",
"js-crc": "^0.2.0",
@@ -125,7 +125,7 @@
"jsonpath": "^1.1.0",
"jsonwebtoken": "^8.5.1",
"jsqr": "^1.3.1",
"jsrsasign": "10.1.5",
"jsrsasign": "^10.1.10",
"kbpgp": "2.1.15",
"libbzip2-wasm": "0.0.4",
"libyara-wasm": "^1.1.0",
@@ -134,14 +134,14 @@
"loglevel-message-prefix": "^3.0.0",
"markdown-it": "^12.0.4",
"moment": "^2.29.1",
"moment-timezone": "^0.5.32",
"moment-timezone": "^0.5.33",
"ngeohash": "^0.6.3",
"node-forge": "^0.10.0",
"node-md6": "^0.1.0",
"nodom": "^2.4.0",
"notepack.io": "^2.3.0",
"nwmatcher": "^1.4.4",
"otp": "^0.1.3",
"otp": "0.1.3",
"path": "^0.12.7",
"popper.js": "^1.16.1",
"process": "^0.11.10",
@@ -152,15 +152,15 @@
"split.js": "^1.6.2",
"ssdeep.js": "0.0.2",
"stream-browserify": "^3.0.0",
"terser": "^5.5.1",
"tesseract.js": "^2.1.1",
"ua-parser-js": "^0.7.23",
"terser": "^5.6.0",
"tesseract.js": "2.1.1",
"ua-parser-js": "^0.7.24",
"unorm": "^1.6.0",
"utf8": "^3.0.0",
"vkbeautify": "^0.99.3",
"xmldom": "^0.4.0",
"xpath": "0.0.32",
"xregexp": "^4.4.1",
"xregexp": "^5.0.1",
"zlibjs": "^0.3.1"
},
"scripts": {
@@ -173,6 +173,8 @@
"testuidev": "npx nightwatch --env=dev",
"lint": "npx grunt lint",
"postinstall": "npx grunt exec:fixCryptoApiImports",
"newop": "node --experimental-modules src/core/config/scripts/newOperation.mjs"
"newop": "node --experimental-modules src/core/config/scripts/newOperation.mjs",
"getheapsize": "node -e 'console.log(`node heap limit = ${require(\"v8\").getHeapStatistics().heap_size_limit / (1024 * 1024)} Mb`)'",
"setheapsize": "export NODE_OPTIONS=--max_old_space_size=2048"
}
}
+8 -3
View File
@@ -170,13 +170,18 @@ class Utils {
*
* @param {string} str - The input string to display.
* @param {boolean} [preserveWs=false] - Whether or not to print whitespace.
* @param {boolean} [onlyAscii=false] - Whether or not to replace non ASCII characters.
* @returns {string}
*/
static printable(str, preserveWs=false) {
static printable(str, preserveWs=false, onlyAscii=false) {
if (isWebEnvironment() && window.app && !window.app.options.treatAsUtf8) {
str = Utils.byteArrayToChars(Utils.strToByteArray(str));
}
if (onlyAscii) {
return str.replace(/[^\x20-\x7f]/g, ".");
}
// eslint-disable-next-line no-misleading-character-class
const re = /[\0-\x08\x0B-\x0C\x0E-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uD7FF\uE000-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/g;
const wsRe = /[\x09-\x10\x0D\u2028\u2029]/g;
@@ -890,8 +895,8 @@ class Utils {
while ((m = recipeRegex.exec(recipe))) {
// Translate strings in args back to double-quotes
args = m[2]
.replace(/"/g, '\\"') // Escape double quotes lgtm [js/incomplete-sanitization]
args = m[2] // lgtm [js/incomplete-sanitization]
.replace(/"/g, '\\"') // Escape double quotes
.replace(/(^|,|{|:)'/g, '$1"') // Replace opening ' with "
.replace(/([^\\]|(?:\\\\)+)'(,|:|}|$)/g, '$1"$2') // Replace closing ' with "
.replace(/\\'/g, "'"); // Unescape single quotes
+3
View File
@@ -238,6 +238,7 @@
"Pad lines",
"Find / Replace",
"Regular expression",
"Fuzzy Match",
"Offset checker",
"Hamming Distance",
"Convert distance",
@@ -267,6 +268,7 @@
"Windows Filetime to UNIX Timestamp",
"UNIX Timestamp to Windows Filetime",
"Extract dates",
"Get Time",
"Sleep"
]
},
@@ -286,6 +288,7 @@
"JPath expression",
"CSS selector",
"Extract EXIF",
"Extract ID3",
"Extract Files"
]
},
+4
View File
@@ -148,4 +148,8 @@ export const ALPHABET_OPTIONS = [
{name: "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r", value: "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"},
{name: "ROT13: N-ZA-Mn-za-m0-9+/=", value: "N-ZA-Mn-za-m0-9+/="},
{name: "UNIX crypt: ./0-9A-Za-z", value: "./0-9A-Za-z"},
{name: "Atom128: /128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", value: "/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC"},
{name: "Megan35: 3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", value: "3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5"},
{name: "Zong22: ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", value: "ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2"},
{name: "Hazz15: HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", value: "HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5"}
];
+4
View File
@@ -7,6 +7,7 @@
*/
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
@@ -58,6 +59,9 @@ export function toBinary(data, delim="Space", padding=8) {
* fromBinary("00010000:00100000:00110000", "Colon");
*/
export function fromBinary(data, delim="Space", byteLen=8) {
if (byteLen < 1 || Math.round(byteLen) !== byteLen)
throw new OperationError("Byte length must be a positive integer");
const delimRegex = Utils.regexRep(delim);
data = data.replace(delimRegex, "");
@@ -16,40 +16,72 @@
* Anurag Awasthi - updated to 0.2.0
*/
const SEQUENTIAL_BONUS = 15; // bonus for adjacent matches
const SEPARATOR_BONUS = 30; // bonus if match occurs after a separator
const CAMEL_BONUS = 30; // bonus if match is uppercase and prev is lower
const FIRST_LETTER_BONUS = 15; // bonus if the first letter is matched
export const DEFAULT_WEIGHTS = {
sequentialBonus: 15, // bonus for adjacent matches
separatorBonus: 30, // bonus if match occurs after a separator
camelBonus: 30, // bonus if match is uppercase and prev is lower
firstLetterBonus: 15, // bonus if the first letter is matched
const LEADING_LETTER_PENALTY = -5; // penalty applied for every letter in str before the first match
const MAX_LEADING_LETTER_PENALTY = -15; // maximum penalty for leading letters
const UNMATCHED_LETTER_PENALTY = -1;
leadingLetterPenalty: -5, // penalty applied for every letter in str before the first match
maxLeadingLetterPenalty: -15, // maximum penalty for leading letters
unmatchedLetterPenalty: -1
};
/**
* Does a fuzzy search to find pattern inside a string.
* @param {*} pattern string pattern to search for
* @param {*} str string string which is being searched
* @param {string} pattern pattern to search for
* @param {string} str string which is being searched
* @param {boolean} global whether to search for all matches or just one
* @returns [boolean, number] a boolean which tells if pattern was
* found or not and a search score
*/
export function fuzzyMatch(pattern, str) {
export function fuzzyMatch(pattern, str, global=false, weights=DEFAULT_WEIGHTS) {
const recursionCount = 0;
const recursionLimit = 10;
const matches = [];
const maxMatches = 256;
return fuzzyMatchRecursive(
pattern,
str,
0 /* patternCurIndex */,
0 /* strCurrIndex */,
null /* srcMatces */,
matches,
maxMatches,
0 /* nextMatch */,
recursionCount,
recursionLimit
);
if (!global) {
return fuzzyMatchRecursive(
pattern,
str,
0 /* patternCurIndex */,
0 /* strCurrIndex */,
null /* srcMatches */,
matches,
maxMatches,
0 /* nextMatch */,
recursionCount,
recursionLimit,
weights
);
}
// Return all matches
let foundMatch = true,
score,
idxs,
strCurrIndex = 0;
const results = [];
while (foundMatch) {
[foundMatch, score, idxs] = fuzzyMatchRecursive(
pattern,
str,
0 /* patternCurIndex */,
strCurrIndex,
null /* srcMatches */,
matches,
maxMatches,
0 /* nextMatch */,
recursionCount,
recursionLimit,
weights
);
if (foundMatch) results.push([foundMatch, score, [...idxs]]);
strCurrIndex = idxs[idxs.length - 1] + 1;
}
return results;
}
/**
@@ -65,7 +97,8 @@ function fuzzyMatchRecursive(
maxMatches,
nextMatch,
recursionCount,
recursionLimit
recursionLimit,
weights
) {
let outScore = 0;
@@ -110,7 +143,8 @@ function fuzzyMatchRecursive(
maxMatches,
nextMatch,
recursionCount,
recursionLimit
recursionLimit,
weights
);
if (matched) {
@@ -134,16 +168,16 @@ function fuzzyMatchRecursive(
outScore = 100;
// Apply leading letter penalty
let penalty = LEADING_LETTER_PENALTY * matches[0];
let penalty = weights.leadingLetterPenalty * matches[0];
penalty =
penalty < MAX_LEADING_LETTER_PENALTY ?
MAX_LEADING_LETTER_PENALTY :
penalty < weights.maxLeadingLetterPenalty ?
weights.maxLeadingLetterPenalty :
penalty;
outScore += penalty;
// Apply unmatched penalty
const unmatched = str.length - nextMatch;
outScore += UNMATCHED_LETTER_PENALTY * unmatched;
outScore += weights.unmatchedLetterPenalty * unmatched;
// Apply ordering bonuses
for (let i = 0; i < nextMatch; i++) {
@@ -152,7 +186,7 @@ function fuzzyMatchRecursive(
if (i > 0) {
const prevIdx = matches[i - 1];
if (currIdx === prevIdx + 1) {
outScore += SEQUENTIAL_BONUS;
outScore += weights.sequentialBonus;
}
}
@@ -165,15 +199,15 @@ function fuzzyMatchRecursive(
neighbor !== neighbor.toUpperCase() &&
curr !== curr.toLowerCase()
) {
outScore += CAMEL_BONUS;
outScore += weights.camelBonus;
}
const isNeighbourSeparator = neighbor === "_" || neighbor === " ";
if (isNeighbourSeparator) {
outScore += SEPARATOR_BONUS;
outScore += weights.separatorBonus;
}
} else {
// First letter
outScore += FIRST_LETTER_BONUS;
outScore += weights.firstLetterBonus;
}
}
+4
View File
@@ -7,6 +7,7 @@
*/
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
@@ -100,6 +101,9 @@ export function toHexFast(data) {
* fromHex("0a:14:1e", "Colon");
*/
export function fromHex(data, delim="Auto", byteLen=2) {
if (byteLen < 1 || Math.round(byteLen) !== byteLen)
throw new OperationError("Byte length must be a positive integer");
if (delim !== "None") {
const delimRegex = delim === "Auto" ? /[^a-f\d]|(0x)/gi : Utils.regexRep(delim);
data = data.replace(delimRegex, "");
+2 -2
View File
@@ -15,7 +15,7 @@ import { toHex, fromHex } from "./Hex.mjs";
* @param {number} indent
* @returns {string}
*/
export function formatDnStr (dnStr, indent) {
export function formatDnStr(dnStr, indent) {
const fields = dnStr.substr(1).replace(/([^\\])\//g, "$1$1/").split(/[^\\]\//);
let output = "",
maxKeyLen = 0,
@@ -54,7 +54,7 @@ export function formatDnStr (dnStr, indent) {
* @param {number} indent
* @returns {string}
*/
export function formatByteStr (byteStr, length, indent) {
export function formatByteStr(byteStr, length, indent) {
byteStr = toHex(fromHex(byteStr), ":");
length = length * 3;
let output = "";
+16 -1
View File
@@ -21,9 +21,24 @@ class ExtractFiles extends Operation {
constructor() {
super();
// Get the first extension for each signature that can be extracted
let supportedExts = Object.keys(FILE_SIGNATURES).map(cat => {
return FILE_SIGNATURES[cat]
.filter(sig => sig.extractor)
.map(sig => sig.extension.toUpperCase());
});
// Flatten categories and remove duplicates
supportedExts = [].concat(...supportedExts).unique();
this.name = "Extract Files";
this.module = "Default";
this.description = "Performs file carving to attempt to extract files from the input.<br><br>This operation is currently capable of carving out the following formats:<ul><li>JPG</li><li>EXE</li><li>ZIP</li><li>PDF</li><li>PNG</li><li>BMP</li><li>FLV</li><li>RTF</li><li>DOCX, PPTX, XLSX</li><li>EPUB</li><li>GZIP</li><li>ZLIB</li><li>ELF, BIN, AXF, O, PRX, SO</li></ul>";
this.description = `Performs file carving to attempt to extract files from the input.<br><br>This operation is currently capable of carving out the following formats:
<ul>
<li>
${supportedExts.join("</li><li>")}
</li>
</ul>`;
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=File_Carving";
this.inputType = "ArrayBuffer";
this.outputType = "List<File>";
+324
View File
@@ -0,0 +1,324 @@
/**
* @author n1073645 [n1073645@gmail.com]
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2020
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
/**
* Extract ID3 operation
*/
class ExtractID3 extends Operation {
/**
* ExtractID3 constructor
*/
constructor() {
super();
this.name = "Extract ID3";
this.module = "Default";
this.description = "This operation extracts ID3 metadata from an MP3 file.<br><br>ID3 is a metadata container most often used in conjunction with the MP3 audio file format. It allows information such as the title, artist, album, track number, and other information about the file to be stored in the file itself.";
this.infoURL = "https://wikipedia.org/wiki/ID3";
this.inputType = "ArrayBuffer";
this.outputType = "JSON";
this.presentType = "html";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {JSON}
*/
run(input, args) {
input = new Uint8Array(input);
/**
* Extracts the ID3 header fields.
*/
function extractHeader() {
if (!Array.from(input.slice(0, 3)).equals([0x49, 0x44, 0x33]))
throw new OperationError("No valid ID3 header.");
const header = {
"Type": "ID3",
// Tag version
"Version": input[3].toString() + "." + input[4].toString(),
// Header version
"Flags": input[5].toString()
};
input = input.slice(6);
return header;
}
/**
* Converts the size fields to a single integer.
*
* @param {number} num
* @returns {string}
*/
function readSize(num) {
let result = 0;
// The sizes are 7 bit numbers stored in 8 bit locations
for (let i = (num) * 7; i; i -= 7) {
result = (result << i) | input[0];
input = input.slice(1);
}
return result;
}
/**
* Reads frame header based on ID.
*
* @param {string} id
* @returns {number}
*/
function readFrame(id) {
const frame = {};
// Size of frame
const size = readSize(4);
frame.Size = size.toString();
frame.Description = FRAME_DESCRIPTIONS[id];
input = input.slice(2);
// Read data from frame
let data = "";
for (let i = 1; i < size; i++)
data += String.fromCharCode(input[i]);
frame.Data = data;
// Move to next Frame
input = input.slice(size);
return [frame, size];
}
const result = extractHeader();
const headerTagSize = readSize(4);
result.Size = headerTagSize.toString();
const tags = {};
let pos = 10;
// While the current element is in the header
while (pos < headerTagSize) {
// Frame Identifier of frame
let id = String.fromCharCode(input[0]) + String.fromCharCode(input[1]) + String.fromCharCode(input[2]);
input = input.slice(3);
// If the next character is non-zero it is an identifier
if (input[0] !== 0) {
id += String.fromCharCode(input[0]);
}
input = input.slice(1);
if (id in FRAME_DESCRIPTIONS) {
const [frame, size] = readFrame(id);
tags[id] = frame;
pos += 10 + size;
} else if (id === "\x00\x00\x00") { // end of header
break;
} else {
throw new OperationError("Unknown Frame Identifier: " + id);
}
}
result.Tags = tags;
return result;
}
/**
* Displays the extracted data in a more accessible format for web apps.
* @param {JSON} data
* @returns {html}
*/
present(data) {
if (!data || !Object.prototype.hasOwnProperty.call(data, "Tags"))
return JSON.stringify(data, null, 4);
let output = `<table class="table table-hover table-sm table-bordered table-nonfluid">
<tr><th>Tag</th><th>Description</th><th>Data</th></tr>`;
for (const tagID in data.Tags) {
const description = data.Tags[tagID].Description,
contents = data.Tags[tagID].Data;
output += `<tr><td>${tagID}</td><td>${Utils.escapeHtml(description)}</td><td>${Utils.escapeHtml(contents)}</td></tr>`;
}
output += "</table>";
return output;
}
}
// Borrowed from https://github.com/aadsm/jsmediatags
const FRAME_DESCRIPTIONS = {
// v2.2
"BUF": "Recommended buffer size",
"CNT": "Play counter",
"COM": "Comments",
"CRA": "Audio encryption",
"CRM": "Encrypted meta frame",
"ETC": "Event timing codes",
"EQU": "Equalization",
"GEO": "General encapsulated object",
"IPL": "Involved people list",
"LNK": "Linked information",
"MCI": "Music CD Identifier",
"MLL": "MPEG location lookup table",
"PIC": "Attached picture",
"POP": "Popularimeter",
"REV": "Reverb",
"RVA": "Relative volume adjustment",
"SLT": "Synchronized lyric/text",
"STC": "Synced tempo codes",
"TAL": "Album/Movie/Show title",
"TBP": "BPM (Beats Per Minute)",
"TCM": "Composer",
"TCO": "Content type",
"TCR": "Copyright message",
"TDA": "Date",
"TDY": "Playlist delay",
"TEN": "Encoded by",
"TFT": "File type",
"TIM": "Time",
"TKE": "Initial key",
"TLA": "Language(s)",
"TLE": "Length",
"TMT": "Media type",
"TOA": "Original artist(s)/performer(s)",
"TOF": "Original filename",
"TOL": "Original Lyricist(s)/text writer(s)",
"TOR": "Original release year",
"TOT": "Original album/Movie/Show title",
"TP1": "Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group",
"TP2": "Band/Orchestra/Accompaniment",
"TP3": "Conductor/Performer refinement",
"TP4": "Interpreted, remixed, or otherwise modified by",
"TPA": "Part of a set",
"TPB": "Publisher",
"TRC": "ISRC (International Standard Recording Code)",
"TRD": "Recording dates",
"TRK": "Track number/Position in set",
"TSI": "Size",
"TSS": "Software/hardware and settings used for encoding",
"TT1": "Content group description",
"TT2": "Title/Songname/Content description",
"TT3": "Subtitle/Description refinement",
"TXT": "Lyricist/text writer",
"TXX": "User defined text information frame",
"TYE": "Year",
"UFI": "Unique file identifier",
"ULT": "Unsychronized lyric/text transcription",
"WAF": "Official audio file webpage",
"WAR": "Official artist/performer webpage",
"WAS": "Official audio source webpage",
"WCM": "Commercial information",
"WCP": "Copyright/Legal information",
"WPB": "Publishers official webpage",
"WXX": "User defined URL link frame",
// v2.3
"AENC": "Audio encryption",
"APIC": "Attached picture",
"ASPI": "Audio seek point index",
"CHAP": "Chapter",
"CTOC": "Table of contents",
"COMM": "Comments",
"COMR": "Commercial frame",
"ENCR": "Encryption method registration",
"EQU2": "Equalisation (2)",
"EQUA": "Equalization",
"ETCO": "Event timing codes",
"GEOB": "General encapsulated object",
"GRID": "Group identification registration",
"IPLS": "Involved people list",
"LINK": "Linked information",
"MCDI": "Music CD identifier",
"MLLT": "MPEG location lookup table",
"OWNE": "Ownership frame",
"PRIV": "Private frame",
"PCNT": "Play counter",
"POPM": "Popularimeter",
"POSS": "Position synchronisation frame",
"RBUF": "Recommended buffer size",
"RVA2": "Relative volume adjustment (2)",
"RVAD": "Relative volume adjustment",
"RVRB": "Reverb",
"SEEK": "Seek frame",
"SYLT": "Synchronized lyric/text",
"SYTC": "Synchronized tempo codes",
"TALB": "Album/Movie/Show title",
"TBPM": "BPM (beats per minute)",
"TCOM": "Composer",
"TCON": "Content type",
"TCOP": "Copyright message",
"TDAT": "Date",
"TDLY": "Playlist delay",
"TDRC": "Recording time",
"TDRL": "Release time",
"TDTG": "Tagging time",
"TENC": "Encoded by",
"TEXT": "Lyricist/Text writer",
"TFLT": "File type",
"TIME": "Time",
"TIPL": "Involved people list",
"TIT1": "Content group description",
"TIT2": "Title/songname/content description",
"TIT3": "Subtitle/Description refinement",
"TKEY": "Initial key",
"TLAN": "Language(s)",
"TLEN": "Length",
"TMCL": "Musician credits list",
"TMED": "Media type",
"TMOO": "Mood",
"TOAL": "Original album/movie/show title",
"TOFN": "Original filename",
"TOLY": "Original lyricist(s)/text writer(s)",
"TOPE": "Original artist(s)/performer(s)",
"TORY": "Original release year",
"TOWN": "File owner/licensee",
"TPE1": "Lead performer(s)/Soloist(s)",
"TPE2": "Band/orchestra/accompaniment",
"TPE3": "Conductor/performer refinement",
"TPE4": "Interpreted, remixed, or otherwise modified by",
"TPOS": "Part of a set",
"TPRO": "Produced notice",
"TPUB": "Publisher",
"TRCK": "Track number/Position in set",
"TRDA": "Recording dates",
"TRSN": "Internet radio station name",
"TRSO": "Internet radio station owner",
"TSOA": "Album sort order",
"TSOP": "Performer sort order",
"TSOT": "Title sort order",
"TSIZ": "Size",
"TSRC": "ISRC (international standard recording code)",
"TSSE": "Software/Hardware and settings used for encoding",
"TSST": "Set subtitle",
"TYER": "Year",
"TXXX": "User defined text information frame",
"UFID": "Unique file identifier",
"USER": "Terms of use",
"USLT": "Unsychronized lyric/text transcription",
"WCOM": "Commercial information",
"WCOP": "Copyright/Legal information",
"WOAF": "Official audio file webpage",
"WOAR": "Official artist/performer webpage",
"WOAS": "Official audio source webpage",
"WORS": "Official internet radio station homepage",
"WPAY": "Payment",
"WPUB": "Publishers official webpage",
"WXXX": "User defined URL link frame"
};
export default ExtractID3;
+20
View File
@@ -102,6 +102,26 @@ class FromBase64 extends Operation {
flags: "i",
args: ["./0-9A-Za-z", true]
},
{
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}CC|[A-Z=\\d\\+/]{3}C)?\\s*$",
flags: "i",
args: ["/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", true]
},
{
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
flags: "i",
args: ["3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", true]
},
{
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}22|[A-Z=\\d\\+/]{3}2)?\\s*$",
flags: "i",
args: ["ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", true]
},
{
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
flags: "i",
args: ["HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", true]
}
];
}
+2 -1
View File
@@ -35,7 +35,8 @@ class FromBinary extends Operation {
{
"name": "Byte Length",
"type": "number",
"value": 8
"value": 8,
"min": 1
}
];
this.checks = [
+121
View File
@@ -0,0 +1,121 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import {fuzzyMatch, calcMatchRanges, DEFAULT_WEIGHTS} from "../lib/FuzzyMatch.mjs";
import Utils from "../Utils.mjs";
/**
* Fuzzy Match operation
*/
class FuzzyMatch extends Operation {
/**
* FuzzyMatch constructor
*/
constructor() {
super();
this.name = "Fuzzy Match";
this.module = "Default";
this.description = "Conducts a fuzzy search to find a pattern within the input based on weighted criteria.<br><br>e.g. A search for <code>dpan</code> will match on <code><b>D</b>on't <b>Pan</b>ic</code>";
this.infoURL = "https://wikipedia.org/wiki/Fuzzy_matching_(computer-assisted_translation)";
this.inputType = "string";
this.outputType = "html";
this.args = [
{
name: "Search",
type: "binaryString",
value: ""
},
{
name: "Sequential bonus",
type: "number",
value: DEFAULT_WEIGHTS.sequentialBonus,
hint: "Bonus for adjacent matches"
},
{
name: "Separator bonus",
type: "number",
value: DEFAULT_WEIGHTS.separatorBonus,
hint: "Bonus if match occurs after a separator"
},
{
name: "Camel bonus",
type: "number",
value: DEFAULT_WEIGHTS.camelBonus,
hint: "Bonus if match is uppercase and previous is lower"
},
{
name: "First letter bonus",
type: "number",
value: DEFAULT_WEIGHTS.firstLetterBonus,
hint: "Bonus if the first letter is matched"
},
{
name: "Leading letter penalty",
type: "number",
value: DEFAULT_WEIGHTS.leadingLetterPenalty,
hint: "Penalty applied for every letter in the input before the first match"
},
{
name: "Max leading letter penalty",
type: "number",
value: DEFAULT_WEIGHTS.maxLeadingLetterPenalty,
hint: "Maxiumum penalty for leading letters"
},
{
name: "Unmatched letter penalty",
type: "number",
value: DEFAULT_WEIGHTS.unmatchedLetterPenalty
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
run(input, args) {
const searchStr = args[0];
const weights = {
sequentialBonus: args[1],
separatorBonus: args[2],
camelBonus: args[3],
firstLetterBonus: args[4],
leadingLetterPenalty: args[5],
maxLeadingLetterPenalty: args[6],
unmatchedLetterPenalty: args[7]
};
const matches = fuzzyMatch(searchStr, input, true, weights);
if (!matches) {
return "No matches.";
}
let result = "", pos = 0, hlClass = "hl1";
matches.forEach(([matches, score, idxs]) => {
const matchRanges = calcMatchRanges(idxs);
matchRanges.forEach(([start, length], i) => {
result += Utils.escapeHtml(input.slice(pos, start));
if (i === 0) result += `<span class="${hlClass}">`;
pos = start + length;
result += `<b>${Utils.escapeHtml(input.slice(start, pos))}</b>`;
});
result += "</span>";
hlClass = hlClass === "hl1" ? "hl2" : "hl1";
});
result += Utils.escapeHtml(input.slice(pos, input.length));
return result;
}
}
export default FuzzyMatch;
+63
View File
@@ -0,0 +1,63 @@
/**
* @author n1073645 [n1073645@gmail.com]
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2020
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import {UNITS} from "../lib/DateTime.mjs";
/**
* Get Time operation
*/
class GetTime extends Operation {
/**
* GetTime constructor
*/
constructor() {
super();
this.name = "Get Time";
this.module = "Default";
this.description = "Generates a timestamp showing the amount of time since the UNIX epoch (1970-01-01 00:00:00 UTC). Uses the W3C High Resolution Time API.";
this.infoURL = "https://wikipedia.org/wiki/Unix_time";
this.inputType = "string";
this.outputType = "number";
this.args = [
{
name: "Granularity",
type: "option",
value: UNITS
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {number}
*/
run(input, args) {
const nowMs = (performance.timeOrigin + performance.now()),
granularity = args[0];
switch (granularity) {
case "Nanoseconds (ns)":
return Math.round(nowMs * 1000 * 1000);
case "Microseconds (μs)":
return Math.round(nowMs * 1000);
case "Milliseconds (ms)":
return Math.round(nowMs);
case "Seconds (s)":
return Math.round(nowMs / 1000);
default:
throw new OperationError("Unknown granularity value: " + granularity);
}
}
}
export default GetTime;
+10 -7
View File
@@ -43,15 +43,16 @@ class JSONToCSV extends Operation {
/**
* Converts JSON to a CSV equivalent.
*
* @param {boolean} force - Whether to force conversion of data to fit in a cell
* @returns {string}
*/
toCSV() {
toCSV(force=false) {
const self = this;
// If the JSON is an array of arrays, this is easy
if (this.flattened[0] instanceof Array) {
return this.flattened
.map(row => row
.map(self.escapeCellContents.bind(self))
.map(d => self.escapeCellContents(d, force))
.join(this.cellDelim)
)
.join(this.rowDelim) +
@@ -61,13 +62,13 @@ class JSONToCSV extends Operation {
// If it's an array of dictionaries...
const header = Object.keys(this.flattened[0]);
return header
.map(self.escapeCellContents.bind(self))
.map(d => self.escapeCellContents(d, force))
.join(this.cellDelim) +
this.rowDelim +
this.flattened
.map(row => header
.map(h => row[h])
.map(self.escapeCellContents.bind(self))
.map(d => self.escapeCellContents(d, force))
.join(this.cellDelim)
)
.join(this.rowDelim) +
@@ -98,7 +99,7 @@ class JSONToCSV extends Operation {
if (!(this.flattened instanceof Array)) {
this.flattened = [this.flattened];
}
return this.toCSV();
return this.toCSV(true);
} catch (err) {
throw new OperationError("Unable to parse JSON to CSV: " + err.toString());
}
@@ -109,15 +110,17 @@ class JSONToCSV extends Operation {
* Correctly escapes a cell's contents based on the cell and row delimiters.
*
* @param {string} data
* @param {boolean} force - Whether to force conversion of data to fit in a cell
* @returns {string}
*/
escapeCellContents(data) {
escapeCellContents(data, force=false) {
if (typeof data === "number") data = data.toString();
if (force && typeof data !== "string") data = JSON.stringify(data);
// Double quotes should be doubled up
data = data.replace(/"/g, '""');
// If the cell contains a cell or row delimiter or a double quote, it mut be enclosed in double quotes
// If the cell contains a cell or row delimiter or a double quote, it must be enclosed in double quotes
if (
data.indexOf(this.cellDelim) >= 0 ||
data.indexOf(this.rowDelim) >= 0 ||
+2 -2
View File
@@ -185,7 +185,7 @@ class RegularExpression extends Operation {
* @param {boolean} captureGroups - Display each of the capture groups separately
* @returns {string}
*/
function regexList (input, regex, displayTotal, matches, captureGroups) {
function regexList(input, regex, displayTotal, matches, captureGroups) {
let output = "",
total = 0,
match;
@@ -225,7 +225,7 @@ function regexList (input, regex, displayTotal, matches, captureGroups) {
* @param {boolean} displayTotal
* @returns {string}
*/
function regexHighlight (input, regex, displayTotal) {
function regexHighlight(input, regex, displayTotal) {
let output = "",
title = "",
hl = 1,
+16 -4
View File
@@ -6,6 +6,7 @@
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* To Hexdump operation
@@ -20,7 +21,7 @@ class ToHexdump extends Operation {
this.name = "To Hexdump";
this.module = "Default";
this.description = "Creates a hexdump of the input data, displaying both the hexadecimal values of each byte and an ASCII representation alongside.";
this.description = "Creates a hexdump of the input data, displaying both the hexadecimal values of each byte and an ASCII representation alongside.<br><br>The 'UNIX format' argument defines which subset of printable characters are displayed in the preview column.";
this.infoURL = "https://wikipedia.org/wiki/Hex_dump";
this.inputType = "ArrayBuffer";
this.outputType = "string";
@@ -28,7 +29,8 @@ class ToHexdump extends Operation {
{
"name": "Width",
"type": "number",
"value": 16
"value": 16,
"min": 1
},
{
"name": "Upper case hex",
@@ -39,6 +41,11 @@ class ToHexdump extends Operation {
"name": "Include final length",
"type": "boolean",
"value": false
},
{
"name": "UNIX format",
"type": "boolean",
"value": false
}
];
}
@@ -50,9 +57,12 @@ class ToHexdump extends Operation {
*/
run(input, args) {
const data = new Uint8Array(input);
const [length, upperCase, includeFinalLength] = args;
const [length, upperCase, includeFinalLength, unixFormat] = args;
const padding = 2;
if (length < 1 || Math.round(length) !== length)
throw new OperationError("Width must be a positive integer");
let output = "";
for (let i = 0; i < data.length; i += length) {
const buff = data.slice(i, i+length);
@@ -70,7 +80,9 @@ class ToHexdump extends Operation {
output += lineNo + " " +
hexa.padEnd(length*(padding+1), " ") +
" |" + Utils.printable(Utils.byteArrayToChars(buff)).padEnd(buff.length, " ") + "|\n";
" |" +
Utils.printable(Utils.byteArrayToChars(buff), false, unixFormat).padEnd(buff.length, " ") +
"|\n";
if (includeFinalLength && i+buff.length === data.length) {
output += Utils.hex(i+buff.length, 8) + "\n";
+1 -1
View File
@@ -725,7 +725,7 @@ class App {
this.progress = 0;
this.autoBake();
this.updateTitle(false, null, true);
this.updateTitle(true, null, true);
}
+4 -2
View File
@@ -100,7 +100,7 @@ class ControlsWaiter {
const includeRecipe = document.getElementById("save-link-recipe-checkbox").checked;
const includeInput = document.getElementById("save-link-input-checkbox").checked;
const saveLinkEl = document.getElementById("save-link");
const saveLink = this.generateStateUrl(includeRecipe, includeInput, recipeConfig);
const saveLink = this.generateStateUrl(includeRecipe, includeInput, null, recipeConfig);
saveLinkEl.innerHTML = Utils.escapeHtml(Utils.truncate(saveLink, 120));
saveLinkEl.setAttribute("href", saveLink);
@@ -128,11 +128,13 @@ class ControlsWaiter {
includeRecipe = includeRecipe && (recipeConfig.length > 0);
// If we don't get passed an input, get it from the current URI
if (input === null) {
if (input === null && includeInput) {
const params = this.app.getURIParams();
if (params.input) {
includeInput = true;
input = params.input;
} else {
includeInput = false;
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
import HTMLOperation from "../HTMLOperation.mjs";
import Sortable from "sortablejs";
import {fuzzyMatch, calcMatchRanges} from "../../core/lib/FuzzySearch.mjs";
import {fuzzyMatch, calcMatchRanges} from "../../core/lib/FuzzyMatch.mjs";
/**