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

Compare commits

..

209 Commits

Author SHA1 Message Date
n1474335
6fbb2f26d1 7.5.2 2018-01-18 15:27:26 +00:00
n1474335
c91bfeaa81 Merge branch 'qistoph-SplitAndJoin' 2018-01-18 15:26:34 +00:00
n1474335
aa2b3b2843 Changed order of split delimiters, placing comma first. 2018-01-18 15:26:09 +00:00
Chris van Marle
90d8be48d4 Make Split more flexible so it can be used to join 2018-01-17 15:52:25 +01:00
n1474335
aa6890432c 7.5.1 2018-01-12 23:59:03 +00:00
n1474335
192d0ed8a6 Merge branch 'feature-unicode-strings' 2018-01-12 23:57:20 +00:00
n1474335
fff188eb30 Merged master into feature-unicode-strings 2018-01-12 23:57:02 +00:00
n1474335
71067939e3 Added Regex tests and updated description 2018-01-12 23:51:51 +00:00
n1474335
b07c014b48 Added more modifiers to the Regex operation 2018-01-12 23:42:48 +00:00
n1474335
f2c073798b 'Strings' now supports various different match types in ASCII and Unicode 2018-01-12 23:09:27 +00:00
n1474335
4cc38db895 Added documentation. 2018-01-12 22:14:06 +00:00
n1474335
2762319dbb 7.5.0 2018-01-12 18:29:10 +00:00
n1474335
8e5d43dfa8 Merge branch 'feature-hamming' 2018-01-12 18:28:55 +00:00
n1474335
82ad8cc444 BigNumber Dish types are now converted to fixed notation instead of exponential when translated between types 2018-01-12 18:28:40 +00:00
n1474335
1d6bf39548 Added 'Hamming Distance' operation. 2018-01-12 18:17:28 +00:00
n1474335
ec02b7deda Regexes are now checked for 0-length matches and incremented manually to avoid infinite loops 2018-01-10 19:44:25 +00:00
n1474335
fa938f832f Fixed IE 11 detection so that support message is correctly shown. 2018-01-10 18:38:17 +00:00
n1474335
6f59d9217c Changed 'Number' option to 'Integer' in PRNG op 2018-01-06 17:01:01 +00:00
n1474335
7176e5ca6e 7.4.0 2018-01-06 16:34:06 +00:00
n1474335
429829471f Merge branch 'feature-paste-performance' 2018-01-06 16:33:12 +00:00
n1474335
4760e539b7 PRNG operation now supports BigNumbers as output 2018-01-06 16:30:17 +00:00
n1474335
f53e7ad617 Chef now defaults to treat as UTF8 if option is not set 2018-01-06 16:21:42 +00:00
n1474335
28c83fa921 Dish translation now obeys UTF8 rules 2018-01-06 16:02:50 +00:00
n1474335
4588cd151c Data pasted into the input is treat as a file if it's over the IO threshold 2018-01-06 15:29:58 +00:00
n1474335
2d9f87abef Added more loading messages 2018-01-05 20:26:51 +00:00
n1474335
491e6f5f5f 7.3.0 2018-01-05 18:50:48 +00:00
n1474335
ab7c05284d Merge branch 'artemisbot-features/big-number' 2018-01-05 18:50:13 +00:00
n1474335
0586fa0e01 Fixed failing Fork test that required an error. 2018-01-05 18:49:51 +00:00
n1474335
53eba2337c BCD operations now support BigNumbers 2018-01-05 18:38:23 +00:00
n1474335
283d3e1e7b Blank BigNumber dishes are now treat as NaN instead of erroring 2018-01-05 18:20:06 +00:00
n1474335
7992a540ae Conversion operations now support BigNumbers 2018-01-05 18:14:03 +00:00
n1474335
3f3e7a78eb Arithmetic operations now support BigNumbers 2018-01-05 18:04:55 +00:00
n1474335
8d3d39acd3 Merge branch 'features/big-number' of https://github.com/artemisbot/CyberChef into artemisbot-features/big-number 2018-01-05 17:31:27 +00:00
n1474335
7b20aba2ff Improved descriptions for 'Unescape string' and 'Escape string' operations 2018-01-04 18:32:03 +00:00
n1474335
bbfb732d8f 7.2.3 2018-01-04 17:48:08 +00:00
n1474335
566adbcda5 'Unescape string' operation now works with capitalised hex 2018-01-04 17:48:01 +00:00
n1474335
1bff490fa4 7.2.2 2018-01-04 14:43:54 +00:00
n1474335
d38375a08c Improved error handling for file uploads 2018-01-04 14:43:49 +00:00
n1474335
56551712d6 Began implementing UTF-16 support in the 'Strings' operation. 2018-01-03 16:51:10 +00:00
Matt C
c241d2f90b Adds basic BigNumber type support
Fixes `To Base` & `From Base` issues as reported on twitter
2018-01-03 11:26:31 +00:00
n1474335
0ba28dc891 Updated README.md 2018-01-02 16:02:46 +00:00
n1474335
ac9af6d2ba 7.2.1 2018-01-02 15:33:08 +00:00
n1474335
90d9e087f7 'Take bytes' and 'Drop bytes' operations now support ArrayBuffers 2018-01-02 15:33:02 +00:00
n1474335
50b24d9a56 Fixed no-trailing-space lint 2018-01-02 14:46:35 +00:00
n1474335
10f42e9a7f 7.2.0 2018-01-02 00:13:34 +00:00
n1474335
987bd303a0 Merge branch 'forge' 2018-01-02 00:13:20 +00:00
n1474335
a3f58fb831 Added 'Pseudo-Random Number Generator' operation. 2018-01-01 20:50:01 +00:00
n1474335
f52f5a0edb Added 'RC2 Encrypt' and 'RC2 Decrypt' operations. 2018-01-01 19:50:06 +00:00
n1474335
b58942f69a Rewrote PBKDF2 operation to use Forge instead of CryptoJS and improved the API for both PBKDF2 and EVP operations. 2018-01-01 18:49:22 +00:00
n1474335
68e52d1645 Removed CryptoJS encodings from Blowfish operations. 2018-01-01 17:54:45 +00:00
n1474335
9fc7e6cd98 Rewrote AES, DES and Triple DES operations to use Forge instead of CryptoJS, simplifying their options and adding many tests. Removed Rabbit operations. Fixes #63 and #210. 2018-01-01 16:09:58 +00:00
n1474335
87f346d88c 7.1.0 2017-12-29 17:48:22 +00:00
n1474335
e423ff2639 Merge branch 'feature-logging' 2017-12-29 17:48:14 +00:00
n1474335
fa6905ef00 Added more comprehensive logging to FlowControl ops and added '>' prefix to all ChefWorker logs to improve clarity 2017-12-29 17:32:23 +00:00
n1474335
8684bc0158 Removed duplicate logging message 2017-12-28 18:33:59 +00:00
n1474335
a96eb450de Improved Recipe logging 2017-12-28 18:24:29 +00:00
n1474335
d079420d46 Added logging with configurable levels to make debugging easier. 2017-12-28 18:17:38 +00:00
n1474335
124ff83206 7.0.0 2017-12-28 16:10:39 +00:00
n1474335
2c9e67ad1d Merge branch 'feature-files' 2017-12-28 16:09:11 +00:00
n1474335
75a554e215 'To Base64' and 'To Hexdump' operations now support ArrayBuffers 2017-12-28 15:59:58 +00:00
n1474335
849d41ee56 Removed padLeft and padRight in favour of String.prototype.padStart and padEnd. 'To Hex' now supports ArrayBuffers. 2017-12-28 14:38:57 +00:00
n1474335
e18ec5f2b2 Changed inputType to ArrayBuffer for 'Frequency distribution', 'Chi Square' and 'Extract EXIF' operations. 2017-12-28 00:24:16 +00:00
n1474335
50e4daeaf2 Output info tidied up for file outputs 2017-12-27 23:05:32 +00:00
n1474335
f7f07f2cb5 Loading a new file only causes a statechange event once the whole file has loaded 2017-12-27 15:52:38 +00:00
n1474335
e2161ec934 Resolved conflicts 2017-12-27 12:50:29 +00:00
n1474335
1e4d2ba90d Updated documentation for file support 2017-12-27 12:45:22 +00:00
n1474335
caf794b01d Threshold for treating output as a file is now configurable 2017-12-27 12:29:10 +00:00
n1474335
e81122739b Files can now be moved from the output to the input 2017-12-27 02:26:24 +00:00
n1474335
a0aa363203 Download filenames now persist 2017-12-27 02:01:17 +00:00
n1474335
bad0816115 Output files can be viewed in slices 2017-12-27 01:52:41 +00:00
n1474335
53a3f3d452 Changed inputType for file magic byte operations to ArrayBuffer 2017-12-26 22:05:10 +00:00
n1474335
ff94172b3c Output files can now be downloaded using FileSaver (supports large files ~500MB) 2017-12-26 01:32:51 +00:00
n1474335
af71ca6a25 Output over 1MiB is passed back as an ArrayBuffer and an output file card is displayed. 2017-12-26 00:44:40 +00:00
n1474335
0e7989111f Removed CryptoJS from Utils.js. UTF8 conversion is now achieved with the much smaller and actively maintained utf8 library. 2017-12-25 23:11:52 +00:00
n1474335
8ee3742216 6.8.0 2017-12-21 15:13:54 +00:00
n1474335
194eb184f3 Merge branch 'bwhitn-math' 2017-12-21 15:13:35 +00:00
n1474335
98f59ace3a Small tweaks to the arithmetic ops 2017-12-21 15:12:06 +00:00
n1474335
c1fb6d9776 Merge branch 'math' of https://github.com/bwhitn/CyberChef into bwhitn-math 2017-12-21 14:46:37 +00:00
bwhitn
fc7d2c2f52 separated all functions and updated comments/descriptions 2017-12-21 05:58:31 -08:00
bwhitn
0fea84ed7a WIP 2017-12-21 00:19:47 -05:00
n1474335
5e7f8e3976 Removed unnecessary whitespace 2017-12-20 16:34:21 +00:00
n1474335
fe4c5f5899 6.7.2 2017-12-20 15:56:56 +00:00
n1474335
d47b7b9242 Merge branch 'bug-numberwang' 2017-12-20 15:56:47 +00:00
n1474335
09b6661e35 Fixed trailing spaces 2017-12-20 15:51:57 +00:00
n1474335
57b1667b69 Fixed the Numberwang algorithm to correctly recognise recently discovered Numberwang values. Fixes #204 2017-12-20 15:45:58 +00:00
n1474335
5e9380b550 6.7.1 2017-12-20 13:43:31 +00:00
n1474335
fc8a0480fb Merge branch 'bug-css-selector' 2017-12-20 13:43:00 +00:00
n1474335
317327d097 CSS selector operation now works in a web worker. Fixes #218 2017-12-20 13:30:51 +00:00
n1474335
ecd0ac2521 Updated dependencies 2017-12-19 16:53:57 +00:00
n1474335
f9eaf67db2 6.7.0 2017-12-19 15:02:23 +00:00
n1474335
99e0c8d5e3 Merge branch 'bwhitn-chi' 2017-12-19 15:02:08 +00:00
n1474335
7a951d86d8 Tidied up Chi Square operation 2017-12-19 15:02:01 +00:00
n1474335
d9dfaec84c Merge branch 'chi' of https://github.com/bwhitn/CyberChef into bwhitn-chi 2017-12-19 14:45:50 +00:00
n1474335
80719ae368 6.6.3 2017-12-19 14:41:34 +00:00
n1474335
9407809356 Merge branch 'bwhitn-fixHOTP' 2017-12-19 14:40:30 +00:00
n1474335
f7e958e7a1 Changed HOTP inputType to byteArray 2017-12-19 14:38:13 +00:00
n1474335
5d75f8636a Resolved conflict 2017-12-19 14:31:40 +00:00
n1474335
22aaeb3ff5 6.6.2 2017-12-19 14:25:12 +00:00
n1474335
8c29ce95e2 Merge branch 'artemisbot-bug/uuid' 2017-12-19 14:24:59 +00:00
n1474335
049fc66785 Added note to Default.js to show that crypto is included in that module. 2017-12-19 14:24:47 +00:00
n1474335
029c55fd53 Merge branch 'bug/uuid' of https://github.com/artemisbot/CyberChef into artemisbot-bug/uuid 2017-12-19 14:15:31 +00:00
n1474335
7b433b9bd6 6.6.1 2017-12-19 14:12:24 +00:00
n1474335
5ec210990b Fixed NetBIOS space removal 2017-12-19 14:12:18 +00:00
n1474335
a7a0cacddb Merge branch 'bwhitn-fixNB' 2017-12-19 13:54:02 +00:00
n1474335
e61ced93d6 Removed dependency for Utils.js from NetBIOS.js 2017-12-19 13:53:33 +00:00
n1474335
df122da1d2 Merge branch 'fixNB' of https://github.com/bwhitn/CyberChef into bwhitn-fixNB 2017-12-19 13:26:49 +00:00
n1474335
31d90939fe 6.6.0 2017-12-19 13:19:46 +00:00
n1474335
67b0fdf73e Merge branch 'bwhitn-control' 2017-12-19 13:18:58 +00:00
n1474335
12fc8c22dd Made some naming changes to Label-related operations. 2017-12-19 13:18:25 +00:00
n1474335
4e00ac9300 Files are now uploaded in a worker and not displayed in the input by default. Added ArrayBuffer Dish type. 2017-12-18 20:39:55 +00:00
bwhitn
4ca2a30249 Fixed minor errors 2017-12-18 05:33:52 -08:00
bwhitn
08a31523b2 changed the function comment 2017-12-18 05:04:11 -08:00
Matt C
4b29a61065 Fixes UUID incompatibility with webworkers 2017-12-18 09:53:23 +00:00
bwhitn
06c83cb44c forgot a equal sign 2017-12-17 23:58:53 -05:00
bwhitn
75a5fc0ddc Added Test, function checks, and cleaned some output. 2017-12-17 23:58:53 -05:00
bwhitn
946d165aa0 fixed decode 2017-12-17 23:57:35 -05:00
bwhitn
224d79be05 merged with master 2017-12-17 23:56:30 -05:00
bwhitn
435ed587a5 Fixed HOTP, TOTP and added test for HOTP 2017-12-17 23:53:13 -05:00
bwhitn
81082ea001 I really need to learn git 2017-12-17 23:45:15 -05:00
bwhitn
1d3229a729 rebase to current 2017-12-17 23:35:34 -05:00
bwhitn
a9e60d3450 minor fix 2017-12-17 23:29:16 -05:00
bwhitn
f9ddee7d80 Inital commit for pull 2017-12-17 23:29:16 -05:00
bwhitn
ef0d3b73b0 changed op array 2017-12-17 23:29:16 -05:00
bwhitn
5368040e83 Added additional arithmetic source 2017-12-17 23:29:16 -05:00
bwhitn
b9b4147c2f start of math operations 2017-12-17 23:28:09 -05:00
bwhitn
caae0ec5ca Merge pull request #2 from gchq/master
update
2017-12-17 20:16:25 -08:00
bwhitn
2b47631f4d minor fix 2017-12-17 22:15:13 -05:00
bwhitn
298e8e8491 Inital commit for pull 2017-12-17 21:57:09 -05:00
bwhitn
6ad3728314 changed op array 2017-12-17 15:29:31 -05:00
bwhitn
772f9a806e Added additional arithmetic source 2017-12-17 15:20:58 -05:00
bwhitn
ae8d1f2178 start of math operations 2017-12-17 15:19:10 -05:00
bwhitn
9bc6c46dc3 Fixed HOTP, TOTP and added test for HOTP 2017-12-16 09:10:52 -05:00
n1474335
b48e940f2d Merge branch 'control' of https://github.com/bwhitn/CyberChef into bwhitn-control 2017-12-08 13:47:45 +00:00
bwhitn
e500cfae75 Fixed errors 2017-11-24 10:31:26 -08:00
bwhitn
f01c0adee2 Changed jumps from index based to label base. Updated test. 2017-11-24 10:12:08 -08:00
n1474335
021cae1a95 6.5.0 2017-11-24 16:33:46 +00:00
n1474335
f66cd8f983 Merge branch 'jarmovanlenthe-master' 2017-11-24 16:32:45 +00:00
n1474335
fe8049199a Moved PhpDeserialize.js to PHP.js to encompass possible future PHP-related ops 2017-11-24 16:32:11 +00:00
n1474335
cfb6dd9471 Merge branch 'master' of https://github.com/jarmovanlenthe/CyberChef into jarmovanlenthe-master 2017-11-24 15:19:56 +00:00
bwhitn
7abda44fd6 Added Negative Matching to conditional jumps so negative lookahead is not required. 2017-11-24 05:48:40 -08:00
bwhitn
47ce240e70 Merge pull request #1 from gchq/master
updating
2017-11-24 07:50:13 -05:00
n1474335
c23d7fd79c 6.4.6 2017-11-20 17:47:48 +00:00
n1474335
555fed2d51 Merge branch 'artemisbot-features/keybindings' 2017-11-20 17:46:09 +00:00
n1474335
7a2f68e14a Cosmetic changes to keybindings code 2017-11-20 17:45:50 +00:00
n1474335
8fd08cb2bf Merge branch 'features/keybindings' of https://github.com/artemisbot/CyberChef into artemisbot-features/keybindings 2017-11-20 16:57:16 +00:00
n1474335
7a2b75c861 Updated dependencies 2017-11-20 16:57:04 +00:00
Jarmo van Lenthe
00074f914f Change test to Apache-2.0 license 2017-11-16 07:37:11 -05:00
Jarmo van Lenthe
ea352e05f0 Change PHP Serialization operation to Apache-2.0 license. 2017-11-15 16:00:53 -05:00
Jarmo van Lenthe
305956cbe3 Fix copyright statement 2017-11-13 07:15:06 -05:00
Jarmo van Lenthe
5399d27875 Add space after for 2017-11-12 22:23:38 -05:00
Jarmo van Lenthe
29047c2481 Add JSDoc to helper functions and reformat while true. 2017-11-12 22:20:16 -05:00
Jarmo van Lenthe
50a32e90d9 Reformatted PHP deserialization. 2017-11-12 22:11:16 -05:00
Jarmo van Lenthe
f596fe8404 Add tests for PHP deserialization 2017-11-12 22:10:28 -05:00
Jarmo van Lenthe
4be7f89fd8 Add PHP Deserialization. 2017-11-12 21:37:29 -05:00
Matt C
8b30fdf7f1 Adds ability for user to use Meta key instead of alt for keybindings
- includes dynamically updating keybinding list
2017-11-04 12:55:28 +00:00
n1474335
2cd4256ece Updated dependencies 2017-10-26 17:00:13 +00:00
n1474335
c6a65c4686 6.4.5 2017-10-23 17:32:47 +00:00
n1474335
1553b5f54b Fixed lint 2017-10-23 17:32:36 +00:00
n1474335
2ddd2e0a60 Added 'Copy output' button. Closes #198. 2017-10-23 17:31:53 +00:00
Matt C
d924ede9cf Merge remote-tracking branch 'upstream/master' into features/keybindings 2017-10-17 19:37:14 +01:00
Matt C
638e03856b Initial keybinding functionality + documentation
Todo:
- allow user to specify whether to use alt or meta key (relatively easy to implement)
- keybinding icon for about pane
2017-10-17 19:36:51 +01:00
n1474335
8afd77b32d 6.4.4 2017-10-13 11:29:45 +00:00
n1474335
599fefb39b Fixed 'Parse URI' operation and improved error handling from worker 2017-10-13 11:29:22 +00:00
n1474335
ec7294d734 6.4.3 2017-10-13 09:35:34 +00:00
n1474335
c0d03db9e8 Merge branch 'qistoph-numsort' 2017-10-13 09:35:25 +00:00
n1474335
0365f96678 Merge branch 'numsort' of https://github.com/qistoph/CyberChef into qistoph-numsort 2017-10-13 09:33:09 +00:00
n1474335
40d8b42478 6.4.2 2017-10-13 09:19:22 +00:00
n1474335
e5a32ac57d Added links to Gitter chat room 2017-10-13 09:19:16 +00:00
Chris van Marle
390d5927a4 BugFix: compare odd size elements, like empty line 2017-10-12 14:23:04 +02:00
n1474335
b61a1b4edb 6.4.1 2017-10-09 15:39:10 +00:00
n1474335
1d78578fc2 Merge branch 'slurdge-master' 2017-10-09 15:20:52 +00:00
n1474335
0e3751407b Cleaned lint. 2017-10-09 15:17:20 +00:00
slurdge
7feafbf0e7 Fixes the lint problems and add JSDoc 2017-10-09 16:02:12 +02:00
slurdge
cfd9b16f8b Factorize all CryptoApi.hash calls and pass string directly. Fixes #193 2017-10-09 15:43:37 +02:00
n1474335
1bbc73ec50 Removed devDependencies badge from README.md 2017-10-05 16:33:20 +00:00
n1474335
dc9ba583d5 Merge branch 'master' of github.com:gchq/CyberChef 2017-10-05 16:29:09 +00:00
n1474335
6b5ff83927 Updated dependencies 2017-10-05 16:28:49 +00:00
n1474335
a5ffa406a8 6.4.0 2017-10-04 23:08:23 +01:00
n1474335
6234afc119 Added disassembly examples to README and index.html 2017-10-04 23:08:16 +01:00
n1474335
ef2ead262b Fixed merge conflict 2017-10-04 22:54:24 +01:00
n1474335
cd5265fad4 Added 'Disassemble x86' operation 2017-10-04 22:35:44 +01:00
n1474335
0c4ef1cc94 OperationConfig now exports a natural version as well as the val-loader version 2017-10-04 15:56:51 +00:00
n1474335
4fee6cd5d8 6.3.2 2017-10-03 17:49:29 +00:00
n1474335
da63420d67 Added dependency badges to README 2017-10-03 17:49:19 +00:00
n1474335
72c79c684b Lint improvements 2017-10-03 17:14:40 +00:00
n1474335
a997ec0695 Updated dependencies 2017-10-03 16:56:37 +00:00
n1474335
e3be2a9531 Merge branch 'master' of github.com:gchq/CyberChef 2017-10-03 16:46:35 +00:00
n1474335
bd1790b692 6.3.1 2017-09-28 19:45:37 +00:00
n1474335
90763dee6a Added 'Register' example to FAQ 2017-09-28 19:45:29 +00:00
n1474335
16879a9190 Added 'Register' example 2017-09-28 20:43:07 +01:00
n1474335
2afd23fcf7 6.3.0 2017-09-28 19:33:01 +00:00
n1474335
39fd0f9b34 Merge branch 'feature-registers' 2017-09-28 19:31:59 +00:00
n1474335
ee57a92daa Improved register reference regex 2017-09-28 19:24:28 +00:00
n1474335
ab7cc878c3 Removed debug code 2017-09-28 18:40:29 +00:00
n1474335
877ab57f0a Multiple Register operations can now be called in a single recipe 2017-09-28 18:39:35 +00:00
n1474335
e2ac297102 Register values now displayed in the recipe 2017-09-28 17:35:52 +00:00
n1474335
b86dceb3c6 Added Register operation 2017-09-28 16:27:39 +00:00
n1474335
35c9dca955 6.2.1 2017-09-27 15:44:10 +00:00
n1474335
d54d7011d9 Removed excess auto-baking on input load from URI 2017-09-27 15:44:00 +00:00
n1474335
392652ed8e 6.2.0 2017-09-27 15:33:37 +00:00
n1474335
7557e1e9e5 Added Snefru hashing operation 2017-09-27 15:33:14 +00:00
n1474335
d924da2f25 6.1.0 2017-09-22 17:36:03 +00:00
n1474335
be4d1eabaa Merge branch 'feature-hashing' 2017-09-22 17:34:18 +00:00
n1474335
db98e56e72 Reduced extraneous auto bakes 2017-09-22 17:33:46 +00:00
n1474335
e2a35ea844 Updated Whirlpool and HAS-160 operations to work with modules 2017-09-22 17:01:39 +00:00
n1474335
7966b2bde6 Merge branch 'master' into feature-hashing 2017-09-22 16:09:13 +00:00
n1474335
9391b947c6 Merge branch 'master' of github.com:gchq/CyberChef 2017-09-22 16:05:59 +00:00
n1474335
e61e3bcf9b Updated the dev server to work on external interfaces 2017-09-22 16:05:15 +00:00
n1474335
d79a0e737a Added HAS-160 and Whirlpool hashing operations 2017-09-20 09:43:14 +00:00
91 changed files with 13629 additions and 2621 deletions

View File

@@ -28,18 +28,13 @@
// modify rules from base configurations
"no-unused-vars": ["error", {
"args": "none",
"vars": "local",
// Allow vars that start with a capital letter to be unused.
// This is mainly for exported module names which are useful to indicate
// the name of the module and may be used to refer to itself in future.
"varsIgnorePattern": "^[A-Z]"
"vars": "all"
}],
"no-empty": ["error", {
"allowEmptyCatch": true
}],
// disable rules from base configurations
"no-console": "off",
"no-control-regex": "off",
// stylistic conventions
@@ -94,6 +89,7 @@
"$": false,
"jQuery": false,
"moment": false,
"log": false,
"COMPILE_TIME": false,
"COMPILE_MSG": false,

View File

@@ -5,21 +5,10 @@
<!-- Misc: -->
### Summary
<!-- If you're describing a bug, tell us what's wrong -->
<!-- If you're suggesting a change/improvement, tell us what it is and how it should work -->
### Example
<!-- If describing a bug, tell us what happens instead of the expected behavior -->
<!-- Include a link that triggers the bug if possible -->
<!-- If you are requesting a new operation, include example input and output -->
### Possible solutions
<!-- Not obligatory, but suggest a fix/reason for the bug, or ideas for how to -->
<!-- implement the addition or change, including links to relevant resources -->
### Environment
<!-- Include any relevant details about the environment you experienced the bug in -->
<!-- This information is displayed in the About/Support pane -->
* CyberChef compile time:
* User-Agent:
* [Link to reproduce]()

View File

@@ -39,4 +39,10 @@ deploy:
on:
tags: true
branch: master
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/83c143a6822e218d5b34
on_success: change
on_failure: always
on_start: never

View File

@@ -1,3 +1,5 @@
"use strict";
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const NodeExternals = require("webpack-node-externals");
@@ -309,7 +311,10 @@ module.exports = function (grunt) {
"webpack-dev-server": {
options: {
webpack: webpackConfig,
host: "0.0.0.0",
disableHostCheck: true,
overlay: true,
inline: false,
clientLogLevel: "error",
stats: {
children: false,

View File

@@ -1,8 +1,12 @@
# CyberChef
[![Build Status](https://travis-ci.org/gchq/CyberChef.svg?branch=master)](https://travis-ci.org/gchq/CyberChef)
[![npm](https://badge.fury.io/js/cyberchef.svg)](https://www.npmjs.com/package/cyberchef)
![](https://reposs.herokuapp.com/?path=gchq/CyberChef&color=brightgreen)
[![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://reposs.herokuapp.com/?path=gchq/CyberChef&color=blue)
[![](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/gchq/CyberChef/blob/master/LICENSE)
[![Gitter](https://badges.gitter.im/gchq/CyberChef.svg)](https://gitter.im/gchq/CyberChef?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
#### *The Cyber Swiss Army Knife*
@@ -23,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.
@@ -34,34 +38,36 @@ You can use as many operations as you like in simple or complex ways. Some examp
- [Convert a date and time to a different time zone][3]
- [Parse a Teredo IPv6 address][4]
- [Convert data from a hexdump, then decompress][5]
- [Display multiple timestamps as full dates][6]
- [Carry out different operations on data of different types][7]
- [Decrypt and disassemble shellcode][6]
- [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 youll 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 youll 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][8]).
- 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 (note that files larger than about 500kb may cause your browser to hang or even crash due to the way that browsers handle large amounts of textual data).
- 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 input or recipe configuration 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.
- 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 local machine.
## Browser support
@@ -92,6 +98,9 @@ CyberChef is released under the [Apache 2.0 Licence](https://www.apache.org/lice
[3]: https://gchq.github.io/CyberChef/#recipe=Translate_DateTime_Format('Standard%20date%20and%20time','DD/MM/YYYY%20HH:mm:ss','UTC','dddd%20Do%20MMMM%20YYYY%20HH:mm:ss%20Z%20z','Australia/Queensland')&input=MTUvMDYvMjAxNSAyMDo0NTowMA
[4]: https://gchq.github.io/CyberChef/#recipe=Parse_IPv6_address()&input=MjAwMTowMDAwOjQxMzY6ZTM3ODo4MDAwOjYzYmY6M2ZmZjpmZGQy
[5]: https://gchq.github.io/CyberChef/#recipe=From_Hexdump()Gunzip()&input=MDAwMDAwMDAgIDFmIDhiIDA4IDAwIDEyIGJjIGYzIDU3IDAwIGZmIDBkIGM3IGMxIDA5IDAwIDIwICB8Li4uLi6881cu/y7HwS4uIHwKMDAwMDAwMTAgIDA4IDA1IGQwIDU1IGZlIDA0IDJkIGQzIDA0IDFmIGNhIDhjIDQ0IDIxIDViIGZmICB8Li7QVf4uLdMuLsouRCFb/3wKMDAwMDAwMjAgIDYwIGM3IGQ3IDAzIDE2IGJlIDQwIDFmIDc4IDRhIDNmIDA5IDg5IDBiIDlhIDdkICB8YMfXLi6%2BQC54Sj8uLi4ufXwKMDAwMDAwMzAgIDRlIGM4IDRlIDZkIDA1IDFlIDAxIDhiIDRjIDI0IDAwIDAwIDAwICAgICAgICAgICB8TshObS4uLi5MJC4uLnw
[6]: https://gchq.github.io/CyberChef/#recipe=Fork('%5C%5Cn','%5C%5Cn',false)From_UNIX_Timestamp('Seconds%20(s)')&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA
[7]: https://gchq.github.io/CyberChef/#recipe=Fork('%5C%5Cn','%5C%5Cn',false)Conditional_Jump('1',2,10)To_Hex('Space')Return()To_Base64('A-Za-z0-9%2B/%3D')&input=U29tZSBkYXRhIHdpdGggYSAxIGluIGl0ClNvbWUgZGF0YSB3aXRoIGEgMiBpbiBpdA
[8]: https://gchq.github.io/CyberChef/#recipe=XOR(%7B'option':'Hex','string':'3a'%7D,'',false)To_Hexdump(16,false,false)&input=VGhlIGFuc3dlciB0byB0aGUgdWx0aW1hdGUgcXVlc3Rpb24gb2YgbGlmZSwgdGhlIFVuaXZlcnNlLCBhbmQgZXZlcnl0aGluZyBpcyA0Mi4
[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.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=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

2698
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "6.0.2",
"version": "7.5.2",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef",
@@ -32,11 +32,11 @@
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.0",
"babel-preset-env": "^1.6.1",
"css-loader": "^0.28.7",
"exports-loader": "^0.6.4",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^0.11.2",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.6",
"grunt": ">=1.0.1",
"grunt-accessibility": "~5.0.0",
"grunt-chmod": "~1.1.1",
@@ -46,56 +46,64 @@
"grunt-eslint": "^20.1.0",
"grunt-exec": "~3.0.0",
"grunt-execute": "^0.2.2",
"grunt-jsdoc": "^2.1.1",
"grunt-jsdoc": "^2.2.0",
"grunt-webpack": "^3.0.2",
"html-webpack-plugin": "^2.30.1",
"imports-loader": "^0.7.1",
"ink-docstrap": "^1.3.0",
"ink-docstrap": "^1.3.2",
"jsdoc-babel": "^0.3.0",
"less": "^2.7.2",
"less": "^2.7.3",
"less-loader": "^4.0.5",
"postcss-css-variables": "^0.8.0",
"postcss-import": "^10.0.0",
"postcss-loader": "^2.0.6",
"style-loader": "^0.18.2",
"url-loader": "^0.5.9",
"val-loader": "^1.0.2",
"web-resource-inliner": "^4.1.1",
"webpack": "^3.5.6",
"webpack-dev-server": "^2.5.0",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.9",
"style-loader": "^0.19.1",
"url-loader": "^0.6.2",
"val-loader": "^1.1.0",
"web-resource-inliner": "^4.2.0",
"webpack": "^3.10.0",
"webpack-dev-server": "^2.9.7",
"webpack-node-externals": "^1.6.0",
"worker-loader": "^0.8.0"
"worker-loader": "^1.1.0"
},
"dependencies": {
"babel-polyfill": "^6.26.0",
"bignumber.js": "^5.0.0",
"bootstrap": "^3.3.7",
"bootstrap-colorpicker": "^2.5.1",
"bootstrap-colorpicker": "^2.5.2",
"bootstrap-switch": "^3.3.4",
"crypto-api": "^0.7.3",
"crypto-api": "^0.7.5",
"crypto-js": "^3.1.9-1",
"diff": "^3.3.1",
"diff": "^3.4.0",
"escodegen": "^1.9.0",
"esmangle": "^1.0.1",
"esprima": "^4.0.0",
"exif-parser": "^0.1.12",
"file-saver": "^1.3.3",
"google-code-prettify": "^1.0.5",
"jquery": "^3.2.1",
"js-crc": "^0.2.0",
"js-sha3": "^0.6.1",
"js-sha3": "^0.7.0",
"jsbn": "^1.1.0",
"jsonpath": "^0.2.12",
"jsrsasign": "8.0.3",
"jsonpath": "^1.0.0",
"jsrsasign": "8.0.4",
"lodash": "^4.17.4",
"moment": "^2.18.1",
"moment-timezone": "^0.5.13",
"loglevel": "^1.6.0",
"loglevel-message-prefix": "^3.0.0",
"moment": "^2.20.1",
"moment-timezone": "^0.5.14",
"node-forge": "^0.7.1",
"node-md6": "^0.1.0",
"nwmatcher": "^1.4.3",
"otp": "^0.1.3",
"sladex-blowfish": "^0.8.1",
"sortablejs": "^1.6.1",
"sortablejs": "^1.7.0",
"split.js": "^1.3.5",
"utf8": "^3.0.0",
"vkbeautify": "^0.99.3",
"xmldom": "^0.1.27",
"xpath": "0.0.24",
"xpath": "0.0.27",
"xregexp": "^4.0.0",
"zlibjs": "^0.3.1"
},
"scripts": {

View File

@@ -19,7 +19,7 @@ const Chef = function() {
/**
* Runs the recipe over the input.
*
* @param {string} inputText - The input data as a string
* @param {string|ArrayBuffer} input - The input data as a string or ArrayBuffer
* @param {Object[]} recipeConfig - The recipe configuration object
* @param {Object} options - The options object storing various user choices
* @param {boolean} options.attempHighlight - Whether or not to attempt highlighting
@@ -33,11 +33,13 @@ const Chef = function() {
* @returns {number} response.duration - The number of ms it took to execute the recipe
* @returns {number} response.error - The error object thrown by a failed operation (false if no error)
*/
Chef.prototype.bake = async function(inputText, recipeConfig, options, progress, step) {
let startTime = new Date().getTime(),
Chef.prototype.bake = async function(input, recipeConfig, options, progress, step) {
log.debug("Chef baking");
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);
@@ -62,22 +64,30 @@ Chef.prototype.bake = async function(inputText, recipeConfig, options, progress,
// If starting from scratch, load data
if (progress === 0) {
this.dish.set(inputText, Dish.STRING);
const type = input instanceof ArrayBuffer ? Dish.ARRAY_BUFFER : Dish.STRING;
this.dish.set(input, type);
}
try {
progress = await recipe.execute(this.dish, progress);
} catch (err) {
// Return the error in the result so that everything else gets correctly updated
// rather than throwing it here and losing state info.
error = err;
log.error(err);
error = {
displayStr: err.displayStr,
};
progress = err.progress;
}
// 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.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(Dish.STRING),
this.dish.get(Dish.HTML, notUTF8) :
this.dish.get(returnType, notUTF8),
type: Dish.enumLookup(this.dish.type),
progress: progress,
duration: new Date().getTime() - startTime,
@@ -104,6 +114,8 @@ Chef.prototype.bake = async function(inputText, recipeConfig, options, progress,
* @returns {number} The time it took to run the silent bake in milliseconds.
*/
Chef.prototype.silentBake = function(recipeConfig) {
log.debug("Running silent bake");
let startTime = new Date().getTime(),
recipe = new Recipe(recipeConfig),
dish = new Dish("", Dish.STRING);

View File

@@ -11,6 +11,15 @@ import Chef from "./Chef.js";
import OperationConfig from "./config/MetaConfig.js";
import OpModules from "./config/modules/Default.js";
// Add ">" to the start of all log messages in the Chef Worker
import loglevelMessagePrefix from "loglevel-message-prefix";
loglevelMessagePrefix(log, {
prefixes: [],
staticPrefixes: [">"],
prefixFormat: "%p"
});
// Set up Chef instance
self.chef = new Chef();
@@ -42,6 +51,8 @@ self.postMessage({
self.addEventListener("message", function(e) {
// Handle message
const r = e.data;
log.debug("ChefWorker receiving command '" + r.action + "'");
switch (r.action) {
case "bake":
bake(r.data);
@@ -61,6 +72,9 @@ self.addEventListener("message", function(e) {
r.data.pos
);
break;
case "setLogLevel":
log.setLevel(r.data, false);
break;
default:
break;
}
@@ -86,13 +100,13 @@ async function bake(data) {
);
self.postMessage({
action: "bakeSuccess",
action: "bakeComplete",
data: response
});
} catch (err) {
self.postMessage({
action: "bakeError",
data: err.message
data: err
});
}
}
@@ -121,7 +135,7 @@ function loadRequiredModules(recipeConfig) {
let module = self.OperationConfig[op.op].module;
if (!OpModules.hasOwnProperty(module)) {
console.log("Loading module " + module);
log.info("Loading module " + module);
self.sendStatusMessage("Loading module " + module);
self.importScripts(self.docURL + "/" + module + ".js");
}
@@ -176,3 +190,22 @@ self.setOption = function(option, value) {
}
});
};
/**
* Send register values back to the app.
*
* @param {number} opIndex
* @param {number} numPrevRegisters
* @param {string[]} registers
*/
self.setRegisters = function(opIndex, numPrevRegisters, registers) {
self.postMessage({
action: "setRegisters",
data: {
opIndex: opIndex,
numPrevRegisters: numPrevRegisters,
registers: registers
}
});
};

View File

@@ -1,19 +1,21 @@
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} 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) {
this.value = value || typeof value == "string" ? value : null;
this.type = type || Dish.BYTE_ARRAY;
this.value = value || typeof value === "string" ? value : null;
this.type = type || Dish.BYTE_ARRAY;
};
@@ -41,6 +43,18 @@ Dish.NUMBER = 2;
* @enum
*/
Dish.HTML = 3;
/**
* Dish data type enum for ArrayBuffers.
* @readonly
* @enum
*/
Dish.ARRAY_BUFFER = 4;
/**
* Dish data type enum for BigNumbers.
* @readonly
* @enum
*/
Dish.BIG_NUMBER = 5;
/**
@@ -51,19 +65,22 @@ Dish.HTML = 3;
* @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 "array buffer":
return Dish.ARRAY_BUFFER;
case "bignumber":
case "big number":
return Dish.BIG_NUMBER;
default:
throw "Invalid data type string. No matching enum.";
}
@@ -74,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) {
@@ -87,6 +104,10 @@ Dish.enumLookup = function(typeEnum) {
return "number";
case Dish.HTML:
return "html";
case Dish.ARRAY_BUFFER:
return "ArrayBuffer";
case Dish.BIG_NUMBER:
return "BigNumber";
default:
throw "Invalid data type enum. No matching type.";
}
@@ -96,12 +117,13 @@ Dish.enumLookup = function(typeEnum) {
/**
* Sets the data value and type and then validates them.
*
* @param {byteArray|string|number} 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) {
log.debug("Dish type: " + Dish.enumLookup(type));
this.value = value;
this.type = type;
this.type = type;
if (!this.valid()) {
const sample = Utils.truncate(JSON.stringify(this.value), 13);
@@ -114,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} 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;
};
@@ -128,37 +151,59 @@ 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) {
case Dish.STRING:
this.value = this.value ? Utils.strToByteArray(this.value) : [];
this.type = Dish.BYTE_ARRAY;
break;
case Dish.NUMBER:
this.value = typeof this.value == "number" ? Utils.strToByteArray(this.value.toString()) : [];
this.type = Dish.BYTE_ARRAY;
break;
case Dish.HTML:
this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : [];
this.type = Dish.BYTE_ARRAY;
break;
case Dish.ARRAY_BUFFER:
// 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;
}
this.type = Dish.BYTE_ARRAY;
// Convert from byteArray to 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;
}
@@ -180,7 +225,7 @@ Dish.prototype.valid = function() {
// Check that every value is a number between 0 - 255
for (let i = 0; i < this.value.length; i++) {
if (typeof this.value[i] != "number" ||
if (typeof this.value[i] !== "number" ||
this.value[i] < 0 ||
this.value[i] > 255) {
return false;
@@ -189,18 +234,41 @@ Dish.prototype.valid = function() {
return true;
case Dish.STRING:
case Dish.HTML:
if (typeof this.value == "string") {
return true;
}
return false;
return typeof this.value === "string";
case Dish.NUMBER:
if (typeof this.value == "number") {
return true;
}
return false;
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;
}
};
/**
* Determines how much space the Dish takes up.
* Numbers in JavaScript are 64-bit floating point, however for the purposes of the Dish,
* we measure how many bytes are taken up when the number is written as a string.
*
* @returns {number}
*/
Dish.prototype.size = function() {
switch (this.type) {
case Dish.BYTE_ARRAY:
case Dish.STRING:
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;
default:
return -1;
}
};
export default Dish;

View File

@@ -56,6 +56,7 @@ const FlowControl = {
// Run recipe over each tranche
for (i = 0; i < inputs.length; i++) {
log.debug(`Entering tranche ${i + 1} of ${inputs.length}`);
const dish = new Dish(inputs[i], inputType);
try {
progress = await recipe.execute(dish, 0);
@@ -90,6 +91,74 @@ const FlowControl = {
},
/**
* Register operation.
*
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @returns {Object} The updated state of the recipe.
*/
runRegister: function(state) {
const ings = state.opList[state.progress].getIngValues(),
extractorStr = ings[0],
i = ings[1],
m = ings[2];
let modifiers = "";
if (i) modifiers += "i";
if (m) modifiers += "m";
const extractor = new RegExp(extractorStr, modifiers),
input = state.dish.get(Dish.STRING),
registers = input.match(extractor);
if (!registers) return state;
if (ENVIRONMENT_IS_WORKER()) {
self.setRegisters(state.progress, state.numRegisters, registers.slice(1));
}
/**
* Replaces references to registers (e.g. $R0) with the contents of those registers.
*
* @param {string} str
* @returns {string}
*/
function replaceRegister(str) {
// Replace references to registers ($Rn) with contents of registers
return str.replace(/(\\*)\$R(\d{1,2})/g, (match, slashes, regNum) => {
const index = parseInt(regNum, 10) + 1;
if (index <= state.numRegisters || index >= state.numRegisters + registers.length)
return match;
if (slashes.length % 2 !== 0) return match.slice(1); // Remove escape
return slashes + registers[index - state.numRegisters];
});
}
// Step through all subsequent ops and replace registers in args with extracted content
for (let i = state.progress + 1; i < state.opList.length; i++) {
if (state.opList[i].isDisabled()) continue;
let args = state.opList[i].getIngValues();
args = args.map(arg => {
if (typeof arg !== "string" && typeof arg !== "object") return arg;
if (typeof arg === "object" && arg.hasOwnProperty("string")) {
arg.string = replaceRegister(arg.string);
return arg;
}
return replaceRegister(arg);
});
state.opList[i].setIngValues(args);
}
state.numRegisters += registers.length - 1;
return state;
},
/**
* Jump operation.
*
@@ -101,20 +170,19 @@ const FlowControl = {
* @returns {Object} The updated state of the recipe.
*/
runJump: function(state) {
let ings = state.opList[state.progress].getIngValues(),
jumpNum = ings[0],
maxJumps = ings[1];
const ings = state.opList[state.progress].getIngValues(),
label = ings[0],
maxJumps = ings[1],
jmpIndex = FlowControl._getLabelIndex(label, state);
if (jumpNum < 0) {
jumpNum--;
}
if (state.numJumps >= maxJumps) {
if (state.numJumps >= maxJumps || jmpIndex === -1) {
log.debug("Maximum jumps reached or label cannot be found");
return state;
}
state.progress += jumpNum;
state.progress = jmpIndex;
state.numJumps++;
log.debug(`Jumping to label '${label}' at position ${jmpIndex} (jumps = ${state.numJumps})`);
return state;
},
@@ -130,23 +198,26 @@ const FlowControl = {
* @returns {Object} The updated state of the recipe.
*/
runCondJump: function(state) {
let ings = state.opList[state.progress].getIngValues(),
const ings = state.opList[state.progress].getIngValues(),
dish = state.dish,
regexStr = ings[0],
jumpNum = ings[1],
maxJumps = ings[2];
invert = ings[1],
label = ings[2],
maxJumps = ings[3],
jmpIndex = FlowControl._getLabelIndex(label, state);
if (jumpNum < 0) {
jumpNum--;
}
if (state.numJumps >= maxJumps) {
if (state.numJumps >= maxJumps || jmpIndex === -1) {
log.debug("Maximum jumps reached or label cannot be found");
return state;
}
if (regexStr !== "" && dish.get(Dish.STRING).search(regexStr) > -1) {
state.progress += jumpNum;
state.numJumps++;
if (regexStr !== "") {
let strMatch = dish.get(Dish.STRING).search(regexStr) > -1;
if (!invert && strMatch || invert && !strMatch) {
state.progress = jmpIndex;
state.numJumps++;
log.debug(`Jumping to label '${label}' at position ${jmpIndex} (jumps = ${state.numJumps})`);
}
}
return state;
@@ -181,6 +252,27 @@ const FlowControl = {
return state;
},
/**
* Returns the index of a label.
*
* @private
* @param {Object} state
* @param {string} name
* @returns {number}
*/
_getLabelIndex: function(name, state) {
for (let o = 0; o < state.opList.length; o++) {
let operation = state.opList[o];
if (operation.name === "Label"){
let ings = operation.getIngValues();
if (name === ings[0]) {
return o;
}
}
}
return -1;
},
};
export default FlowControl;

View File

@@ -145,32 +145,39 @@ Recipe.prototype.lastOpIndex = function(startIndex) {
*/
Recipe.prototype.execute = async function(dish, startFrom) {
startFrom = startFrom || 0;
let op, input, output, numJumps = 0;
let op, input, output, numJumps = 0, numRegisters = 0;
log.debug(`[*] Executing recipe of ${this.opList.length} operations, starting at ${startFrom}`);
for (let i = startFrom; i < this.opList.length; i++) {
op = this.opList[i];
log.debug(`[${i}] ${op.name} ${JSON.stringify(op.getIngValues())}`);
if (op.isDisabled()) {
log.debug("Operation is disabled, skipping");
continue;
}
if (op.isBreakpoint()) {
log.debug("Pausing at breakpoint");
return i;
}
try {
input = dish.get(op.inputType);
log.debug("Executing operation");
if (op.isFlowControl()) {
// Package up the current state
let state = {
"progress": i,
"dish": dish,
"opList": this.opList,
"numJumps": numJumps
"progress": i,
"dish": dish,
"opList": this.opList,
"numJumps": numJumps,
"numRegisters": numRegisters
};
state = await op.run(state);
i = state.progress;
numJumps = state.numJumps;
numRegisters = state.numRegisters;
} else {
output = await op.run(input, op.getIngValues());
dish.set(output, op.outputType);
@@ -191,6 +198,7 @@ Recipe.prototype.execute = async function(dish, startFrom) {
}
}
log.debug("Recipe complete");
return this.opList.length;
};

View File

@@ -1,4 +1,4 @@
import CryptoJS from "crypto-js";
import utf8 from "utf8";
/**
@@ -64,58 +64,6 @@ const Utils = {
},
/**
* Adds leading zeros to strings
*
* @param {string} str - String to add leading characters to.
* @param {number} max - Maximum width of the string.
* @param {char} [chr='0'] - The character to pad with.
* @returns {string}
*
* @example
* // returns "0a"
* Utils.padLeft("a", 2);
*
* // returns "000a"
* Utils.padLeft("a", 4);
*
* // returns "xxxa"
* Utils.padLeft("a", 4, "x");
*
* // returns "bcabchello"
* Utils.padLeft("hello", 10, "abc");
*/
padLeft: function(str, max, chr) {
chr = chr || "0";
let startIndex = chr.length - (max - str.length);
startIndex = startIndex < 0 ? 0 : startIndex;
return str.length < max ?
Utils.padLeft(chr.slice(startIndex, chr.length) + str, max, chr) : str;
},
/**
* Adds trailing spaces to strings.
*
* @param {string} str - String to add trailing characters to.
* @param {number} max - Maximum width of the string.
* @param {char} [chr='0'] - The character to pad with.
* @returns {string}
*
* @example
* // returns "a "
* Utils.padRight("a", 4);
*
* // returns "axxx"
* Utils.padRight("a", 4, "x");
*/
padRight: function(str, max, chr) {
chr = chr || " ";
return str.length < max ?
Utils.padRight(str + chr.slice(0, max-str.length), max, chr) : str;
},
/**
* Adds trailing bytes to a byteArray.
*
@@ -152,14 +100,6 @@ const Utils = {
},
/**
* @alias Utils.padLeft
*/
pad: function(str, max, chr) {
return Utils.padLeft(str, max, chr);
},
/**
* Truncates a long string to max length and adds suffix.
*
@@ -201,7 +141,7 @@ const Utils = {
hex: function(c, length) {
c = typeof c == "string" ? Utils.ord(c) : c;
length = length || 2;
return Utils.pad(c.toString(16), length);
return c.toString(16).padStart(length, "0");
},
@@ -222,7 +162,7 @@ const Utils = {
bin: function(c, length) {
c = typeof c == "string" ? Utils.ord(c) : c;
length = length || 8;
return Utils.pad(c.toString(2), length);
return c.toString(2).padStart(length, "0");
},
@@ -261,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":
@@ -340,6 +280,72 @@ const Utils = {
},
/**
* Coverts data of varying types to a byteArray.
* Accepts hex, Base64, UTF8 and Latin1 strings.
*
* @param {string} str
* @param {string} type - One of "Hex", "Base64", "UTF8" or "Latin1"
* @returns {byteArray}
*
* @example
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
* Utils.convertToByteArray("Привет", "utf8");
*
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
* Utils.convertToByteArray("d097d0b4d180d0b0d0b2d181d182d0b2d183d0b9d182d0b5", "hex");
*
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
* Utils.convertToByteArray("0JfQtNGA0LDQstGB0YLQstGD0LnRgtC1", "base64");
*/
convertToByteArray: function(str, type) {
switch (type.toLowerCase()) {
case "hex":
return Utils.fromHex(str);
case "base64":
return Utils.fromBase64(str, null, "byteArray");
case "utf8":
return Utils.strToUtf8ByteArray(str);
case "latin1":
default:
return Utils.strToByteArray(str);
}
},
/**
* Coverts data of varying types to a byte string.
* Accepts hex, Base64, UTF8 and Latin1 strings.
*
* @param {string} str
* @param {string} type - One of "Hex", "Base64", "UTF8" or "Latin1"
* @returns {string}
*
* @example
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
* Utils.convertToByteArray("Привет", "utf8");
*
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
* Utils.convertToByteArray("d097d0b4d180d0b0d0b2d181d182d0b2d183d0b9d182d0b5", "hex");
*
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
* Utils.convertToByteArray("0JfQtNGA0LDQstGB0YLQstGD0LnRgtC1", "base64");
*/
convertToByteString: function(str, type) {
switch (type.toLowerCase()) {
case "hex":
return Utils.byteArrayToChars(Utils.fromHex(str));
case "base64":
return Utils.byteArrayToChars(Utils.fromBase64(str, null, "byteArray"));
case "utf8":
return utf8.encode(str);
case "latin1":
default:
return str;
}
},
/**
* Converts a string to a byte array.
* Treats the string as UTF-8 if any values are over 255.
@@ -381,17 +387,17 @@ const Utils = {
* Utils.strToUtf8ByteArray("你好");
*/
strToUtf8ByteArray: function(str) {
let wordArray = CryptoJS.enc.Utf8.parse(str),
byteArray = Utils.wordArrayToByteArray(wordArray);
const utf8Str = utf8.encode(str);
if (str.length !== wordArray.sigBytes) {
if (str.length !== utf8Str.length) {
if (ENVIRONMENT_IS_WORKER()) {
self.setOption("attemptHighlight", false);
} else if (ENVIRONMENT_IS_WEB()) {
window.app.options.attemptHighlight = false;
}
}
return byteArray;
return Utils.strToByteArray(utf8Str);
},
@@ -409,7 +415,7 @@ const Utils = {
* Utils.strToCharcode("你好");
*/
strToCharcode: function(str) {
const charcode = new Array();
const charcode = [];
for (let i = 0; i < str.length; i++) {
let ord = str.charCodeAt(i);
@@ -443,26 +449,21 @@ const Utils = {
* Utils.byteArrayToUtf8([228,189,160,229,165,189]);
*/
byteArrayToUtf8: function(byteArray) {
const str = Utils.byteArrayToChars(byteArray);
try {
// Try to output data as UTF-8 string
const words = [];
for (let i = 0; i < byteArray.length; i++) {
words[i >>> 2] |= byteArray[i] << (24 - (i % 4) * 8);
}
let wordArray = new CryptoJS.lib.WordArray.init(words, byteArray.length),
str = CryptoJS.enc.Utf8.stringify(wordArray);
const utf8Str = utf8.decode(str);
if (str.length !== wordArray.sigBytes) {
if (str.length !== utf8Str.length) {
if (ENVIRONMENT_IS_WORKER()) {
self.setOption("attemptHighlight", false);
} else if (ENVIRONMENT_IS_WEB()) {
window.app.options.attemptHighlight = false;
}
}
return str;
return utf8Str;
} catch (err) {
// If it fails, treat it as ANSI
return Utils.byteArrayToChars(byteArray);
return str;
}
},
@@ -470,7 +471,7 @@ const Utils = {
/**
* Converts a charcode array to a string.
*
* @param {byteArray} byteArray
* @param {byteArray|Uint8Array} byteArray
* @returns {string}
*
* @example
@@ -491,33 +492,25 @@ const Utils = {
/**
* Converts a CryptoJS.lib.WordArray to a byteArray.
* Converts an ArrayBuffer to a string.
*
* @param {CryptoJS.lib.WordArray} wordArray
* @returns {byteArray}
* @param {ArrayBuffer} arrayBuffer
* @returns {string}
*
* @example
* // returns [84, 101, 115, 116]
* Utils.wordArrayToByteArray(CryptoJS.enc.Hex.parse("54657374"));
* // returns "hello"
* Utils.arrayBufferToStr(Uint8Array.from([104,101,108,108,111]).buffer);
*/
wordArrayToByteArray: function(wordArray) {
if (wordArray.sigBytes <= 0) return [];
let words = wordArray.words,
byteArray = [];
for (let i = 0; i < wordArray.sigBytes; i++) {
byteArray.push((words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
}
return byteArray;
arrayBufferToStr: function(arrayBuffer) {
const byteArray = Array.prototype.slice.call(new Uint8Array(arrayBuffer));
return Utils.byteArrayToUtf8(byteArray);
},
/**
* Base64's the input byte array using the given alphabet, returning a string.
*
* @param {byteArray|string} data
* @param {byteArray|Uint8Array|string} data
* @param {string} [alphabet]
* @returns {string}
*
@@ -636,7 +629,7 @@ const Utils = {
/**
* Convert a byte array into a hex string.
*
* @param {byteArray} data
* @param {Uint8Array|byteArray} data
* @param {string} [delim=" "]
* @param {number} [padding=2]
* @returns {string}
@@ -656,7 +649,7 @@ const Utils = {
let output = "";
for (let i = 0; i < data.length; i++) {
output += Utils.pad(data[i].toString(16), padding) + delim;
output += data[i].toString(16).padStart(padding, "0") + delim;
}
// Add \x or 0x to beginning
@@ -859,7 +852,7 @@ const Utils = {
*
* fragment = *( pchar / "/" / "?" )
* query = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* pct-encoded = "%" HEXDIG HEXDIG
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
@@ -1248,21 +1241,6 @@ const Utils = {
"None": /\s+/g // Included here to remove whitespace when there shouldn't be any
},
/**
* A mapping of string formats to their classes in the CryptoJS library.
* @constant
*/
format: {
"Hex": CryptoJS.enc.Hex,
"Base64": CryptoJS.enc.Base64,
"UTF8": CryptoJS.enc.Utf8,
"UTF16": CryptoJS.enc.Utf16,
"UTF16LE": CryptoJS.enc.Utf16LE,
"UTF16BE": CryptoJS.enc.Utf16BE,
"Latin1": CryptoJS.enc.Latin1,
},
};
export default Utils;
@@ -1376,29 +1354,43 @@ String.prototype.count = function(chr) {
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Library overrides ///////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Override for the CryptoJS Hex encoding parser to remove whitespace before attempting to parse
* the hex string.
*
* @param {string} hexStr
* @returns {CryptoJS.lib.WordArray}
/*
* Polyfills
*/
CryptoJS.enc.Hex.parse = function (hexStr) {
// Remove whitespace
hexStr = hexStr.replace(/\s/g, "");
// Shortcut
const hexStrLength = hexStr.length;
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
if (!String.prototype.padStart) {
String.prototype.padStart = function padStart(targetLength, padString) {
targetLength = targetLength>>0; //floor if number or convert non-number to 0;
padString = String((typeof padString !== "undefined" ? padString : " "));
if (this.length > targetLength) {
return String(this);
} else {
targetLength = targetLength-this.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength/padString.length); //append to original to ensure we are longer than needed
}
return padString.slice(0, targetLength) + String(this);
}
};
}
// Convert
const words = [];
for (let i = 0; i < hexStrLength; i += 2) {
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
}
return new CryptoJS.lib.WordArray.init(words, hexStrLength / 2);
};
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
if (!String.prototype.padEnd) {
String.prototype.padEnd = function padEnd(targetLength, padString) {
targetLength = targetLength>>0; //floor if number or convert non-number to 0;
padString = String((typeof padString !== "undefined" ? padString : " "));
if (this.length > targetLength) {
return String(this);
} else {
targetLength = targetLength-this.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength/padString.length); //append to original to ensure we are longer than needed
}
return String(this) + padString.slice(0, targetLength);
}
};
}

View File

@@ -79,8 +79,8 @@ const Categories = [
"DES Decrypt",
"Triple DES Encrypt",
"Triple DES Decrypt",
"Rabbit Encrypt",
"Rabbit Decrypt",
"RC2 Encrypt",
"RC2 Decrypt",
"RC4",
"RC4 Drop",
"ROT13",
@@ -99,6 +99,7 @@ const Categories = [
"Substitute",
"Derive PBKDF2 key",
"Derive EVP key",
"Pseudo-Random Number Generator",
]
},
{
@@ -113,7 +114,7 @@ const Categories = [
]
},
{
name: "Logical operations",
name: "Arithmetic / Logic",
ops: [
"XOR",
"XOR Brute Force",
@@ -122,6 +123,13 @@ const Categories = [
"AND",
"ADD",
"SUB",
"Sum",
"Subtract",
"Multiply",
"Divide",
"Mean",
"Median",
"Standard Deviation",
"Bit shift left",
"Bit shift right",
"Rotate left",
@@ -181,6 +189,7 @@ const Categories = [
"Find / Replace",
"Regular expression",
"Offset checker",
"Hamming Distance",
"Convert distance",
"Convert area",
"Convert mass",
@@ -191,6 +200,7 @@ const Categories = [
"Parse colour code",
"Escape string",
"Unescape string",
"Pseudo-Random Number Generator",
]
},
{
@@ -255,6 +265,9 @@ const Categories = [
"Keccak",
"Shake",
"RIPEMD",
"HAS-160",
"Whirlpool",
"Snefru",
"HMAC",
"Fletcher-8 Checksum",
"Fletcher-16 Checksum",
@@ -285,6 +298,7 @@ const Categories = [
"XPath expression",
"JPath expression",
"CSS selector",
"PHP Deserialize",
"Microsoft Script Decoder",
"Strip HTML tags",
"Diff",
@@ -298,8 +312,11 @@ const Categories = [
ops: [
"Entropy",
"Frequency distribution",
"Chi Square",
"Detect File Type",
"Scan for Embedded Files",
"Disassemble x86",
"Pseudo-Random Number Generator",
"Generate UUID",
"Generate TOTP",
"Generate HOTP",
@@ -314,6 +331,8 @@ const Categories = [
ops: [
"Fork",
"Merge",
"Register",
"Label",
"Jump",
"Conditional Jump",
"Return",

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,6 @@ import CharEnc from "../../operations/CharEnc.js";
*
* Libraries:
* - cptable
* - CryptoJS
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017

View File

@@ -7,6 +7,7 @@ import Cipher from "../../operations/Cipher.js";
* Libraries:
* - CryptoJS
* - Blowfish
* - Forge
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
@@ -23,12 +24,12 @@ OpModules.Ciphers = {
"DES Decrypt": Cipher.runDesDec,
"Triple DES Encrypt": Cipher.runTripleDesEnc,
"Triple DES Decrypt": Cipher.runTripleDesDec,
"Rabbit Encrypt": Cipher.runRabbitEnc,
"Rabbit Decrypt": Cipher.runRabbitDec,
"Derive PBKDF2 key": Cipher.runPbkdf2,
"Derive EVP key": Cipher.runEvpkdf,
"RC4": Cipher.runRc4,
"RC4 Drop": Cipher.runRc4drop,
"RC2 Encrypt": Cipher.runRc2Enc,
"RC2 Decrypt": Cipher.runRc2Dec,
"Vigenère Encode": Cipher.runVigenereEnc,
"Vigenère Decode": Cipher.runVigenereDec,
"Bifid Cipher Encode": Cipher.runBifidEnc,
@@ -37,6 +38,7 @@ OpModules.Ciphers = {
"Affine Cipher Decode": Cipher.runAffineDec,
"Atbash Cipher": Cipher.runAtbash,
"Substitute": Cipher.runSubstitute,
"Pseudo-Random Number Generator": Cipher.runPRNG,
};
export default OpModules;

View File

@@ -1,4 +1,5 @@
import FlowControl from "../../FlowControl.js";
import Arithmetic from "../../operations/Arithmetic.js";
import Base from "../../operations/Base.js";
import Base58 from "../../operations/Base58.js";
import Base64 from "../../operations/Base64.js";
@@ -9,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";
@@ -20,16 +20,15 @@ import NetBIOS from "../../operations/NetBIOS.js";
import Numberwang from "../../operations/Numberwang.js";
import OS from "../../operations/OS.js";
import OTP from "../../operations/OTP.js";
import PHP from "../../operations/PHP.js";
import QuotedPrintable from "../../operations/QuotedPrintable.js";
import Rotate from "../../operations/Rotate.js";
import SeqUtils from "../../operations/SeqUtils.js";
import StrUtils from "../../operations/StrUtils.js";
import Tidy from "../../operations/Tidy.js";
import Unicode from "../../operations/Unicode.js";
import URL_ from "../../operations/URL.js";
import UUID from "../../operations/UUID.js";
/**
* Default module.
*
@@ -38,8 +37,8 @@ import UUID from "../../operations/UUID.js";
*
* Libraries:
* - Utils.js
* - CryptoJS
* - otp
* - crypto
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
@@ -77,9 +76,6 @@ OpModules.Default = {
"From HTML Entity": HTML.runFromEntity,
"Strip HTML tags": HTML.runStripTags,
"Parse colour code": HTML.runParseColourCode,
"URL Encode": URL_.runTo,
"URL Decode": URL_.runFrom,
"Parse URI": URL_.runParse,
"Unescape Unicode Characters": Unicode.runUnescape,
"To Quoted Printable": QuotedPrintable.runTo,
"From Quoted Printable": QuotedPrintable.runFrom,
@@ -102,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,
@@ -135,17 +130,10 @@ 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,
"Chi Square": Entropy.runChiSq,
"Detect File Type": FileType.runDetect,
"Scan for Embedded Files": FileType.runScanForEmbeddedFiles,
"Generate UUID": UUID.runGenerateV4,
@@ -154,10 +142,20 @@ OpModules.Default = {
"Generate HOTP": OTP.runHOTP,
"Fork": FlowControl.runFork,
"Merge": FlowControl.runMerge,
"Register": FlowControl.runRegister,
"Label": FlowControl.runComment,
"Jump": FlowControl.runJump,
"Conditional Jump": FlowControl.runCondJump,
"Return": FlowControl.runReturn,
"Comment": FlowControl.runComment,
"PHP Deserialize": PHP.runDeserialize,
"Sum": Arithmetic.runSum,
"Subtract": Arithmetic.runSub,
"Multiply": Arithmetic.runMulti,
"Divide": Arithmetic.runDiv,
"Mean": Arithmetic.runMean,
"Median": Arithmetic.runMedian,
"Standard Deviation": Arithmetic.runStdDev,
/*

View File

@@ -31,6 +31,9 @@ OpModules.Hashing = {
"Keccak": Hash.runKeccak,
"Shake": Hash.runShake,
"RIPEMD": Hash.runRIPEMD,
"HAS-160": Hash.runHAS,
"Whirlpool": Hash.runWhirlpool,
"Snefru": Hash.runSnefru,
"HMAC": Hash.runHMAC,
"Fletcher-8 Checksum": Checksum.runFletcher8,
"Fletcher-16 Checksum": Checksum.runFletcher16,

View File

@@ -18,6 +18,9 @@ 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";
Object.assign(
OpModules,
@@ -31,7 +34,10 @@ Object.assign(
HTTPModule,
ImageModule,
JSBNModule,
PublicKeyModule
PublicKeyModule,
RegexModule,
ShellcodeModule,
URLModule
);
export default OpModules;

View File

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

View File

@@ -0,0 +1,20 @@
import Shellcode from "../../operations/Shellcode.js";
/**
* Shellcode module.
*
* Libraries:
* - DisassembleX86-64.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Shellcode = {
"Disassemble x86": Shellcode.runDisassemble,
};
export default OpModules;

View File

@@ -0,0 +1,23 @@
import URL_ from "../../operations/URL.js";
/**
* URL module.
*
* Libraries:
* - Utils.js
* - url
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.URL = {
"URL Encode": URL_.runTo,
"URL Decode": URL_.runFrom,
"Parse URI": URL_.runParse,
};
export default OpModules;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,253 @@
import Utils from "../Utils.js";
import BigNumber from "bignumber.js";
/**
* Math operations on numbers.
*
* @author bwhitn [brian.m.whitney@outlook.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Arithmetic = {
/**
* @constant
* @default
*/
DELIM_OPTIONS: ["Line feed", "Space", "Comma", "Semi-colon", "Colon", "CRLF"],
/**
* Splits a string based on a delimiter and calculates the sum of numbers.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runSum: function(input, args) {
const val = Arithmetic._sum(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and subtracts all the numbers.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runSub: function(input, args) {
let val = Arithmetic._sub(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and multiplies the numbers.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runMulti: function(input, args) {
let val = Arithmetic._multi(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and divides the numbers.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runDiv: function(input, args) {
let val = Arithmetic._div(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and computes the mean (average).
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runMean: function(input, args) {
let val = Arithmetic._mean(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and finds the median.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runMedian: function(input, args) {
let val = Arithmetic._median(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* splits a string based on a delimiter and computes the standard deviation.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runStdDev: function(input, args) {
let val = Arithmetic._stdDev(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Converts a string array to a number array.
*
* @private
* @param {string[]} input
* @param {string} delim
* @returns {BigNumber[]}
*/
_createNumArray: function(input, delim) {
delim = Utils.charRep[delim || "Space"];
let splitNumbers = input.split(delim),
numbers = [],
num;
for (let i = 0; i < splitNumbers.length; i++) {
try {
num = BigNumber(splitNumbers[i].trim());
if (!num.isNaN()) {
numbers.push(num);
}
} catch (err) {
// This line is not a valid number
}
}
return numbers;
},
/**
* Adds an array of numbers and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_sum: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.plus(curr));
}
},
/**
* Subtracts an array of numbers and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_sub: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.minus(curr));
}
},
/**
* Multiplies an array of numbers and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_multi: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.times(curr));
}
},
/**
* Divides an array of numbers and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_div: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.div(curr));
}
},
/**
* Computes mean of a number array and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_mean: function(data) {
if (data.length > 0) {
return Arithmetic._sum(data).div(data.length);
}
},
/**
* Computes median of a number array and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_median: function (data) {
if ((data.length % 2) === 0 && data.length > 0) {
let first, second;
data.sort(function(a, b){
return a.minus(b);
});
first = data[Math.floor(data.length / 2)];
second = data[Math.floor(data.length / 2) - 1];
return Arithmetic._mean([first, second]);
} else {
return data[Math.floor(data.length / 2)];
}
},
/**
* Computes standard deviation of a number array and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_stdDev: function (data) {
if (data.length > 0) {
let avg = Arithmetic._mean(data);
let devSum = new BigNumber(0);
for (let i = 0; i < data.length; i++) {
devSum = devSum.plus(data[i].minus(avg).pow(2));
}
return devSum.div(data.length).sqrt();
}
},
};
export default Arithmetic;

View File

@@ -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();
@@ -134,11 +135,11 @@ const BCD = {
switch (outputFormat) {
case "Nibbles":
return nibbles.map(n => {
return Utils.padLeft(n.toString(2), 4);
return n.toString(2).padStart(4, "0");
}).join(" ");
case "Bytes":
return bytes.map(b => {
return Utils.padLeft(b.toString(2), 8);
return b.toString(2).padStart(8, "0");
}).join(" ");
case "Raw":
default:
@@ -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);
},
};

View File

@@ -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;

View File

@@ -40,13 +40,13 @@ const Base64 = {
/**
* To Base64 operation.
*
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
const alphabet = args[0] || Base64.ALPHABET;
return Utils.toBase64(input, alphabet);
return Utils.toBase64(new Uint8Array(input), alphabet);
},

View File

@@ -67,7 +67,7 @@ const BitwiseOp = {
* @constant
* @default
*/
KEY_FORMAT: ["Hex", "Base64", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1"],
KEY_FORMAT: ["Hex", "Base64", "UTF8", "Latin1"],
/**
* XOR operation.
@@ -77,12 +77,10 @@ const BitwiseOp = {
* @returns {byteArray}
*/
runXor: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || ""),
const key = Utils.convertToByteArray(args[0].string || "", args[0].option),
scheme = args[1],
nullPreserving = args[2];
key = Utils.wordArrayToByteArray(key);
return BitwiseOp._bitOp(input, key, BitwiseOp._xor, nullPreserving, scheme);
},
@@ -200,8 +198,7 @@ const BitwiseOp = {
* @returns {byteArray}
*/
runAnd: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || "");
key = Utils.wordArrayToByteArray(key);
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
return BitwiseOp._bitOp(input, key, BitwiseOp._and);
},
@@ -215,8 +212,7 @@ const BitwiseOp = {
* @returns {byteArray}
*/
runOr: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || "");
key = Utils.wordArrayToByteArray(key);
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
return BitwiseOp._bitOp(input, key, BitwiseOp._or);
},
@@ -230,8 +226,7 @@ const BitwiseOp = {
* @returns {byteArray}
*/
runAdd: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || "");
key = Utils.wordArrayToByteArray(key);
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
return BitwiseOp._bitOp(input, key, BitwiseOp._add);
},
@@ -245,8 +240,7 @@ const BitwiseOp = {
* @returns {byteArray}
*/
runSub: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || "");
key = Utils.wordArrayToByteArray(key);
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
return BitwiseOp._bitOp(input, key, BitwiseOp._sub);
},

View File

@@ -1,4 +1,3 @@
/* globals app */
import Utils from "../Utils.js";
@@ -32,13 +31,13 @@ const ByteRepr = {
/**
* To Hex operation.
*
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runToHex: function(input, args) {
const delim = Utils.charRep[args[0] || "Space"];
return Utils.toHex(input, delim, 2);
return Utils.toHex(new Uint8Array(input), delim, 2);
},
@@ -187,7 +186,7 @@ const ByteRepr = {
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
if (delim === "0x" || delim === "\\x") {
pos[0].start += 2;
pos[0].end += 2;
pos[0].end += 2;
}
return pos;
},
@@ -267,7 +266,7 @@ const ByteRepr = {
padding = 8;
for (let i = 0; i < input.length; i++) {
output += Utils.pad(input[i].toString(2), padding) + delim;
output += input[i].toString(2).padStart(padding, "0") + delim;
}
if (delim.length) {

View File

@@ -1,6 +1,4 @@
import cptable from "../lib/js-codepage/cptable.js";
import Utils from "../Utils.js";
import CryptoJS from "crypto-js";
/**

View File

@@ -1,6 +1,8 @@
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";
/**
@@ -18,132 +20,27 @@ const Cipher = {
* @constant
* @default
*/
IO_FORMAT1: ["Hex", "Base64", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1"],
IO_FORMAT1: ["Hex", "UTF8", "Latin1", "Base64"],
/**
* @constant
* @default
*/
IO_FORMAT2: ["UTF8", "Latin1", "Hex", "Base64"],
/**
* @constant
* @default
*/
IO_FORMAT3: ["Raw", "Hex"],
/**
* @constant
* @default
*/
IO_FORMAT4: ["Hex", "Raw"],
/**
* @constant
* @default
*/
IO_FORMAT2: ["UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1", "Hex", "Base64"],
/**
* @constant
* @default
*/
IO_FORMAT3: ["Hex", "Base64", "UTF16", "UTF16LE", "UTF16BE", "Latin1"],
/**
* @constant
* @default
*/
IO_FORMAT4: ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"],
/**
* @constant
* @default
*/
MODES: ["CBC", "CFB", "CTR", "OFB", "ECB"],
/**
* @constant
* @default
*/
PADDING: ["Pkcs7", "Iso97971", "AnsiX923", "Iso10126", "ZeroPadding", "NoPadding"],
/**
* @constant
* @default
*/
RESULT_TYPE: ["Show all", "Ciphertext", "Key", "IV", "Salt"],
/**
* Runs encryption operations using the CryptoJS framework.
*
* @private
* @param {function} algo - The CryptoJS algorithm to use
* @param {byteArray} input
* @param {function} args
* @returns {string}
*/
_enc: function (algo, input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || ""),
iv = Utils.format[args[1].option].parse(args[1].string || ""),
salt = Utils.format[args[2].option].parse(args[2].string || ""),
mode = CryptoJS.mode[args[3]],
padding = CryptoJS.pad[args[4]],
resultOption = args[5].toLowerCase(),
outputFormat = args[6];
if (iv.sigBytes === 0) {
// Use passphrase rather than key. Need to convert it to a string.
key = key.toString(CryptoJS.enc.Latin1);
}
const encrypted = algo.encrypt(input, key, {
salt: salt.sigBytes > 0 ? salt : false,
iv: iv.sigBytes > 0 ? iv : null,
mode: mode,
padding: padding
});
let result = "";
if (resultOption === "show all") {
result += "Key: " + encrypted.key.toString(Utils.format[outputFormat]);
result += "\nIV: " + encrypted.iv.toString(Utils.format[outputFormat]);
if (encrypted.salt) result += "\nSalt: " + encrypted.salt.toString(Utils.format[outputFormat]);
result += "\n\nCiphertext: " + encrypted.ciphertext.toString(Utils.format[outputFormat]);
} else {
result = encrypted[resultOption].toString(Utils.format[outputFormat]);
}
return result;
},
/**
* Runs decryption operations using the CryptoJS framework.
*
* @private
* @param {function} algo - The CryptoJS algorithm to use
* @param {byteArray} input
* @param {function} args
* @returns {string}
*/
_dec: function (algo, input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || ""),
iv = Utils.format[args[1].option].parse(args[1].string || ""),
salt = Utils.format[args[2].option].parse(args[2].string || ""),
mode = CryptoJS.mode[args[3]],
padding = CryptoJS.pad[args[4]],
inputFormat = args[5],
outputFormat = args[6];
// The ZeroPadding option causes a crash when the input length is 0
if (!input.length) {
return "No input";
}
const ciphertext = Utils.format[inputFormat].parse(input);
if (iv.sigBytes === 0) {
// Use passphrase rather than key. Need to convert it to a string.
key = key.toString(CryptoJS.enc.Latin1);
}
const decrypted = algo.decrypt({
ciphertext: ciphertext,
salt: salt.sigBytes > 0 ? salt : false
}, key, {
iv: iv.sigBytes > 0 ? iv : null,
mode: mode,
padding: padding
});
let result;
try {
result = decrypted.toString(Utils.format[outputFormat]);
} catch (err) {
result = "Decrypt error: " + err.message;
}
return result;
},
AES_MODES: ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"],
/**
* AES Encrypt operation.
@@ -153,7 +50,41 @@ const Cipher = {
* @returns {string}
*/
runAesEnc: function (input, args) {
return Cipher._enc(CryptoJS.AES, input, args);
const key = Utils.convertToByteArray(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4];
if ([16, 24, 32].indexOf(key.length) < 0) {
return `Invalid key length: ${key.length} bytes
The following algorithms will be used based on the size of the key:
16 bytes = AES-128
24 bytes = AES-192
32 bytes = AES-256`;
}
input = Utils.convertToByteString(input, inputType);
const cipher = forge.cipher.createCipher("AES-" + mode, key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(input));
cipher.finish();
if (outputType === "Hex") {
if (mode === "GCM") {
return cipher.output.toHex() + "\n\n" +
"Tag: " + cipher.mode.tag.toHex();
}
return cipher.output.toHex();
} else {
if (mode === "GCM") {
return cipher.output.getBytes() + "\n\n" +
"Tag: " + cipher.mode.tag.getBytes();
}
return cipher.output.getBytes();
}
},
@@ -165,10 +96,46 @@ const Cipher = {
* @returns {string}
*/
runAesDec: function (input, args) {
return Cipher._dec(CryptoJS.AES, input, args);
const key = Utils.convertToByteArray(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4],
gcmTag = Utils.convertToByteString(args[5].string, args[5].option);
if ([16, 24, 32].indexOf(key.length) < 0) {
return `Invalid key length: ${key.length} bytes
The following algorithms will be used based on the size of the key:
16 bytes = AES-128
24 bytes = AES-192
32 bytes = AES-256`;
}
input = Utils.convertToByteString(input, inputType);
const decipher = forge.cipher.createDecipher("AES-" + mode, key);
decipher.start({
iv: iv,
tag: gcmTag
});
decipher.update(forge.util.createBuffer(input));
const result = decipher.finish();
if (result) {
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
} else {
return "Unable to decrypt input with these parameters.";
}
},
/**
* @constant
* @default
*/
DES_MODES: ["CBC", "CFB", "OFB", "CTR", "ECB"],
/**
* DES Encrypt operation.
*
@@ -177,7 +144,27 @@ const Cipher = {
* @returns {string}
*/
runDesEnc: function (input, args) {
return Cipher._enc(CryptoJS.DES, input, args);
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4];
if (key.length !== 8) {
return `Invalid key length: ${key.length} bytes
DES uses a key length of 8 bytes (64 bits).
Triple DES uses a key length of 24 bytes (192 bits).`;
}
input = Utils.convertToByteString(input, inputType);
const cipher = forge.cipher.createCipher("DES-" + mode, key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(input));
cipher.finish();
return outputType === "Hex" ? cipher.output.toHex() : cipher.output.getBytes();
},
@@ -189,7 +176,31 @@ const Cipher = {
* @returns {string}
*/
runDesDec: function (input, args) {
return Cipher._dec(CryptoJS.DES, input, args);
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4];
if (key.length !== 8) {
return `Invalid key length: ${key.length} bytes
DES uses a key length of 8 bytes (64 bits).
Triple DES uses a key length of 24 bytes (192 bits).`;
}
input = Utils.convertToByteString(input, inputType);
const decipher = forge.cipher.createDecipher("DES-" + mode, key);
decipher.start({iv: iv});
decipher.update(forge.util.createBuffer(input));
const result = decipher.finish();
if (result) {
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
} else {
return "Unable to decrypt input with these parameters.";
}
},
@@ -201,7 +212,27 @@ const Cipher = {
* @returns {string}
*/
runTripleDesEnc: function (input, args) {
return Cipher._enc(CryptoJS.TripleDES, input, args);
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4];
if (key.length !== 24) {
return `Invalid key length: ${key.length} bytes
Triple DES uses a key length of 24 bytes (192 bits).
DES uses a key length of 8 bytes (64 bits).`;
}
input = Utils.convertToByteString(input, inputType);
const cipher = forge.cipher.createCipher("3DES-" + mode, key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(input));
cipher.finish();
return outputType === "Hex" ? cipher.output.toHex() : cipher.output.getBytes();
},
@@ -213,31 +244,79 @@ const Cipher = {
* @returns {string}
*/
runTripleDesDec: function (input, args) {
return Cipher._dec(CryptoJS.TripleDES, input, args);
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4];
if (key.length !== 24) {
return `Invalid key length: ${key.length} bytes
Triple DES uses a key length of 24 bytes (192 bits).
DES uses a key length of 8 bytes (64 bits).`;
}
input = Utils.convertToByteString(input, inputType);
const decipher = forge.cipher.createDecipher("3DES-" + mode, key);
decipher.start({iv: iv});
decipher.update(forge.util.createBuffer(input));
const result = decipher.finish();
if (result) {
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
} else {
return "Unable to decrypt input with these parameters.";
}
},
/**
* Rabbit Encrypt operation.
* RC2 Encrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runRabbitEnc: function (input, args) {
return Cipher._enc(CryptoJS.Rabbit, input, args);
runRc2Enc: function (input, args) {
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteString(args[1].string, args[1].option),
inputType = args[2],
outputType = args[3],
cipher = forge.rc2.createEncryptionCipher(key);
input = Utils.convertToByteString(input, inputType);
cipher.start(iv || null);
cipher.update(forge.util.createBuffer(input));
cipher.finish();
return outputType === "Hex" ? cipher.output.toHex() : cipher.output.getBytes();
},
/**
* Rabbit Decrypt operation.
* RC2 Decrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runRabbitDec: function (input, args) {
return Cipher._dec(CryptoJS.Rabbit, input, args);
runRc2Dec: function (input, args) {
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteString(args[1].string, args[1].option),
inputType = args[2],
outputType = args[3],
decipher = forge.rc2.createDecryptionCipher(key);
input = Utils.convertToByteString(input, inputType);
decipher.start(iv || null);
decipher.update(forge.util.createBuffer(input));
decipher.finish();
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
},
@@ -245,12 +324,29 @@ const Cipher = {
* @constant
* @default
*/
BLOWFISH_MODES: ["ECB", "CBC", "PCBC", "CFB", "OFB", "CTR"],
BLOWFISH_MODES: ["CBC", "PCBC", "CFB", "OFB", "CTR", "ECB"],
/**
* @constant
* @default
*/
BLOWFISH_OUTPUT_TYPES: ["Base64", "Hex", "String", "Raw"],
BLOWFISH_OUTPUT_TYPES: ["Hex", "Base64", "Raw"],
/**
* Lookup table for Blowfish output types.
*
* @private
*/
_BLOWFISH_OUTPUT_TYPE_LOOKUP: {
Base64: 0, Hex: 1, String: 2, Raw: 3
},
/**
* Lookup table for Blowfish modes.
*
* @private
*/
_BLOWFISH_MODE_LOOKUP: {
ECB: 0, CBC: 1, PCBC: 2, CFB: 3, OFB: 4, CTR: 5
},
/**
* Blowfish Encrypt operation.
@@ -260,19 +356,24 @@ const Cipher = {
* @returns {string}
*/
runBlowfishEnc: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1),
mode = args[1],
outputFormat = args[2];
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4];
if (key.length === 0) return "Enter a key";
let encHex = Blowfish.encrypt(input, key, {
outputType: 1,
cipherMode: Cipher.BLOWFISH_MODES.indexOf(mode)
}),
enc = CryptoJS.enc.Hex.parse(encHex);
input = Utils.convertToByteString(input, inputType);
return enc.toString(Utils.format[outputFormat]);
Blowfish.setIV(Utils.toBase64(iv), 0);
const enc = Blowfish.encrypt(input, key, {
outputType: Cipher._BLOWFISH_OUTPUT_TYPE_LOOKUP[outputType],
cipherMode: Cipher._BLOWFISH_MODE_LOOKUP[mode]
});
return outputType === "Raw" ? Utils.byteArrayToChars(enc) : enc ;
},
@@ -284,18 +385,24 @@ const Cipher = {
* @returns {string}
*/
runBlowfishDec: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1),
mode = args[1],
inputFormat = args[2];
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4];
if (key.length === 0) return "Enter a key";
input = Utils.format[inputFormat].parse(input);
input = inputType === "Raw" ? Utils.strToByteArray(input) : input;
return Blowfish.decrypt(input.toString(CryptoJS.enc.Base64), key, {
outputType: 0, // This actually means inputType. The library is weird.
cipherMode: Cipher.BLOWFISH_MODES.indexOf(mode)
Blowfish.setIV(Utils.toBase64(iv), 0);
const result = Blowfish.decrypt(input, key, {
outputType: Cipher._BLOWFISH_OUTPUT_TYPE_LOOKUP[inputType], // This actually means inputType. The library is weird.
cipherMode: Cipher._BLOWFISH_MODE_LOOKUP[mode]
});
return outputType === "Hex" ? Utils.toHexFast(Utils.strToByteArray(result)) : result;
},
@@ -303,7 +410,7 @@ const Cipher = {
* @constant
* @default
*/
KDF_KEY_SIZE: 256,
KDF_KEY_SIZE: 128,
/**
* @constant
* @default
@@ -313,7 +420,7 @@ const Cipher = {
* @constant
* @default
*/
HASHERS: ["MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "RIPEMD160"],
HASHERS: ["SHA1", "SHA256", "SHA384", "SHA512", "MD5"],
/**
* Derive PBKDF2 key operation.
@@ -323,20 +430,15 @@ const Cipher = {
* @returns {string}
*/
runPbkdf2: function (input, args) {
let keySize = args[0] / 32,
iterations = args[1],
hasher = args[2],
salt = CryptoJS.enc.Hex.parse(args[3] || ""),
inputFormat = args[4],
outputFormat = args[5],
passphrase = Utils.format[inputFormat].parse(input),
key = CryptoJS.PBKDF2(passphrase, salt, {
keySize: keySize,
hasher: CryptoJS.algo[hasher],
iterations: iterations,
});
const passphrase = Utils.convertToByteString(args[0].string, args[0].option),
keySize = args[1],
iterations = args[2],
hasher = args[3],
salt = Utils.convertToByteString(args[4].string, args[4].option) ||
forge.random.getBytesSync(keySize),
derivedKey = forge.pkcs5.pbkdf2(passphrase, salt, iterations, keySize / 8, hasher.toLowerCase());
return key.toString(Utils.format[outputFormat]);
return forge.util.bytesToHex(derivedKey);
},
@@ -348,23 +450,33 @@ const Cipher = {
* @returns {string}
*/
runEvpkdf: function (input, args) {
let keySize = args[0] / 32,
iterations = args[1],
hasher = args[2],
salt = CryptoJS.enc.Hex.parse(args[3] || ""),
inputFormat = args[4],
outputFormat = args[5],
passphrase = Utils.format[inputFormat].parse(input),
const passphrase = Utils.convertToByteString(args[0].string, args[0].option),
keySize = args[1] / 32,
iterations = args[2],
hasher = args[3],
salt = Utils.convertToByteString(args[4].string, args[4].option),
key = CryptoJS.EvpKDF(passphrase, salt, {
keySize: keySize,
hasher: CryptoJS.algo[hasher],
iterations: iterations,
});
return key.toString(Utils.format[outputFormat]);
return key.toString(CryptoJS.enc.Hex);
},
/**
* @constant
* @default
*/
RC4_KEY_FORMAT: ["UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1", "Hex", "Base64"],
/**
* @constant
* @default
*/
CJS_IO_FORMAT: ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"],
/**
* RC4 operation.
*
@@ -373,11 +485,11 @@ const Cipher = {
* @returns {string}
*/
runRc4: function (input, args) {
let message = Utils.format[args[1]].parse(input),
passphrase = Utils.format[args[0].option].parse(args[0].string),
let message = Cipher._format[args[1]].parse(input),
passphrase = Cipher._format[args[0].option].parse(args[0].string),
encrypted = CryptoJS.RC4.encrypt(message, passphrase);
return encrypted.ciphertext.toString(Utils.format[args[2]]);
return encrypted.ciphertext.toString(Cipher._format[args[2]]);
},
@@ -395,12 +507,63 @@ const Cipher = {
* @returns {string}
*/
runRc4drop: function (input, args) {
let message = Utils.format[args[1]].parse(input),
passphrase = Utils.format[args[0].option].parse(args[0].string),
let message = Cipher._format[args[1]].parse(input),
passphrase = Cipher._format[args[0].option].parse(args[0].string),
drop = args[3],
encrypted = CryptoJS.RC4Drop.encrypt(message, passphrase, { drop: drop });
return encrypted.ciphertext.toString(Utils.format[args[2]]);
return encrypted.ciphertext.toString(Cipher._format[args[2]]);
},
/**
* @constant
* @default
*/
PRNG_BYTES: 32,
/**
* @constant
* @default
*/
PRNG_OUTPUT: ["Hex", "Integer", "Byte array", "Raw"],
/**
* Pseudo-Random Number Generator operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runPRNG: function(input, args) {
const numBytes = args[0],
outputAs = args[1];
let bytes;
if (ENVIRONMENT_IS_WORKER() && self.crypto) {
bytes = self.crypto.getRandomValues(new Uint8Array(numBytes));
bytes = Utils.arrayBufferToStr(bytes.buffer);
} else {
bytes = forge.random.getBytesSync(numBytes);
}
let value = new BigNumber(0),
i;
switch (outputAs) {
case "Hex":
return forge.util.bytesToHex(bytes);
case "Integer":
for (i = bytes.length - 1; i >= 0; i--) {
value = value.mul(256).plus(bytes.charCodeAt(i));
}
return value.toFixed();
case "Byte array":
return JSON.stringify(Utils.strToCharcode(bytes));
case "Raw":
default:
return bytes;
}
},
@@ -783,6 +946,23 @@ const Cipher = {
return output;
},
/**
* A mapping of string formats to their classes in the CryptoJS library.
*
* @private
* @constant
*/
_format: {
"Hex": CryptoJS.enc.Hex,
"Base64": CryptoJS.enc.Base64,
"UTF8": CryptoJS.enc.Utf8,
"UTF16": CryptoJS.enc.Utf16,
"UTF16LE": CryptoJS.enc.Utf16LE,
"UTF16BE": CryptoJS.enc.Utf16BE,
"Latin1": CryptoJS.enc.Latin1,
},
};
export default Cipher;
@@ -827,3 +1007,27 @@ CryptoJS.kdf.OpenSSL.execute = function (password, keySize, ivSize, salt) {
// Return params
return CryptoJS.lib.CipherParams.create({ key: key, iv: iv, salt: salt });
};
/**
* Override for the CryptoJS Hex encoding parser to remove whitespace before attempting to parse
* the hex string.
*
* @param {string} hexStr
* @returns {CryptoJS.lib.WordArray}
*/
CryptoJS.enc.Hex.parse = function (hexStr) {
// Remove whitespace
hexStr = hexStr.replace(/\s/g, "");
// Shortcut
const hexStrLength = hexStr.length;
// Convert
const words = [];
for (let i = 0; i < hexStrLength; i += 2) {
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
}
return new CryptoJS.lib.WordArray.init(words, hexStrLength / 2);
};

View File

@@ -2,9 +2,10 @@ import {camelCase, kebabCase, snakeCase} from "lodash";
import Utils from "../Utils.js";
import vkbeautify from "vkbeautify";
import {DOMParser as dom} from "xmldom";
import {DOMParser} from "xmldom";
import xpath from "xpath";
import jpath from "jsonpath";
import nwmatcher from "nwmatcher";
import prettyPrintOne from "imports-loader?window=>global!exports-loader?prettyPrintOne!google-code-prettify/bin/prettify.min.js";
@@ -336,7 +337,7 @@ const Code = {
let doc;
try {
doc = new dom().parseFromString(input);
doc = new DOMParser().parseFromString(input, "application/xml");
} catch (err) {
return "Invalid input XML.";
}
@@ -423,7 +424,7 @@ const Code = {
let query = args[0],
delimiter = args[1],
parser = new DOMParser(),
html,
dom,
result;
if (!query.length || !input.length) {
@@ -431,32 +432,32 @@ const Code = {
}
try {
html = parser.parseFromString(input, "text/html");
dom = parser.parseFromString(input);
} catch (err) {
return "Invalid input HTML.";
}
try {
result = html.querySelectorAll(query);
const matcher = nwmatcher({document: dom});
result = matcher.select(query, dom);
} catch (err) {
return "Invalid CSS Selector. Details:\n" + err.message;
}
const nodeToString = function(node) {
return node.toString();
/* xmldom does not return the outerHTML value.
switch (node.nodeType) {
case Node.ELEMENT_NODE: return node.outerHTML;
case Node.ATTRIBUTE_NODE: return node.value;
case Node.COMMENT_NODE: return node.data;
case Node.TEXT_NODE: return node.wholeText;
case Node.DOCUMENT_NODE: return node.outerHTML;
case node.ELEMENT_NODE: return node.outerHTML;
case node.ATTRIBUTE_NODE: return node.value;
case node.TEXT_NODE: return node.wholeText;
case node.COMMENT_NODE: return node.data;
case node.DOCUMENT_NODE: return node.outerHTML;
default: throw new Error("Unknown Node Type: " + node.nodeType);
}
}*/
};
return Array.apply(null, Array(result.length))
.map(function(_, i) {
return result[i];
})
return result
.map(nodeToString)
.join(delimiter);
},

View File

@@ -418,9 +418,9 @@ const Compress = {
}
};
const fileSize = Utils.padLeft(input.length.toString(8), 11, "0");
const fileSize = input.length.toString(8).padStart(11, "0");
const currentUnixTimestamp = Math.floor(Date.now() / 1000);
const lastModTime = Utils.padLeft(currentUnixTimestamp.toString(8), 11, "0");
const lastModTime = currentUnixTimestamp.toString(8).padStart(11, "0");
const file = {
fileName: Utils.padBytesRight(args[0], 100),
@@ -452,7 +452,7 @@ const Compress = {
}
});
}
checksum = Utils.padBytesRight(Utils.padLeft(checksum.toString(8), 7, "0"), 8);
checksum = Utils.padBytesRight(checksum.toString(8).padStart(7, "0"), 8);
file.checksum = checksum;
const tarball = new Tarball();

View File

@@ -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]);
},
};

View File

@@ -192,268 +192,270 @@ const DateTime = {
/**
* @constant
*/
FORMAT_EXAMPLES: "Format string tokens:\n\n\
<table class='table table-striped table-hover table-condensed table-bordered' style='font-family: sans-serif'>\
<thead>\
<tr>\
<th>Category</th>\
<th>Token</th>\
<th>Output</th>\
</tr>\
</thead>\
<tbody>\
<tr>\
<td><b>Month</b></td>\
<td>M</td>\
<td>1 2 ... 11 12</td>\
</tr>\
<tr>\
<td></td>\
<td>Mo</td>\
<td>1st 2nd ... 11th 12th</td>\
</tr>\
<tr>\
<td></td>\
<td>MM</td>\
<td>01 02 ... 11 12</td>\
</tr>\
<tr>\
<td></td>\
<td>MMM</td>\
<td>Jan Feb ... Nov Dec</td>\
</tr>\
<tr>\
<td></td>\
<td>MMMM</td>\
<td>January February ... November December</td>\
</tr>\
<tr>\
<td><b>Quarter</b></td>\
<td>Q</td>\
<td>1 2 3 4</td>\
</tr>\
<tr>\
<td><b>Day of Month</b></td>\
<td>D</td>\
<td>1 2 ... 30 31</td>\
</tr>\
<tr>\
<td></td>\
<td>Do</td>\
<td>1st 2nd ... 30th 31st</td>\
</tr>\
<tr>\
<td></td>\
<td>DD</td>\
<td>01 02 ... 30 31</td>\
</tr>\
<tr>\
<td><b>Day of Year</b></td>\
<td>DDD</td>\
<td>1 2 ... 364 365</td>\
</tr>\
<tr>\
<td></td>\
<td>DDDo</td>\
<td>1st 2nd ... 364th 365th</td>\
</tr>\
<tr>\
<td></td>\
<td>DDDD</td>\
<td>001 002 ... 364 365</td>\
</tr>\
<tr>\
<td><b>Day of Week</b></td>\
<td>d</td>\
<td>0 1 ... 5 6</td>\
</tr>\
<tr>\
<td></td>\
<td>do</td>\
<td>0th 1st ... 5th 6th</td>\
</tr>\
<tr>\
<td></td>\
<td>dd</td>\
<td>Su Mo ... Fr Sa</td>\
</tr>\
<tr>\
<td></td>\
<td>ddd</td>\
<td>Sun Mon ... Fri Sat</td>\
</tr>\
<tr>\
<td></td>\
<td>dddd</td>\
<td>Sunday Monday ... Friday Saturday</td>\
</tr>\
<tr>\
<td><b>Day of Week (Locale)</b></td>\
<td>e</td>\
<td>0 1 ... 5 6</td>\
</tr>\
<tr>\
<td><b>Day of Week (ISO)</b></td>\
<td>E</td>\
<td>1 2 ... 6 7</td>\
</tr>\
<tr>\
<td><b>Week of Year</b></td>\
<td>w</td>\
<td>1 2 ... 52 53</td>\
</tr>\
<tr>\
<td></td>\
<td>wo</td>\
<td>1st 2nd ... 52nd 53rd</td>\
</tr>\
<tr>\
<td></td>\
<td>ww</td>\
<td>01 02 ... 52 53</td>\
</tr>\
<tr>\
<td><b>Week of Year (ISO)</b></td>\
<td>W</td>\
<td>1 2 ... 52 53</td>\
</tr>\
<tr>\
<td></td>\
<td>Wo</td>\
<td>1st 2nd ... 52nd 53rd</td>\
</tr>\
<tr>\
<td></td>\
<td>WW</td>\
<td>01 02 ... 52 53</td>\
</tr>\
<tr>\
<td><b>Year</b></td>\
<td>YY</td>\
<td>70 71 ... 29 30</td>\
</tr>\
<tr>\
<td></td>\
<td>YYYY</td>\
<td>1970 1971 ... 2029 2030</td>\
</tr>\
<tr>\
<td><b>Week Year</b></td>\
<td>gg</td>\
<td>70 71 ... 29 30</td>\
</tr>\
<tr>\
<td></td>\
<td>gggg</td>\
<td>1970 1971 ... 2029 2030</td>\
</tr>\
<tr>\
<td><b>Week Year (ISO)</b></td>\
<td>GG</td>\
<td>70 71 ... 29 30</td>\
</tr>\
<tr>\
<td></td>\
<td>GGGG</td>\
<td>1970 1971 ... 2029 2030</td>\
</tr>\
<tr>\
<td><b>AM/PM</b></td>\
<td>A</td>\
<td>AM PM</td>\
</tr>\
<tr>\
<td></td>\
<td>a</td>\
<td>am pm</td>\
</tr>\
<tr>\
<td><b>Hour</b></td>\
<td>H</td>\
<td>0 1 ... 22 23</td>\
</tr>\
<tr>\
<td></td>\
<td>HH</td>\
<td>00 01 ... 22 23</td>\
</tr>\
<tr>\
<td></td>\
<td>h</td>\
<td>1 2 ... 11 12</td>\
</tr>\
<tr>\
<td></td>\
<td>hh</td>\
<td>01 02 ... 11 12</td>\
</tr>\
<tr>\
<td><b>Minute</b></td>\
<td>m</td>\
<td>0 1 ... 58 59</td>\
</tr>\
<tr>\
<td></td>\
<td>mm</td>\
<td>00 01 ... 58 59</td>\
</tr>\
<tr>\
<td><b>Second</b></td>\
<td>s</td>\
<td>0 1 ... 58 59</td>\
</tr>\
<tr>\
<td></td>\
<td>ss</td>\
<td>00 01 ... 58 59</td>\
</tr>\
<tr>\
<td><b>Fractional Second</b></td>\
<td>S</td>\
<td>0 1 ... 8 9</td>\
</tr>\
<tr>\
<td></td>\
<td>SS</td>\
<td>00 01 ... 98 99</td>\
</tr>\
<tr>\
<td></td>\
<td>SSS</td>\
<td>000 001 ... 998 999</td>\
</tr>\
<tr>\
<td></td>\
<td>SSSS ... SSSSSSSSS</td>\
<td>000[0..] 001[0..] ... 998[0..] 999[0..]</td>\
</tr>\
<tr>\
<td><b>Timezone</b></td>\
<td>z or zz</td>\
<td>EST CST ... MST PST</td>\
</tr>\
<tr>\
<td></td>\
<td>Z</td>\
<td>-07:00 -06:00 ... +06:00 +07:00</td>\
</tr>\
<tr>\
<td></td>\
<td>ZZ</td>\
<td>-0700 -0600 ... +0600 +0700</td>\
</tr>\
<tr>\
<td><b>Unix Timestamp</b></td>\
<td>X</td>\
<td>1360013296</td>\
</tr>\
<tr>\
<td><b>Unix Millisecond Timestamp</b></td>\
<td>x</td>\
<td>1360013296123</td>\
</tr>\
</tbody>\
</table>",
FORMAT_EXAMPLES: `Format string tokens:
<table class="table table-striped table-hover table-condensed table-bordered" style="font-family: sans-serif">
<thead>
<tr>
<th>Category</th>
<th>Token</th>
<th>Output</th>
</tr>
</thead>
<tbody>
<tr>
<td><b>Month</b></td>
<td>M</td>
<td>1 2 ... 11 12</td>
</tr>
<tr>
<td></td>
<td>Mo</td>
<td>1st 2nd ... 11th 12th</td>
</tr>
<tr>
<td></td>
<td>MM</td>
<td>01 02 ... 11 12</td>
</tr>
<tr>
<td></td>
<td>MMM</td>
<td>Jan Feb ... Nov Dec</td>
</tr>
<tr>
<td></td>
<td>MMMM</td>
<td>January February ... November December</td>
</tr>
<tr>
<td><b>Quarter</b></td>
<td>Q</td>
<td>1 2 3 4</td>
</tr>
<tr>
<td><b>Day of Month</b></td>
<td>D</td>
<td>1 2 ... 30 31</td>
</tr>
<tr>
<td></td>
<td>Do</td>
<td>1st 2nd ... 30th 31st</td>
</tr>
<tr>
<td></td>
<td>DD</td>
<td>01 02 ... 30 31</td>
</tr>
<tr>
<td><b>Day of Year</b></td>
<td>DDD</td>
<td>1 2 ... 364 365</td>
</tr>
<tr>
<td></td>
<td>DDDo</td>
<td>1st 2nd ... 364th 365th</td>
</tr>
<tr>
<td></td>
<td>DDDD</td>
<td>001 002 ... 364 365</td>
</tr>
<tr>
<td><b>Day of Week</b></td>
<td>d</td>
<td>0 1 ... 5 6</td>
</tr>
<tr>
<td></td>
<td>do</td>
<td>0th 1st ... 5th 6th</td>
</tr>
<tr>
<td></td>
<td>dd</td>
<td>Su Mo ... Fr Sa</td>
</tr>
<tr>
<td></td>
<td>ddd</td>
<td>Sun Mon ... Fri Sat</td>
</tr>
<tr>
<td></td>
<td>dddd</td>
<td>Sunday Monday ... Friday Saturday</td>
</tr>
<tr>
<td><b>Day of Week (Locale)</b></td>
<td>e</td>
<td>0 1 ... 5 6</td>
</tr>
<tr>
<td><b>Day of Week (ISO)</b></td>
<td>E</td>
<td>1 2 ... 6 7</td>
</tr>
<tr>
<td><b>Week of Year</b></td>
<td>w</td>
<td>1 2 ... 52 53</td>
</tr>
<tr>
<td></td>
<td>wo</td>
<td>1st 2nd ... 52nd 53rd</td>
</tr>
<tr>
<td></td>
<td>ww</td>
<td>01 02 ... 52 53</td>
</tr>
<tr>
<td><b>Week of Year (ISO)</b></td>
<td>W</td>
<td>1 2 ... 52 53</td>
</tr>
<tr>
<td></td>
<td>Wo</td>
<td>1st 2nd ... 52nd 53rd</td>
</tr>
<tr>
<td></td>
<td>WW</td>
<td>01 02 ... 52 53</td>
</tr>
<tr>
<td><b>Year</b></td>
<td>YY</td>
<td>70 71 ... 29 30</td>
</tr>
<tr>
<td></td>
<td>YYYY</td>
<td>1970 1971 ... 2029 2030</td>
</tr>
<tr>
<td><b>Week Year</b></td>
<td>gg</td>
<td>70 71 ... 29 30</td>
</tr>
<tr>
<td></td>
<td>gggg</td>
<td>1970 1971 ... 2029 2030</td>
</tr>
<tr>
<td><b>Week Year (ISO)</b></td>
<td>GG</td>
<td>70 71 ... 29 30</td>
</tr>
<tr>
<td></td>
<td>GGGG</td>
<td>1970 1971 ... 2029 2030</td>
</tr>
<tr>
<td><b>AM/PM</b></td>
<td>A</td>
<td>AM PM</td>
</tr>
<tr>
<td></td>
<td>a</td>
<td>am pm</td>
</tr>
<tr>
<td><b>Hour</b></td>
<td>H</td>
<td>0 1 ... 22 23</td>
</tr>
<tr>
<td></td>
<td>HH</td>
<td>00 01 ... 22 23</td>
</tr>
<tr>
<td></td>
<td>h</td>
<td>1 2 ... 11 12</td>
</tr>
<tr>
<td></td>
<td>hh</td>
<td>01 02 ... 11 12</td>
</tr>
<tr>
<td><b>Minute</b></td>
<td>m</td>
<td>0 1 ... 58 59</td>
</tr>
<tr>
<td></td>
<td>mm</td>
<td>00 01 ... 58 59</td>
</tr>
<tr>
<td><b>Second</b></td>
<td>s</td>
<td>0 1 ... 58 59</td>
</tr>
<tr>
<td></td>
<td>ss</td>
<td>00 01 ... 58 59</td>
</tr>
<tr>
<td><b>Fractional Second</b></td>
<td>S</td>
<td>0 1 ... 8 9</td>
</tr>
<tr>
<td></td>
<td>SS</td>
<td>00 01 ... 98 99</td>
</tr>
<tr>
<td></td>
<td>SSS</td>
<td>000 001 ... 998 999</td>
</tr>
<tr>
<td></td>
<td>SSSS ... SSSSSSSSS</td>
<td>000[0..] 001[0..] ... 998[0..] 999[0..]</td>
</tr>
<tr>
<td><b>Timezone</b></td>
<td>z or zz</td>
<td>EST CST ... MST PST</td>
</tr>
<tr>
<td></td>
<td>Z</td>
<td>-07:00 -06:00 ... +06:00 +07:00</td>
</tr>
<tr>
<td></td>
<td>ZZ</td>
<td>-0700 -0600 ... +0600 +0700</td>
</tr>
<tr>
<td><b>Unix Timestamp</b></td>
<td>X</td>
<td>1360013296</td>
</tr>
<tr>
<td><b>Unix Millisecond Timestamp</b></td>
<td>x</td>
<td>1360013296123</td>
</tr>
</tbody>
</table>`,
};

View File

@@ -81,22 +81,23 @@ const Entropy = {
/**
* Frequency distribution operation.
*
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {html}
*/
runFreqDistrib: function (input, args) {
if (!input.length) return "No data";
const data = new Uint8Array(input);
if (!data.length) return "No data";
let distrib = new Array(256).fill(0),
percentages = new Array(256),
len = input.length,
len = data.length,
showZeroes = args[0],
i;
// Count bytes
for (i = 0; i < len; i++) {
distrib[input[i]]++;
distrib[data[i]]++;
}
// Calculate percentages
@@ -126,7 +127,7 @@ const Entropy = {
for (i = 0; i < 256; i++) {
if (distrib[i] || showZeroes) {
output += " " + Utils.hex(i, 2) + " (" +
Utils.padRight(percentages[i].toFixed(2).replace(".00", "") + "%)", 8) +
(percentages[i].toFixed(2).replace(".00", "") + "%)").padEnd(8, " ") +
Array(Math.ceil(percentages[i])+1).join("|") + "\n";
}
}
@@ -135,6 +136,32 @@ const Entropy = {
},
/**
* Chi Square operation.
*
* @param {ArrayBuffer} data
* @param {Object[]} args
* @returns {number}
*/
runChiSq: function(input, args) {
const data = new Uint8Array(input);
let distArray = new Array(256).fill(0),
total = 0;
for (let i = 0; i < data.length; i++) {
distArray[data[i]]++;
}
for (let i = 0; i < distArray.length; i++) {
if (distArray[i] > 0) {
total += Math.pow(distArray[i] - data.length / 256, 2) / (data.length / 256);
}
}
return total;
},
/**
* Calculates the Shannon entropy for a given chunk of data.
*

View File

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

View File

@@ -15,12 +15,13 @@ const FileType = {
/**
* Detect File Type operation.
*
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runDetect: function(input, args) {
const type = FileType.magicType(input);
const data = new Uint8Array(input),
type = FileType.magicType(data);
if (!type) {
return "Unknown file type. Have you tried checking the entropy of this data to determine whether it might be encrypted or compressed?";
@@ -46,20 +47,21 @@ const FileType = {
/**
* Scan for Embedded Files operation.
*
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runScanForEmbeddedFiles: function(input, args) {
let output = "Scanning data for 'magic bytes' which may indicate embedded files. The following results may be false positives and should not be treat as reliable. Any suffiently long file is likely to contain these magic bytes coincidentally.\n",
type,
ignoreCommon = args[0],
commonExts = ["ico", "ttf", ""],
numFound = 0,
numCommonFound = 0;
const ignoreCommon = args[0],
commonExts = ["ico", "ttf", ""],
data = new Uint8Array(input);
for (let i = 0; i < input.length; i++) {
type = FileType.magicType(input.slice(i));
for (let i = 0; i < data.length; i++) {
type = FileType.magicType(data.slice(i));
if (type) {
if (ignoreCommon && commonExts.indexOf(type.ext) > -1) {
numCommonFound++;
@@ -96,7 +98,7 @@ const FileType = {
* Given a buffer, detects magic byte sequences at specific positions and returns the
* extension and mime type.
*
* @param {byteArray} buf
* @param {Uint8Array} buf
* @returns {Object} type
* @returns {string} type.ext - File extension
* @returns {string} type.mime - Mime type

View File

@@ -215,9 +215,9 @@ const HTML = {
k = k.toFixed(2);
let hex = "#" +
Utils.padLeft(Math.round(r).toString(16), 2) +
Utils.padLeft(Math.round(g).toString(16), 2) +
Utils.padLeft(Math.round(b).toString(16), 2),
Math.round(r).toString(16).padStart(2, "0") +
Math.round(g).toString(16).padStart(2, "0") +
Math.round(b).toString(16).padStart(2, "0"),
rgb = "rgb(" + r + ", " + g + ", " + b + ")",
rgba = "rgba(" + r + ", " + g + ", " + b + ", " + a + ")",
hsl = "hsl(" + h + ", " + s + "%, " + l + "%)",
@@ -225,26 +225,26 @@ const HTML = {
cmyk = "cmyk(" + c + ", " + m + ", " + y + ", " + k + ")";
// Generate output
return "<div id='colorpicker' style='display: inline-block'></div>" +
"Hex: " + hex + "\n" +
"RGB: " + rgb + "\n" +
"RGBA: " + rgba + "\n" +
"HSL: " + hsl + "\n" +
"HSLA: " + hsla + "\n" +
"CMYK: " + cmyk +
"<script>\
$('#colorpicker').colorpicker({\
format: 'rgba',\
color: '" + rgba + "',\
container: true,\
inline: true,\
}).on('changeColor', function(e) {\
var color = e.color.toRGB();\
document.getElementById('input-text').value = 'rgba(' +\
color.r + ', ' + color.g + ', ' + color.b + ', ' + color.a + ')';\
window.app.autoBake();\
});\
</script>";
return `<div id="colorpicker" style="display: inline-block"></div>
Hex: ${hex}
RGB: ${rgb}
RGBA: ${rgba}
HSL: ${hsl}
HSLA: ${hsla}
CMYK: ${cmyk}
<script>
$('#colorpicker').colorpicker({
format: 'rgba',
color: '${rgba}',
container: true,
inline: true,
}).on('changeColor', function(e) {
var color = e.color.toRGB();
document.getElementById('input-text').value = 'rgba(' +
color.r + ', ' + color.g + ', ' + color.b + ', ' + color.a + ')';
window.app.autoBake();
});
</script>`;
},

View File

@@ -16,6 +16,22 @@ import Checksum from "./Checksum.js";
*/
const Hash = {
/**
* Generic hash function.
*
* @param {string} name
* @param {string} input
* @returns {string}
*/
runHash: function(name, input) {
const hasher = CryptoApi.hasher(name);
hasher.state.message = input;
hasher.state.length += input.length;
hasher.process();
return hasher.finalize().stringify("hex");
},
/**
* MD2 operation.
*
@@ -24,7 +40,7 @@ const Hash = {
* @returns {string}
*/
runMD2: function (input, args) {
return CryptoApi.hash("md2", input, {}).stringify("hex");
return Hash.runHash("md2", input);
},
@@ -36,7 +52,7 @@ const Hash = {
* @returns {string}
*/
runMD4: function (input, args) {
return CryptoApi.hash("md4", input, {}).stringify("hex");
return Hash.runHash("md4", input);
},
@@ -48,7 +64,7 @@ const Hash = {
* @returns {string}
*/
runMD5: function (input, args) {
return CryptoApi.hash("md5", input, {}).stringify("hex");
return Hash.runHash("md5", input);
},
@@ -92,7 +108,7 @@ const Hash = {
* @returns {string}
*/
runSHA0: function (input, args) {
return CryptoApi.hash("sha0", input, {}).stringify("hex");
return Hash.runHash("sha0", input);
},
@@ -104,7 +120,7 @@ const Hash = {
* @returns {string}
*/
runSHA1: function (input, args) {
return CryptoApi.hash("sha1", input, {}).stringify("hex");
return Hash.runHash("sha1", input);
},
@@ -123,7 +139,7 @@ const Hash = {
*/
runSHA2: function (input, args) {
const size = args[0];
return CryptoApi.hash("sha" + size, input, {}).stringify("hex");
return Hash.runHash("sha" + size, input);
},
@@ -259,7 +275,63 @@ const Hash = {
*/
runRIPEMD: function (input, args) {
const size = args[0];
return CryptoApi.hash("ripemd" + size, input, {}).stringify("hex");
return Hash.runHash("ripemd" + size, input);
},
/**
* HAS-160 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runHAS: function (input, args) {
return Hash.runHash("has160", input);
},
/**
* @constant
* @default
*/
WHIRLPOOL_VARIANT: ["Whirlpool", "Whirlpool-T", "Whirlpool-0"],
/**
* Whirlpool operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runWhirlpool: function (input, args) {
const variant = args[0].toLowerCase();
return Hash.runHash(variant, input);
},
/**
* @constant
* @default
*/
SNEFRU_ROUNDS: ["8", "4", "2"],
/**
* @constant
* @default
*/
SNEFRU_SIZE: ["256", "128"],
/**
* Snefru operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSnefru: function (input, args) {
const rounds = args[0],
size = args[1];
return Hash.runHash(`snefru-${rounds}-${size}`, input);
},
@@ -283,6 +355,10 @@ const Hash = {
"RIPEMD160",
"RIPEMD256",
"RIPEMD320",
"HAS160",
"Whirlpool",
"Whirlpool-0",
"Whirlpool-T"
],
/**
@@ -335,6 +411,10 @@ const Hash = {
"\nRIPEMD-160: " + Hash.runRIPEMD(input, ["160"]) +
"\nRIPEMD-256: " + Hash.runRIPEMD(input, ["256"]) +
"\nRIPEMD-320: " + Hash.runRIPEMD(input, ["320"]) +
"\nHAS-160: " + Hash.runHAS(input, []) +
"\nWhirlpool-0: " + Hash.runWhirlpool(input, ["Whirlpool-0"]) +
"\nWhirlpool-T: " + Hash.runWhirlpool(input, ["Whirlpool-T"]) +
"\nWhirlpool: " + Hash.runWhirlpool(input, ["Whirlpool"]) +
"\n\nChecksums:" +
"\nFletcher-8: " + Checksum.runFletcher8(byteArray, []) +
"\nFletcher-16: " + Checksum.runFletcher16(byteArray, []) +

View File

@@ -1,4 +1,3 @@
/* globals app */
import Utils from "../Utils.js";
@@ -32,18 +31,19 @@ const Hexdump = {
/**
* To Hexdump operation.
*
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
const data = new Uint8Array(input);
const length = args[0] || Hexdump.WIDTH;
const upperCase = args[1];
const includeFinalLength = args[2];
let output = "", padding = 2;
for (let i = 0; i < input.length; i += length) {
const buff = input.slice(i, i+length);
for (let i = 0; i < data.length; i += length) {
const buff = data.slice(i, i+length);
let hexa = "";
for (let j = 0; j < buff.length; j++) {
hexa += Utils.hex(buff[j], padding) + " ";
@@ -57,10 +57,10 @@ const Hexdump = {
}
output += lineNo + " " +
Utils.padRight(hexa, (length*(padding+1))) +
" |" + Utils.padRight(Utils.printable(Utils.byteArrayToChars(buff)), buff.length) + "|\n";
hexa.padEnd(length*(padding+1), " ") +
" |" + Utils.printable(Utils.byteArrayToChars(buff)).padEnd(buff.length, " ") + "|\n";
if (includeFinalLength && i+buff.length === input.length) {
if (includeFinalLength && i+buff.length === data.length) {
output += Utils.hex(i+buff.length, 8) + "\n";
}
}

View File

@@ -20,14 +20,13 @@ const Image = {
*
* Extracts EXIF data from a byteArray, representing a JPG or a TIFF image.
*
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runExtractEXIF(input, args) {
try {
const bytes = Uint8Array.from(input);
const parser = ExifParser.create(bytes.buffer);
const parser = ExifParser.create(input);
const result = parser.parse();
let lines = [];
@@ -53,7 +52,7 @@ const Image = {
* @author David Moodie [davidmoodie12@gmail.com]
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
* @returns {byteArray}
*/
runRemoveEXIF(input, args) {
// Do nothing if input is empty

View File

@@ -1,5 +1,5 @@
/**
* Microsoft operations.
* Microsoft operations.
*
* @author bmwhitn [brian.m.whitney@outlook.com]
* @copyright Crown Copyright 2017

View File

@@ -26,9 +26,14 @@ const NetBIOS = {
let output = [],
offset = args[0];
for (let i = 0; i < input.length; i++) {
output.push((input[i] >> 4) + offset);
output.push((input[i] & 0xf) + offset);
if (input.length <= 16) {
let len = input.length;
input.length = 16;
input.fill(32, len, 16);
for (let i = 0; i < input.length; i++) {
output.push((input[i] >> 4) + offset);
output.push((input[i] & 0xf) + offset);
}
}
return output;
@@ -46,9 +51,15 @@ const NetBIOS = {
let output = [],
offset = args[0];
for (let i = 0; i < input.length; i += 2) {
output.push(((input[i] - offset) << 4) |
((input[i + 1] - offset) & 0xf));
if (input.length <= 32 && (input.length % 2) === 0) {
for (let i = 0; i < input.length; i += 2) {
output.push((((input[i] & 0xff) - offset) << 4) |
(((input[i + 1] & 0xff) - offset) & 0xf));
}
for (let i = output.length - 1; i > 0; i--) {
if (output[i] === 32) output.splice(i, i);
else break;
}
}
return output;

View File

@@ -14,16 +14,72 @@ const Numberwang = {
* @returns {string}
*/
run: function(input, args) {
if (!input) return "Let's play Wangernumb!";
const match = input.match(/\d+/);
if (match) {
return match[0] + "! That's Numberwang!";
let output;
if (!input) {
output = "Let's play Wangernumb!";
} else {
// That's a bad miss!
return "Sorry, that's not Numberwang. Let's rotate the board!";
const match = input.match(/(f0rty-s1x|shinty-six|filth-hundred and neeb|-?√?\d+(\.\d+)?i?([a-z]?)%?)/i);
if (match) {
if (match[3]) output = match[0] + "! That's AlphaNumericWang!";
else output = match[0] + "! That's Numberwang!";
} else {
// That's a bad miss!
output = "Sorry, that's not Numberwang. Let's rotate the board!";
}
}
const rand = Math.floor(Math.random() * Numberwang._didYouKnow.length);
return output + "\n\nDid you know: " + Numberwang._didYouKnow[rand];
},
/**
* Taken from http://numberwang.wikia.com/wiki/Numberwang_Wikia
*
* @private
* @constant
*/
_didYouKnow: [
"Numberwang, contrary to popular belief, is a fruit and not a vegetable.",
"Robert Webb once got WordWang while presenting an episode of Numberwang.",
"The 6705th digit of pi is Numberwang.",
"Numberwang was invented on a Sevenday.",
"Contrary to popular belief, Albert Einstein always got good grades in Numberwang at school. He once scored ^4$ on a test.",
"680 asteroids have been named after Numberwang.",
"Archimedes is most famous for proclaiming \"That's Numberwang!\" during an epiphany about water displacement he had while taking a bath.",
"Numberwang Day is celebrated in Japan on every day of the year apart from June 6.",
"Biologists recently discovered Numberwang within a strand of human DNA.",
"Numbernot is a special type of non-Numberwang number. It is divisible by 3 and the letter \"y\".",
"Julie once got 612.04 Numberwangs in a single episode of Emmerdale.",
"In India, it is traditional to shout out \"Numberwang!\" instead of checkmate during games of chess.",
"There is a rule on Countdown which states that if you get Numberwang in the numbers round, you automatically win. It has only ever been invoked twice.",
"\"Numberwang\" was the third-most common baby name for a brief period in 1722.",
"\"The Lion King\" was loosely based on Numberwang.",
"\"A Numberwang a day keeps the doctor away\" is how Donny Cosy, the oldest man in the world, explained how he was in such good health at the age of 136.",
"The \"number lock\" button on a keyboard is based on the popular round of the same name in \"Numberwang\".",
"Cambridge became the first university to offer a course in Numberwang in 1567.",
"Schrödinger's Numberwang is a number that has been confusing dentists for centuries.",
"\"Harry Potter and the Numberwang of Numberwang\" was rejected by publishers -41 times before it became a bestseller.",
"\"Numberwang\" is the longest-running British game show in history; it has aired 226 seasons, each containing 19 episodes, which makes a grand total of 132 episodes.",
"The triple Numberwang bonus was discovered by archaeologist Thomas Jefferson in Somerset.",
"Numberwang is illegal in parts of Czechoslovakia.",
"Numberwang was discovered in India in the 12th century.",
"Numberwang has the chemical formula Zn4SO2(HgEs)3.",
"The first pack of cards ever created featured two \"Numberwang\" cards instead of jokers.",
"Julius Caesar was killed by an overdose of Numberwang.",
"The most Numberwang musical note is G#.",
"In 1934, the forty-third Google Doodle promoted the upcoming television show \"Numberwang on Ice\".",
"A recent psychology study found that toddlers were 17% faster at identifying numbers which were Numberwang.",
"There are 700 ways to commit a foul in the television show \"Numberwang\". All 700 of these fouls were committed by Julie in one single episode in 1473.",
"Astronomers suspect God is Numberwang.",
"Numberwang is the official beverage of Canada.",
"In the pilot episode of \"The Price is Right\", if a contestant got the value of an item exactly right they were told \"That's Numberwang!\" and immediately won ₹5.7032.",
"The first person to get three Numberwangs in a row was Madonna.",
"\"Numberwang\" has the code U+46402 in Unicode.",
"The musical note \"Numberwang\" is between D# and E♮.",
"Numberwang was first played on the moon in 1834.",
],
};
export default Numberwang;

View File

@@ -1,6 +1,7 @@
import otp from "otp";
import Base64 from "./Base64.js";
/**
* One-Time Password operations.
*

160
src/core/operations/PHP.js Normal file
View File

@@ -0,0 +1,160 @@
/**
* PHP operations.
*
* @author Jarmo van Lenthe [github.com/jarmovanlenthe]
* @copyright Jarmo van Lenthe
* @license Apache-2.0
*
* @namespace
*/
const PHP = {
/**
* @constant
* @default
*/
OUTPUT_VALID_JSON: true,
/**
* PHP Deserialize operation.
*
* This Javascript implementation is based on the Python implementation by
* Armin Ronacher (2016), who released it under the 3-Clause BSD license.
* See: https://github.com/mitsuhiko/phpserialize/
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runDeserialize: function (input, args) {
/**
* Recursive method for deserializing.
* @returns {*}
*/
function handleInput() {
/**
* Read `length` characters from the input, shifting them out the input.
* @param length
* @returns {string}
*/
function read(length) {
let result = "";
for (let idx = 0; idx < length; idx++) {
let char = inputPart.shift();
if (char === undefined) {
throw "End of input reached before end of script";
}
result += char;
}
return result;
}
/**
* Read characters from the input until `until` is found.
* @param until
* @returns {string}
*/
function readUntil(until) {
let result = "";
for (;;) {
let char = read(1);
if (char === until) {
break;
} else {
result += char;
}
}
return result;
}
/**
* Read characters from the input that must be equal to `expect`
* @param expect
* @returns {string}
*/
function expect(expect) {
let result = read(expect.length);
if (result !== expect) {
throw "Unexpected input found";
}
return result;
}
/**
* Helper function to handle deserialized arrays.
* @returns {Array}
*/
function handleArray() {
let items = parseInt(readUntil(":"), 10) * 2;
expect("{");
let result = [];
let isKey = true;
let lastItem = null;
for (let idx = 0; idx < items; idx++) {
let item = handleInput();
if (isKey) {
lastItem = item;
isKey = false;
} else {
let numberCheck = lastItem.match(/[0-9]+/);
if (args[0] && numberCheck && numberCheck[0].length === lastItem.length) {
result.push("\"" + lastItem + "\": " + item);
} else {
result.push(lastItem + ": " + item);
}
isKey = true;
}
}
expect("}");
return result;
}
let kind = read(1).toLowerCase();
switch (kind) {
case "n":
expect(";");
return "";
case "i":
case "d":
case "b": {
expect(":");
let data = readUntil(";");
if (kind === "b") {
return (parseInt(data, 10) !== 0);
}
return data;
}
case "a":
expect(":");
return "{" + handleArray() + "}";
case "s": {
expect(":");
let length = readUntil(":");
expect("\"");
let value = read(length);
expect("\";");
if (args[0]) {
return "\"" + value.replace(/"/g, "\\\"") + "\"";
} else {
return "\"" + value + "\"";
}
}
default:
throw "Unknown type: " + kind;
}
}
let inputPart = input.split("");
return handleInput();
}
};
export default PHP;

View File

@@ -121,8 +121,7 @@ const PublicKey = {
// Format Public Key fields
for (let i = 0; i < pkFields.length; i++) {
pkStr += " " + pkFields[i].key + ":" +
Utils.padLeft(
pkFields[i].value + "\n",
(pkFields[i].value + "\n").padStart(
18 - (pkFields[i].key.length + 3) + pkFields[i].value.length + 1,
" "
);
@@ -286,9 +285,9 @@ ${extensions}`;
key = fields[i].split("=")[0];
value = fields[i].split("=")[1];
str = Utils.padRight(key, maxKeyLen) + " = " + value + "\n";
str = key.padEnd(maxKeyLen, " ") + " = " + value + "\n";
output += Utils.padLeft(str, indent + str.length, " ");
output += str.padStart(indent + str.length, " ");
}
return output.slice(0, -1);
@@ -314,7 +313,7 @@ ${extensions}`;
if (i === 0) {
output += str;
} else {
output += Utils.padLeft(str, indent + str.length, " ");
output += str.padStart(indent + str.length, " ");
}
}

View File

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

View File

@@ -158,7 +158,7 @@ const SeqUtils = {
width = lines.length.toString().length;
for (let n = 0; n < lines.length; n++) {
output += Utils.pad((n+1).toString(), width, " ") + " " + lines[n] + "\n";
output += (n+1).toString().padStart(width, " ") + " " + lines[n] + "\n";
}
return output.slice(0, output.length-1);
},
@@ -249,7 +249,7 @@ const SeqUtils = {
}
}
return 0;
return a.localeCompare(b);
},
};

View File

@@ -0,0 +1,96 @@
import disassemble from "../lib/DisassembleX86-64.js";
/**
* Shellcode operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const Shellcode = {
/**
* @constant
* @default
*/
MODE: ["64", "32", "16"],
/**
* @constant
* @default
*/
COMPATIBILITY: [
"Full x86 architecture",
"Knights Corner",
"Larrabee",
"Cyrix",
"Geode",
"Centaur",
"X86/486"
],
/**
* Disassemble x86 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runDisassemble: function(input, args) {
const mode = args[0],
compatibility = args[1],
codeSegment = args[2],
offset = args[3],
showInstructionHex = args[4],
showInstructionPos = args[5];
switch (mode) {
case "64":
disassemble.setBitMode(2);
break;
case "32":
disassemble.setBitMode(1);
break;
case "16":
disassemble.setBitMode(0);
break;
default:
throw "Invalid mode value";
}
switch (compatibility) {
case "Full x86 architecture":
disassemble.CompatibilityMode(0);
break;
case "Knights Corner":
disassemble.CompatibilityMode(1);
break;
case "Larrabee":
disassemble.CompatibilityMode(2);
break;
case "Cyrix":
disassemble.CompatibilityMode(3);
break;
case "Geode":
disassemble.CompatibilityMode(4);
break;
case "Centaur":
disassemble.CompatibilityMode(5);
break;
case "X86/486":
disassemble.CompatibilityMode(6);
break;
}
disassemble.SetBasePosition(codeSegment + ":" + offset);
disassemble.setShowInstructionHex(showInstructionHex);
disassemble.setShowInstructionPos(showInstructionPos);
disassemble.LoadBinCode(input.replace(/\s/g, ""));
return disassemble.LDisassemble();
},
};
export default Shellcode;

View File

@@ -12,128 +12,6 @@ import Utils from "../Utils.js";
*/
const StrUtils = {
/**
* @constant
* @default
*/
REGEX_PRE_POPULATE: [
{
name: "User defined",
value: ""
},
{
name: "IPv4 address",
value: "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?"
},
{
name: "IPv6 address",
value: "((?=.*::)(?!.*::.+::)(::)?([\\dA-Fa-f]{1,4}:(:|\\b)|){5}|([\\dA-Fa-f]{1,4}:){6})((([\\dA-Fa-f]{1,4}((?!\\3)::|:\\b|(?![\\dA-Fa-f])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})"
},
{
name: "Email address",
value: "(\\w[-.\\w]*)@([-\\w]+(?:\\.[-\\w]+)*)\\.([A-Za-z]{2,4})"
},
{
name: "URL",
value: "([A-Za-z]+://)([-\\w]+(?:\\.\\w[-\\w]*)+)(:\\d+)?(/[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]*(?:[.!,?]+[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]+)*)?"
},
{
name: "Domain",
value: "\\b((?=[a-z0-9-]{1,63}\\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,63}\\b"
},
{
name: "Windows file path",
value: "([A-Za-z]):\\\\((?:[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)]{0,61}\\\\?)*[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)]{0,61})(\\.[A-Za-z\\d]{1,6})?"
},
{
name: "UNIX file path",
value: "(?:/[A-Za-z\\d.][A-Za-z\\d\\-.]{0,61})+"
},
{
name: "MAC address",
value: "[A-Fa-f\\d]{2}(?:[:-][A-Fa-f\\d]{2}){5}"
},
{
name: "Date (yyyy-mm-dd)",
value: "((?:19|20)\\d\\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])"
},
{
name: "Date (dd/mm/yyyy)",
value: "(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.]((?:19|20)\\d\\d)"
},
{
name: "Date (mm/dd/yyyy)",
value: "(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.]((?:19|20)\\d\\d)"
},
{
name: "Strings",
value: "[A-Za-z\\d/\\-:.,_$%\\x27\"()<>= !\\[\\]{}@]{4,}"
},
],
/**
* @constant
* @default
*/
REGEX_CASE_INSENSITIVE: true,
/**
* @constant
* @default
*/
REGEX_MULTILINE_MATCHING: true,
/**
* @constant
* @default
*/
OUTPUT_FORMAT: ["Highlight matches", "List matches", "List capture groups", "List matches with capture groups"],
/**
* @constant
* @default
*/
DISPLAY_TOTAL: false,
/**
* Regular expression operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runRegex: function(input, args) {
let userRegex = args[1],
i = args[2],
m = args[3],
displayTotal = args[4],
outputFormat = args[5],
modifiers = "g";
if (i) modifiers += "i";
if (m) modifiers += "m";
if (userRegex && userRegex !== "^" && userRegex !== "$") {
try {
const regex = new RegExp(userRegex, modifiers);
switch (outputFormat) {
case "Highlight matches":
return StrUtils._regexHighlight(input, regex, displayTotal);
case "List matches":
return Utils.escapeHtml(StrUtils._regexList(input, regex, displayTotal, true, false));
case "List capture groups":
return Utils.escapeHtml(StrUtils._regexList(input, regex, displayTotal, false, true));
case "List matches with capture groups":
return Utils.escapeHtml(StrUtils._regexList(input, regex, displayTotal, true, true));
default:
return "Error: Invalid output format";
}
} catch (err) {
return "Invalid regex. Details: " + err.message;
}
} else {
return Utils.escapeHtml(input);
}
},
/**
* @constant
* @default
@@ -187,68 +65,28 @@ const StrUtils = {
* @constant
* @default
*/
SEARCH_TYPE: ["Regex", "Extended (\\n, \\t, \\x...)", "Simple string"],
SPLIT_DELIM_OPTIONS: [
{name: "Comma", value: ","},
{name: "Space", value: " "},
{name: "Line feed", value: "\\n"},
{name: "CRLF", value: "\\r\\n"},
{name: "Semi-colon", value: ";"},
{name: "Colon", value: ":"},
{name: "Nothing (separate chars)", value: ""}
],
/**
* @constant
* @default
*/
FIND_REPLACE_GLOBAL: true,
/**
* @constant
* @default
*/
FIND_REPLACE_CASE: false,
/**
* @constant
* @default
*/
FIND_REPLACE_MULTILINE: true,
/**
* Find / Replace operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runFindReplace: function(input, args) {
let find = args[0].string,
type = args[0].option,
replace = args[1],
g = args[2],
i = args[3],
m = args[4],
modifiers = "";
if (g) modifiers += "g";
if (i) modifiers += "i";
if (m) modifiers += "m";
if (type === "Regex") {
find = new RegExp(find, modifiers);
return input.replace(find, replace);
}
if (type.indexOf("Extended") === 0) {
find = Utils.parseEscapedChars(find);
}
find = new RegExp(Utils.escapeRegex(find), modifiers);
return input.replace(find, replace);
},
/**
* @constant
* @default
*/
SPLIT_DELIM: ",",
/**
* @constant
* @default
*/
DELIMITER_OPTIONS: ["Line feed", "CRLF", "Space", "Comma", "Semi-colon", "Colon", "Nothing (separate chars)"],
JOIN_DELIM_OPTIONS: [
{name: "Line feed", value: "\\n"},
{name: "CRLF", value: "\\r\\n"},
{name: "Space", value: " "},
{name: "Comma", value: ","},
{name: "Semi-colon", value: ";"},
{name: "Colon", value: ":"},
{name: "Nothing (join chars)", value: ""}
],
/**
* Split operation.
@@ -258,8 +96,8 @@ const StrUtils = {
* @returns {string}
*/
runSplit: function(input, args) {
let splitDelim = args[0] || StrUtils.SPLIT_DELIM,
joinDelim = Utils.charRep[args[1]],
let splitDelim = args[0],
joinDelim = args[1],
sections = input.split(splitDelim);
return sections.join(joinDelim);
@@ -510,80 +348,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();
},
};

View File

@@ -1,6 +1,3 @@
import Utils from "../Utils.js";
/**
* Tidy operations.
*
@@ -104,32 +101,39 @@ const Tidy = {
/**
* Drop bytes operation.
*
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
* @returns {ArrayBuffer}
*/
runDropBytes: function(input, args) {
let start = args[0],
const start = args[0],
length = args[1],
applyToEachLine = args[2];
if (start < 0 || length < 0)
throw "Error: Invalid value";
if (!applyToEachLine)
return input.slice(0, start).concat(input.slice(start+length, input.length));
if (!applyToEachLine) {
const left = input.slice(0, start),
right = input.slice(start + length, input.byteLength);
let result = new Uint8Array(left.byteLength + right.byteLength);
result.set(new Uint8Array(left), 0);
result.set(new Uint8Array(right), left.byteLength);
return result.buffer;
}
// Split input into lines
const data = new Uint8Array(input);
let lines = [],
line = [],
i;
for (i = 0; i < input.length; i++) {
if (input[i] === 0x0a) {
for (i = 0; i < data.length; i++) {
if (data[i] === 0x0a) {
lines.push(line);
line = [];
} else {
line.push(input[i]);
line.push(data[i]);
}
}
lines.push(line);
@@ -139,7 +143,7 @@ const Tidy = {
output = output.concat(lines[i].slice(0, start).concat(lines[i].slice(start+length, lines[i].length)));
output.push(0x0a);
}
return output.slice(0, output.length-1);
return new Uint8Array(output.slice(0, output.length-1)).buffer;
},
@@ -157,12 +161,12 @@ const Tidy = {
/**
* Take bytes operation.
*
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
* @returns {ArrayBuffer}
*/
runTakeBytes: function(input, args) {
let start = args[0],
const start = args[0],
length = args[1],
applyToEachLine = args[2];
@@ -173,16 +177,17 @@ const Tidy = {
return input.slice(start, start+length);
// Split input into lines
const data = new Uint8Array(input);
let lines = [],
line = [];
let i;
line = [],
i;
for (i = 0; i < input.length; i++) {
if (input[i] === 0x0a) {
for (i = 0; i < data.length; i++) {
if (data[i] === 0x0a) {
lines.push(line);
line = [];
} else {
line.push(input[i]);
line.push(data[i]);
}
}
lines.push(line);
@@ -192,7 +197,7 @@ const Tidy = {
output = output.concat(lines[i].slice(start, start+length));
output.push(0x0a);
}
return output.slice(0, output.length-1);
return new Uint8Array(output.slice(0, output.length-1)).buffer;
},
@@ -229,11 +234,11 @@ const Tidy = {
if (position === "Start") {
for (i = 0; i < lines.length; i++) {
output += Utils.padLeft(lines[i], lines[i].length+len, chr) + "\n";
output += lines[i].padStart(lines[i].length+len, chr) + "\n";
}
} else if (position === "End") {
for (i = 0; i < lines.length; i++) {
output += Utils.padRight(lines[i], lines[i].length+len, chr) + "\n";
output += lines[i].padEnd(lines[i].length+len, chr) + "\n";
}
}

View File

@@ -1,5 +1,5 @@
/* globals unescape */
import Utils from "../Utils.js";
import url from "url";
/**
@@ -58,56 +58,36 @@ const URL_ = {
* @returns {string}
*/
runParse: function(input, args) {
if (!document) {
throw "This operation only works in a browser.";
}
const uri = url.parse(input, true);
const a = document.createElement("a");
let output = "";
// Overwrite base href which will be the current CyberChef URL to reduce confusion.
a.href = "http://example.com/";
a.href = input;
if (uri.protocol) output += "Protocol:\t" + uri.protocol + "\n";
if (uri.auth) output += "Auth:\t\t" + uri.auth + "\n";
if (uri.hostname) output += "Hostname:\t" + uri.hostname + "\n";
if (uri.port) output += "Port:\t\t" + uri.port + "\n";
if (uri.pathname) output += "Path name:\t" + uri.pathname + "\n";
if (uri.query) {
let keys = Object.keys(uri.query),
padding = 0;
if (a.protocol) {
let output = "";
if (a.hostname !== window.location.hostname) {
output = "Protocol:\t" + a.protocol + "\n";
if (a.hostname) output += "Hostname:\t" + a.hostname + "\n";
if (a.port) output += "Port:\t\t" + a.port + "\n";
}
keys.forEach(k => {
padding = (k.length > padding) ? k.length : padding;
});
if (a.pathname && a.pathname !== window.location.pathname) {
let pathname = a.pathname;
if (pathname.indexOf(window.location.pathname) === 0)
pathname = pathname.replace(window.location.pathname, "");
if (pathname)
output += "Path name:\t" + pathname + "\n";
}
if (a.hash && a.hash !== window.location.hash) {
output += "Hash:\t\t" + a.hash + "\n";
}
if (a.search && a.search !== window.location.search) {
output += "Arguments:\n";
const args_ = (a.search.slice(1, a.search.length)).split("&");
let splitArgs = [], padding = 0, i;
for (i = 0; i < args_.length; i++) {
splitArgs.push(args_[i].split("="));
padding = (splitArgs[i][0].length > padding) ? splitArgs[i][0].length : padding;
}
for (i = 0; i < splitArgs.length; i++) {
output += "\t" + Utils.padRight(splitArgs[i][0], padding);
if (splitArgs[i].length > 1 && splitArgs[i][1].length)
output += " = " + splitArgs[i][1] + "\n";
else output += "\n";
output += "Arguments:\n";
for (let key in uri.query) {
output += "\t" + key.padEnd(padding, " ");
if (uri.query[key].length) {
output += " = " + uri.query[key] + "\n";
} else {
output += "\n";
}
}
return output;
}
if (uri.hash) output += "Hash:\t\t" + uri.hash + "\n";
return "Invalid URI";
return output;
},

View File

@@ -1,3 +1,6 @@
import crypto from "crypto";
/**
* UUID operations.
*
@@ -17,25 +20,17 @@ const UUID = {
* @returns {string}
*/
runGenerateV4: function(input, args) {
if (window && typeof(window.crypto) !== "undefined" && typeof(window.crypto.getRandomValues) !== "undefined") {
let buf = new Uint32Array(4),
i = 0;
window.crypto.getRandomValues(buf);
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
let r = (buf[i >> 3] >> ((i % 8) * 4)) & 0xf,
v = c === "x" ? r : (r & 0x3 | 0x8);
i++;
return v.toString(16);
});
} else {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
let r = Math.random() * 16 | 0,
v = c === "x" ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
},
const buf = new Uint32Array(4).map(() => {
return crypto.randomBytes(4).readUInt32BE(0, true);
});
let i = 0;
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
let r = (buf[i >> 3] >> ((i % 8) * 4)) & 0xf,
v = c === "x" ? r : (r & 0x3 | 0x8);
i++;
return v.toString(16);
});
}
};
export default UUID;

View File

@@ -9,7 +9,7 @@ require("babel-polyfill");
const Chef = require("../core/Chef.js").default;
const CyberChef = module.exports = {
const CyberChef = {
bake: function(input, recipeConfig) {
this.chef = new Chef();
@@ -23,3 +23,5 @@ const CyberChef = module.exports = {
}
};
module.exports = CyberChef;

View File

@@ -49,9 +49,11 @@ App.prototype.setup = function() {
this.manager.setup();
this.resetLayout();
this.setCompileMessage();
this.loadURIParams();
log.debug("App loaded");
this.appLoaded = true;
this.loadURIParams();
this.loaded();
};
@@ -88,9 +90,10 @@ App.prototype.loaded = function() {
* An error handler for displaying the error to the user.
*
* @param {Error} err
* @param {boolean} [logToConsole=false]
*/
App.prototype.handleError = function(err) {
console.error(err);
App.prototype.handleError = function(err, logToConsole) {
if (logToConsole) log.error(err);
const msg = err.displayStr || err.toString();
this.alert(msg, "danger", this.options.errorTimeout, !this.options.showErrors);
};
@@ -122,7 +125,13 @@ App.prototype.bake = function(step) {
* Runs Auto Bake if it is set.
*/
App.prototype.autoBake = function() {
if (this.autoBake_ && !this.autoBakePause && !this.baking) {
// If autoBakePause is set, we are loading a full recipe (and potentially input), so there is no
// need to set the staleness indicator. Just exit and wait until auto bake is called after loading
// has completed.
if (this.autoBakePause) return false;
if (this.autoBake_ && !this.baking) {
log.debug("Auto-baking");
this.bake();
} else {
this.manager.controls.showStaleIndicator();
@@ -369,10 +378,6 @@ App.prototype.loadURIParams = function() {
window.location.hash;
this.uriParams = Utils.parseURIParams(params);
// Pause auto-bake while loading but don't modify `this.autoBake_`
// otherwise `manualBake` cannot trigger.
this.autoBakePause = true;
// Read in recipe from URI params
if (this.uriParams.recipe) {
try {
@@ -401,14 +406,16 @@ App.prototype.loadURIParams = function() {
// Read in input data from URI params
if (this.uriParams.input) {
this.autoBakePause = true;
try {
const inputData = Utils.fromBase64(this.uriParams.input);
this.setInput(inputData);
} catch (err) {}
} catch (err) {
} finally {
this.autoBakePause = false;
}
}
// Unpause auto-bake
this.autoBakePause = false;
this.autoBake();
};
@@ -441,6 +448,10 @@ App.prototype.getRecipeConfig = function() {
App.prototype.setRecipeConfig = function(recipeConfig) {
document.getElementById("rec-list").innerHTML = null;
// Pause auto-bake while loading but don't modify `this.autoBake_`
// otherwise `manualBake` cannot trigger.
this.autoBakePause = true;
for (let i = 0; i < recipeConfig.length; i++) {
const item = this.manager.recipe.addOperation(recipeConfig[i].op);
@@ -473,6 +484,9 @@ App.prototype.setRecipeConfig = function(recipeConfig) {
this.progress = 0;
}
// Unpause auto bake
this.autoBakePause = false;
};
@@ -518,7 +532,7 @@ App.prototype.setCompileMessage = function() {
/**
* Determines whether the browser supports Local Storage and if it is accessible.
*
*
* @returns {boolean}
*/
App.prototype.isLocalStorageAvailable = function() {
@@ -558,7 +572,7 @@ App.prototype.isLocalStorageAvailable = function() {
App.prototype.alert = function(str, style, timeout, silent) {
const time = new Date();
console.log("[" + time.toLocaleString() + "] " + str);
log.info("[" + time.toLocaleString() + "] " + str);
if (silent) return;
style = style || "danger";

217
src/web/BindingsWaiter.js Normal file
View File

@@ -0,0 +1,217 @@
/**
* Waiter to handle keybindings to CyberChef functions (i.e. Bake, Step, Save, Load etc.)
*
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @constructor
* @param {App} app - The main view object for CyberChef.
* @param {Manager} manager - The CyberChef event manager.
*/
const BindingsWaiter = function (app, manager) {
this.app = app;
this.manager = manager;
};
/**
* Handler for all keydown events
* Checks whether valid keyboard shortcut has been instated
*
* @fires Manager#statechange
* @param {event} e
*/
BindingsWaiter.prototype.parseInput = function(e) {
const modKey = this.app.options.useMetaKey ? e.metaKey : e.altKey;
if (e.ctrlKey && modKey) {
let elem;
switch (e.code) {
case "KeyF": // Focus search
e.preventDefault();
document.getElementById("search").focus();
break;
case "KeyI": // Focus input
e.preventDefault();
document.getElementById("input-text").focus();
break;
case "KeyO": // Focus output
e.preventDefault();
document.getElementById("output-text").focus();
break;
case "Period": // Focus next operation
e.preventDefault();
try {
elem = document.activeElement.closest(".operation") || document.querySelector("#rec-list .operation");
if (elem.parentNode.lastChild === elem) {
// If operation is last in recipe, loop around to the top operation's first argument
elem.parentNode.firstChild.querySelectorAll(".arg")[0].focus();
} else {
// Focus first argument of next operation
elem.nextSibling.querySelectorAll(".arg")[0].focus();
}
} catch (e) {
// do nothing, just don't throw an error
}
break;
case "KeyB": // Set breakpoint
e.preventDefault();
try {
elem = document.activeElement.closest(".operation").querySelectorAll(".breakpoint")[0];
if (elem.getAttribute("break") === "false") {
elem.setAttribute("break", "true"); // add break point if not already enabled
elem.classList.add("breakpoint-selected");
} else {
elem.setAttribute("break", "false"); // remove break point if already enabled
elem.classList.remove("breakpoint-selected");
}
window.dispatchEvent(this.manager.statechange);
} catch (e) {
// do nothing, just don't throw an error
}
break;
case "KeyD": // Disable operation
e.preventDefault();
try {
elem = document.activeElement.closest(".operation").querySelectorAll(".disable-icon")[0];
if (elem.getAttribute("disabled") === "false") {
elem.setAttribute("disabled", "true"); // disable operation if enabled
elem.classList.add("disable-elem-selected");
elem.parentNode.parentNode.classList.add("disabled");
} else {
elem.setAttribute("disabled", "false"); // enable operation if disabled
elem.classList.remove("disable-elem-selected");
elem.parentNode.parentNode.classList.remove("disabled");
}
this.app.progress = 0;
window.dispatchEvent(this.manager.statechange);
} catch (e) {
// do nothing, just don't throw an error
}
break;
case "Space": // Bake
e.preventDefault();
this.app.bake();
break;
case "Quote": // Step through
e.preventDefault();
this.app.bake(true);
break;
case "KeyC": // Clear recipe
e.preventDefault();
this.manager.recipe.clearRecipe();
break;
case "KeyS": // Save output to file
e.preventDefault();
this.manager.output.saveClick();
break;
case "KeyL": // Load recipe
e.preventDefault();
this.manager.controls.loadClick();
break;
case "KeyM": // Switch input and output
e.preventDefault();
this.manager.output.switchClick();
break;
default:
if (e.code.match(/Digit[0-9]/g)) { // Select nth operation
e.preventDefault();
try {
// Select the first argument of the operation corresponding to the number pressed
document.querySelector(`li:nth-child(${e.code.substr(-1)}) .arg`).focus();
} catch (e) {
// do nothing, just don't throw an error
}
}
break;
}
}
};
/**
* Updates keybinding list when metaKey option is toggled
*
*/
BindingsWaiter.prototype.updateKeybList = function() {
let modWinLin = "Alt";
let modMac = "Opt";
if (this.app.options.useMetaKey) {
modWinLin = "Win";
modMac = "Cmd";
}
document.getElementById("keybList").innerHTML = `
<tr>
<td><b>Command</b></td>
<td><b>Shortcut (Win/Linux)</b></td>
<td><b>Shortcut (Mac)</b></td>
</tr>
<tr>
<td>Place cursor in search field</td>
<td>Ctrl+${modWinLin}+f</td>
<td>Ctrl+${modMac}+f</td>
<tr>
<td>Place cursor in input box</td>
<td>Ctrl+${modWinLin}+i</td>
<td>Ctrl+${modMac}+i</td>
</tr>
<tr>
<td>Place cursor in output box</td>
<td>Ctrl+${modWinLin}+o</td>
<td>Ctrl+${modMac}+o</td>
</tr>
<tr>
<td>Place cursor in first argument field of the next operation in the recipe</td>
<td>Ctrl+${modWinLin}+.</td>
<td>Ctrl+${modMac}+.</td>
</tr>
<tr>
<td>Place cursor in first argument field of the nth operation in the recipe</td>
<td>Ctrl+${modWinLin}+[1-9]</td>
<td>Ctrl+${modMac}+[1-9]</td>
</tr>
<tr>
<td>Disable current operation</td>
<td>Ctrl+${modWinLin}+d</td>
<td>Ctrl+${modMac}+d</td>
</tr>
<tr>
<td>Set/clear breakpoint</td>
<td>Ctrl+${modWinLin}+b</td>
<td>Ctrl+${modMac}+b</td>
</tr>
<tr>
<td>Bake</td>
<td>Ctrl+${modWinLin}+Space</td>
<td>Ctrl+${modMac}+Space</td>
</tr>
<tr>
<td>Step</td>
<td>Ctrl+${modWinLin}+'</td>
<td>Ctrl+${modMac}+'</td>
</tr>
<tr>
<td>Clear recipe</td>
<td>Ctrl+${modWinLin}+c</td>
<td>Ctrl+${modMac}+c</td>
</tr>
<tr>
<td>Save to file</td>
<td>Ctrl+${modWinLin}+s</td>
<td>Ctrl+${modMac}+s</td>
</tr>
<tr>
<td>Load recipe</td>
<td>Ctrl+${modWinLin}+l</td>
<td>Ctrl+${modMac}+l</td>
</tr>
<tr>
<td>Move output to input</td>
<td>Ctrl+${modWinLin}+m</td>
<td>Ctrl+${modMac}+m</td>
</tr>
`;
};
export default BindingsWaiter;

View File

@@ -361,6 +361,7 @@ ControlsWaiter.prototype.loadButtonClick = function() {
try {
const recipeConfig = Utils.parseRecipeConfig(document.getElementById("load-text").value);
this.app.setRecipeConfig(recipeConfig);
this.app.autoBake();
$("#rec-list [data-toggle=popover]").popover();
} catch (e) {

View File

@@ -1,6 +1,3 @@
import Utils from "../core/Utils.js";
/**
* Waiter to handle events related to highlighting in CyberChef.
*
@@ -312,9 +309,9 @@ HighlighterWaiter.prototype.outputHtmlMousemove = function(e) {
HighlighterWaiter.prototype.selectionInfo = function(start, end) {
const len = end.toString().length;
const width = len < 2 ? 2 : len;
const startStr = Utils.pad(start.toString(), width, " ").replace(/ /g, "&nbsp;");
const endStr = Utils.pad(end.toString(), width, " ").replace(/ /g, "&nbsp;");
const lenStr = Utils.pad((end-start).toString(), width, " ").replace(/ /g, "&nbsp;");
const startStr = start.toString().padStart(width, " ").replace(/ /g, "&nbsp;");
const endStr = end.toString().padStart(width, " ").replace(/ /g, "&nbsp;");
const lenStr = (end-start).toString().padStart(width, " ").replace(/ /g, "&nbsp;");
return "start: " + startStr + "<br>end: " + endStr + "<br>length: " + lenStr;
};
@@ -402,7 +399,7 @@ HighlighterWaiter.prototype.highlight = function(textarea, highlighter, pos) {
// Check if there is a carriage return in the output dish as this will not
// be displayed by the HTML textarea and will mess up highlighting offsets.
if (!this.app.dishStr || this.app.dishStr.indexOf("\r") >= 0) return false;
if (this.manager.output.containsCR()) return false;
const startPlaceholder = "[startHighlight]";
const startPlaceholderRegex = /\[startHighlight\]/g;

View File

@@ -1,4 +1,4 @@
import Utils from "../core/Utils.js";
import LoaderWorker from "worker-loader?inline&fallback=false!./LoaderWorker.js";
/**
@@ -33,6 +33,9 @@ const InputWaiter = function(app, manager) {
144, //Num
145, //Scroll
];
this.loaderWorker = null;
this.fileBuffer = null;
};
@@ -42,20 +45,52 @@ const InputWaiter = function(app, manager) {
* @returns {string}
*/
InputWaiter.prototype.get = function() {
return document.getElementById("input-text").value;
return this.fileBuffer || document.getElementById("input-text").value;
};
/**
* Sets the input in the input textarea.
* Sets the input in the input area.
*
* @param {string} input
* @param {string|File} input
*
* @fires Manager#statechange
*/
InputWaiter.prototype.set = function(input) {
document.getElementById("input-text").value = input;
window.dispatchEvent(this.manager.statechange);
const inputText = document.getElementById("input-text");
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);
}
};
/**
* Shows file details.
*
* @param {File} file
*/
InputWaiter.prototype.setFile = function(file) {
// Display file overlay in input area with details
const fileOverlay = document.getElementById("input-file"),
fileName = document.getElementById("input-file-name"),
fileSize = document.getElementById("input-file-size"),
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";
fileType.textContent = file.type || "unknown";
fileLoaded.textContent = "0%";
};
@@ -69,22 +104,29 @@ InputWaiter.prototype.setInputInfo = function(length, lines) {
let width = length.toString().length;
width = width < 2 ? 2 : width;
const lengthStr = Utils.pad(length.toString(), width, " ").replace(/ /g, "&nbsp;");
const linesStr = Utils.pad(lines.toString(), width, " ").replace(/ /g, "&nbsp;");
const lengthStr = length.toString().padStart(width, " ").replace(/ /g, "&nbsp;");
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, "&nbsp;");
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();
@@ -93,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.
@@ -118,7 +189,7 @@ InputWaiter.prototype.inputDragover = function(e) {
e.stopPropagation();
e.preventDefault();
e.target.classList.add("dropping-file");
e.target.closest("#input-text,#input-file").classList.add("dropping-file");
};
@@ -131,7 +202,8 @@ InputWaiter.prototype.inputDragover = function(e) {
InputWaiter.prototype.inputDragleave = function(e) {
e.stopPropagation();
e.preventDefault();
e.target.classList.remove("dropping-file");
document.getElementById("input-text").classList.remove("dropping-file");
document.getElementById("input-file").classList.remove("dropping-file");
};
@@ -149,57 +221,62 @@ InputWaiter.prototype.inputDrop = function(e) {
e.stopPropagation();
e.preventDefault();
const el = e.target;
const file = e.dataTransfer.files[0];
const text = e.dataTransfer.getData("Text");
const reader = new FileReader();
let inputCharcode = "";
let offset = 0;
const CHUNK_SIZE = 20480; // 20KB
const setInput = function() {
this.app.autoBakePause = true;
const recipeConfig = this.app.getRecipeConfig();
if (!recipeConfig[0] || recipeConfig[0].op !== "From Hex") {
recipeConfig.unshift({op: "From Hex", args: ["Space"]});
this.app.setRecipeConfig(recipeConfig);
}
this.app.autoBakePause = false;
document.getElementById("input-text").classList.remove("dropping-file");
document.getElementById("input-file").classList.remove("dropping-file");
this.set(inputCharcode);
el.classList.remove("loadingFile");
}.bind(this);
const seek = function() {
if (offset >= file.size) {
setInput();
return;
}
el.value = "Processing... " + Math.round(offset / file.size * 100) + "%";
const slice = file.slice(offset, offset + CHUNK_SIZE);
reader.readAsArrayBuffer(slice);
};
reader.onload = function(e) {
const data = new Uint8Array(reader.result);
inputCharcode += Utils.toHexFast(data);
offset += CHUNK_SIZE;
seek();
};
el.classList.remove("dropping-file");
if (text) {
this.closeFile();
this.set(text);
return;
}
if (file) {
el.classList.add("loadingFile");
seek();
} else if (text) {
this.set(text);
this.closeFile();
this.loaderWorker = new LoaderWorker();
this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this));
this.loaderWorker.postMessage({"file": file});
this.set(file);
}
};
/**
* Handler for messages sent back by the LoaderWorker.
*
* @param {MessageEvent} e
*/
InputWaiter.prototype.handleLoaderMessage = function(e) {
const r = e.data;
if (r.hasOwnProperty("progress")) {
const fileLoaded = document.getElementById("input-file-loaded");
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;
window.dispatchEvent(this.manager.statechange);
}
};
/**
* Handler for file close events.
*/
InputWaiter.prototype.closeFile = function() {
if (this.loaderWorker) this.loaderWorker.terminate();
this.fileBuffer = null;
document.getElementById("input-file").style.display = "none";
};
/**
* Handler for clear IO events.
* Resets the input, output and info areas.
@@ -207,6 +284,8 @@ InputWaiter.prototype.inputDrop = function(e) {
* @fires Manager#statechange
*/
InputWaiter.prototype.clearIoClick = function() {
this.closeFile();
this.manager.output.closeFile();
this.manager.highlighter.removeHighlights();
document.getElementById("input-text").value = "";
document.getElementById("output-text").value = "";

54
src/web/LoaderWorker.js Normal file
View File

@@ -0,0 +1,54 @@
/**
* Web Worker to load large amounts of data without locking up the UI.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
/**
* Respond to message from parent thread.
*/
self.addEventListener("message", function(e) {
const r = e.data;
if (r.hasOwnProperty("file")) {
self.loadFile(r.file);
}
});
/**
* Loads a file object into an ArrayBuffer, then transfers it back to the parent thread.
*
* @param {File} file
*/
self.loadFile = function(file) {
const reader = new FileReader();
let data = new Uint8Array(file.size);
let offset = 0;
const CHUNK_SIZE = 10485760; // 10MiB
const seek = function() {
if (offset >= file.size) {
self.postMessage({"progress": 100});
self.postMessage({"fileBuffer": data.buffer}, [data.buffer]);
return;
}
self.postMessage({"progress": Math.round(offset / file.size * 100)});
const slice = file.slice(offset, offset + CHUNK_SIZE);
reader.readAsArrayBuffer(slice);
};
reader.onload = function(e) {
data.set(new Uint8Array(reader.result), offset);
offset += CHUNK_SIZE;
seek();
};
reader.onerror = function(e) {
self.postMessage({"error": file.error.message});
};
seek();
};

View File

@@ -8,6 +8,7 @@ import OutputWaiter from "./OutputWaiter.js";
import OptionsWaiter from "./OptionsWaiter.js";
import HighlighterWaiter from "./HighlighterWaiter.js";
import SeasonalWaiter from "./SeasonalWaiter.js";
import BindingsWaiter from "./BindingsWaiter.js";
/**
@@ -57,9 +58,10 @@ const Manager = function(app) {
this.ops = new OperationsWaiter(this.app, this);
this.input = new InputWaiter(this.app, this);
this.output = new OutputWaiter(this.app, this);
this.options = new OptionsWaiter(this.app);
this.options = new OptionsWaiter(this.app, this);
this.highlighter = new HighlighterWaiter(this.app, this);
this.seasonal = new SeasonalWaiter(this.app, this);
this.bindings = new BindingsWaiter(this.app, this);
// Object to store dynamic handlers to fire on elements that may not exist yet
this.dynamicHandlers = {};
@@ -75,6 +77,7 @@ Manager.prototype.setup = function() {
this.worker.registerChefWorker();
this.recipe.initialiseOperationDragNDrop();
this.controls.autoBakeChange();
this.bindings.updateKeybList();
this.seasonal.load();
};
@@ -116,12 +119,11 @@ Manager.prototype.initialiseEventListeners = function() {
this.addDynamicListener(".op-list .op-icon", "mouseover", this.ops.opIconMouseover, this.ops);
this.addDynamicListener(".op-list .op-icon", "mouseleave", this.ops.opIconMouseleave, this.ops);
this.addDynamicListener(".op-list", "oplistcreate", this.ops.opListCreate, this.ops);
this.addDynamicListener("li.operation", "operationadd", this.recipe.opAdd.bind(this.recipe));
this.addDynamicListener("li.operation", "operationadd", this.recipe.opAdd, this.recipe);
// Recipe
this.addDynamicListener(".arg", "keyup", this.recipe.ingChange, this.recipe);
this.addDynamicListener(".arg", "change", this.recipe.ingChange, this.recipe);
this.addDynamicListener(".arg", "input", this.recipe.ingChange, this.recipe);
this.addDynamicListener(".arg:not(select)", "input", this.recipe.ingChange, this.recipe);
this.addDynamicListener(".arg[type=checkbox], .arg[type=radio], select.arg", "change", this.recipe.ingChange, this.recipe);
this.addDynamicListener(".disable-icon", "click", this.recipe.disableClick, this.recipe);
this.addDynamicListener(".breakpoint", "click", this.recipe.breakpointClick, this.recipe);
this.addDynamicListener("#rec-list li.operation", "dblclick", this.recipe.operationDblclick, this.recipe);
@@ -130,19 +132,22 @@ 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));
document.getElementById("input-text").addEventListener("dragover", this.input.inputDragover.bind(this.input));
document.getElementById("input-text").addEventListener("dragleave", this.input.inputDragleave.bind(this.input));
document.getElementById("input-text").addEventListener("drop", this.input.inputDrop.bind(this.input));
this.addListeners("#input-text,#input-file", "dragover", this.input.inputDragover, this.input);
this.addListeners("#input-text,#input-file", "dragleave", this.input.inputDragleave, this.input);
this.addListeners("#input-text,#input-file", "drop", this.input.inputDrop, this.input);
document.getElementById("input-text").addEventListener("scroll", this.highlighter.inputScroll.bind(this.highlighter));
document.getElementById("input-text").addEventListener("mouseup", this.highlighter.inputMouseup.bind(this.highlighter));
document.getElementById("input-text").addEventListener("mousemove", this.highlighter.inputMousemove.bind(this.highlighter));
this.addMultiEventListener("#input-text", "mousedown dblclick select", this.highlighter.inputMousedown, this.highlighter);
document.querySelector("#input-file .close").addEventListener("click", this.input.clearIoClick.bind(this.input));
// Output
document.getElementById("save-to-file").addEventListener("click", this.output.saveClick.bind(this.output));
document.getElementById("copy-output").addEventListener("click", this.output.copyClick.bind(this.output));
document.getElementById("switch").addEventListener("click", this.output.switchClick.bind(this.output));
document.getElementById("undo-switch").addEventListener("click", this.output.undoSwitchClick.bind(this.output));
document.getElementById("maximise-output").addEventListener("click", this.output.maximiseOutputClick.bind(this.output));
@@ -154,18 +159,24 @@ Manager.prototype.initialiseEventListeners = function() {
this.addMultiEventListener("#output-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter);
this.addMultiEventListener("#output-html", "mousedown dblclick select", this.highlighter.outputHtmlMousedown, this.highlighter);
this.addDynamicListener(".file-switch", "click", this.output.fileSwitch, this.output);
this.addDynamicListener("#output-file-download", "click", this.output.downloadFile, this.output);
this.addDynamicListener("#output-file-slice", "click", this.output.displayFileSlice, this.output);
document.getElementById("show-file-overlay").addEventListener("click", this.output.showFileOverlayClick.bind(this.output));
// Options
document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options));
document.getElementById("reset-options").addEventListener("click", this.options.resetOptionsClick.bind(this.options));
$(document).on("switchChange.bootstrapSwitch", ".option-item input:checkbox", this.options.switchChange.bind(this.options));
$(document).on("switchChange.bootstrapSwitch", ".option-item input:checkbox", this.options.setWordWrap.bind(this.options));
$(document).on("switchChange.bootstrapSwitch", ".option-item input:checkbox#useMetaKey", this.bindings.updateKeybList.bind(this.bindings));
this.addDynamicListener(".option-item input[type=number]", "keyup", this.options.numberChange, this.options);
this.addDynamicListener(".option-item input[type=number]", "change", this.options.numberChange, this.options);
this.addDynamicListener(".option-item select", "change", this.options.selectChange, this.options);
document.getElementById("theme").addEventListener("change", this.options.themeChange.bind(this.options));
document.getElementById("logLevel").addEventListener("change", this.options.logLevelChange.bind(this.options));
// Misc
window.addEventListener("keydown", this.bindings.parseInput.bind(this.bindings));
document.getElementById("alert-close").addEventListener("click", this.app.alertCloseClick.bind(this.app));
};

View File

@@ -8,8 +8,9 @@
* @constructor
* @param {App} app - The main view object for CyberChef.
*/
const OptionsWaiter = function(app) {
const OptionsWaiter = function(app, manager) {
this.app = app;
this.manager = manager;
};
@@ -86,6 +87,7 @@ OptionsWaiter.prototype.switchChange = function(e, state) {
const el = e.target;
const option = el.getAttribute("option");
log.debug(`Setting ${option} to ${state}`);
this.app.options[option] = state;
if (this.app.isLocalStorageAvailable())
@@ -102,8 +104,10 @@ OptionsWaiter.prototype.switchChange = function(e, state) {
OptionsWaiter.prototype.numberChange = function(e) {
const el = e.target;
const option = el.getAttribute("option");
const val = parseInt(el.value, 10);
this.app.options[option] = parseInt(el.value, 10);
log.debug(`Setting ${option} to ${val}`);
this.app.options[option] = val;
if (this.app.isLocalStorageAvailable())
localStorage.setItem("options", JSON.stringify(this.app.options));
@@ -120,6 +124,7 @@ OptionsWaiter.prototype.selectChange = function(e) {
const el = e.target;
const option = el.getAttribute("option");
log.debug(`Setting ${option} to ${el.value}`);
this.app.options[option] = el.value;
if (this.app.isLocalStorageAvailable())
@@ -149,6 +154,8 @@ OptionsWaiter.prototype.setWordWrap = function() {
/**
* Changes the theme by setting the class of the <html> element.
*
* @param {Event} e
*/
OptionsWaiter.prototype.themeChange = function (e) {
const themeClass = e.target.value;
@@ -156,4 +163,16 @@ OptionsWaiter.prototype.themeChange = function (e) {
document.querySelector(":root").className = themeClass;
};
/**
* Changes the console logging level.
*
* @param {Event} e
*/
OptionsWaiter.prototype.logLevelChange = function (e) {
const level = e.target.value;
log.setLevel(level, false);
this.manager.worker.setLogLevel();
};
export default OptionsWaiter;

View File

@@ -1,4 +1,5 @@
import Utils from "../core/Utils.js";
import FileSaver from "file-saver";
/**
@@ -15,6 +16,9 @@ import Utils from "../core/Utils.js";
const OutputWaiter = function(app, manager) {
this.app = app;
this.manager = manager;
this.dishBuffer = null;
this.dishStr = null;
};
@@ -31,47 +35,151 @@ OutputWaiter.prototype.get = function() {
/**
* Sets the output in the output textarea.
*
* @param {string} dataStr - The output string/HTML
* @param {string|ArrayBuffer} data - The output string/HTML/ArrayBuffer
* @param {string} type - The data type of the output
* @param {number} duration - The length of time (ms) it took to generate the output
* @param {boolean} [preserveBuffer=false] - Whether to preserve the dishBuffer
*/
OutputWaiter.prototype.set = function(dataStr, type, duration) {
OutputWaiter.prototype.set = function(data, type, duration, preserveBuffer) {
log.debug("Output type: " + type);
const outputText = document.getElementById("output-text");
const outputHtml = document.getElementById("output-html");
const outputFile = document.getElementById("output-file");
const outputHighlighter = document.getElementById("output-highlighter");
const inputHighlighter = document.getElementById("input-highlighter");
let scriptElements, lines, length;
if (type === "html") {
outputText.style.display = "none";
outputHtml.style.display = "block";
outputHighlighter.display = "none";
inputHighlighter.display = "none";
if (!preserveBuffer) {
this.closeFile();
document.getElementById("show-file-overlay").style.display = "none";
}
outputText.value = "";
outputHtml.innerHTML = dataStr;
switch (type) {
case "html":
outputText.style.display = "none";
outputHtml.style.display = "block";
outputFile.style.display = "none";
outputHighlighter.display = "none";
inputHighlighter.display = "none";
// Execute script sections
const scriptElements = outputHtml.querySelectorAll("script");
for (let i = 0; i < scriptElements.length; i++) {
try {
eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval
} catch (err) {
console.error(err);
outputText.value = "";
outputHtml.innerHTML = data;
this.dishStr = Utils.stripHtmlTags(data, true);
length = data.length;
lines = this.dishStr.count("\n") + 1;
// Execute script sections
scriptElements = outputHtml.querySelectorAll("script");
for (let i = 0; i < scriptElements.length; i++) {
try {
eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval
} catch (err) {
log.error(err);
}
}
}
} else {
outputText.style.display = "block";
outputHtml.style.display = "none";
outputHighlighter.display = "block";
inputHighlighter.display = "block";
break;
case "ArrayBuffer":
outputText.style.display = "block";
outputHtml.style.display = "none";
outputHighlighter.display = "none";
inputHighlighter.display = "none";
outputText.value = Utils.printable(dataStr, true);
outputHtml.innerHTML = "";
outputText.value = "";
outputHtml.innerHTML = "";
this.dishStr = "";
length = data.byteLength;
this.setFile(data);
break;
case "string":
default:
outputText.style.display = "block";
outputHtml.style.display = "none";
outputFile.style.display = "none";
outputHighlighter.display = "block";
inputHighlighter.display = "block";
outputText.value = Utils.printable(data, true);
outputHtml.innerHTML = "";
lines = data.count("\n") + 1;
length = data.length;
this.dishStr = data;
break;
}
this.manager.highlighter.removeHighlights();
const lines = dataStr.count("\n") + 1;
this.setOutputInfo(dataStr.length, lines, duration);
this.setOutputInfo(length, lines, duration);
};
/**
* Shows file details.
*
* @param {ArrayBuffer} buf
*/
OutputWaiter.prototype.setFile = function(buf) {
this.dishBuffer = buf;
const file = new File([buf], "output.dat");
// Display file overlay in output area with details
const fileOverlay = document.getElementById("output-file"),
fileSize = document.getElementById("output-file-size");
fileOverlay.style.display = "block";
fileSize.textContent = file.size.toLocaleString() + " bytes";
};
/**
* Removes the output file and nulls its memory.
*/
OutputWaiter.prototype.closeFile = function() {
this.dishBuffer = null;
document.getElementById("output-file").style.display = "none";
};
/**
* Handler for file download events.
*/
OutputWaiter.prototype.downloadFile = function() {
this.filename = window.prompt("Please enter a filename:", this.filename || "download.dat");
const file = new File([this.dishBuffer], this.filename);
if (this.filename) FileSaver.saveAs(file, this.filename, false);
};
/**
* Handler for file slice display events.
*/
OutputWaiter.prototype.displayFileSlice = function() {
const startTime = new Date().getTime(),
showFileOverlay = document.getElementById("show-file-overlay"),
sliceFromEl = document.getElementById("output-file-slice-from"),
sliceToEl = document.getElementById("output-file-slice-to"),
sliceFrom = parseInt(sliceFromEl.value, 10),
sliceTo = parseInt(sliceToEl.value, 10),
str = Utils.arrayBufferToStr(this.dishBuffer.slice(sliceFrom, sliceTo));
showFileOverlay.style.display = "block";
this.set(str, "string", new Date().getTime() - startTime, true);
};
/**
* Handler for show file overlay events.
*
* @param {Event} e
*/
OutputWaiter.prototype.showFileOverlayClick = function(e) {
const outputFile = document.getElementById("output-file"),
showFileOverlay = e.target;
outputFile.style.display = "block";
showFileOverlay.style.display = "none";
this.setOutputInfo(this.dishBuffer.byteLength, null, 0);
};
@@ -86,13 +194,17 @@ OutputWaiter.prototype.setOutputInfo = function(length, lines, duration) {
let width = length.toString().length;
width = width < 4 ? 4 : width;
const lengthStr = Utils.pad(length.toString(), width, " ").replace(/ /g, "&nbsp;");
const linesStr = Utils.pad(lines.toString(), width, " ").replace(/ /g, "&nbsp;");
const timeStr = Utils.pad(duration.toString() + "ms", width, " ").replace(/ /g, "&nbsp;");
const lengthStr = length.toString().padStart(width, " ").replace(/ /g, "&nbsp;");
const timeStr = (duration.toString() + "ms").padStart(width, " ").replace(/ /g, "&nbsp;");
document.getElementById("output-info").innerHTML = "time: " + timeStr +
"<br>length: " + lengthStr +
"<br>lines: " + linesStr;
let msg = "time: " + timeStr + "<br>length: " + lengthStr;
if (typeof lines === "number") {
const linesStr = lines.toString().padStart(width, " ").replace(/ /g, "&nbsp;");
msg += "<br>lines: " + linesStr;
}
document.getElementById("output-info").innerHTML = msg;
document.getElementById("input-selection-info").innerHTML = "";
document.getElementById("output-selection-info").innerHTML = "";
};
@@ -105,17 +217,20 @@ OutputWaiter.prototype.setOutputInfo = function(length, lines, duration) {
OutputWaiter.prototype.adjustWidth = function() {
const output = document.getElementById("output");
const saveToFile = document.getElementById("save-to-file");
const copyOutput = document.getElementById("copy-output");
const switchIO = document.getElementById("switch");
const undoSwitch = document.getElementById("undo-switch");
const maximiseOutput = document.getElementById("maximise-output");
if (output.clientWidth < 680) {
saveToFile.childNodes[1].nodeValue = "";
copyOutput.childNodes[1].nodeValue = "";
switchIO.childNodes[1].nodeValue = "";
undoSwitch.childNodes[1].nodeValue = "";
maximiseOutput.childNodes[1].nodeValue = "";
} else {
saveToFile.childNodes[1].nodeValue = " Save to file";
copyOutput.childNodes[1].nodeValue = " Copy output";
switchIO.childNodes[1].nodeValue = " Move output to input";
undoSwitch.childNodes[1].nodeValue = " Undo";
maximiseOutput.childNodes[1].nodeValue =
@@ -126,24 +241,51 @@ OutputWaiter.prototype.adjustWidth = function() {
/**
* Handler for save click events.
* Saves the current output to a file, downloaded as a URL octet stream.
* Saves the current output to a file.
*/
OutputWaiter.prototype.saveClick = function() {
const data = Utils.toBase64(this.app.dishStr);
const filename = window.prompt("Please enter a filename:", "download.dat");
if (filename) {
const el = document.createElement("a");
el.setAttribute("href", "data:application/octet-stream;base64;charset=utf-8," + data);
el.setAttribute("download", filename);
// Firefox requires that the element be added to the DOM before it can be clicked
el.style.display = "none";
document.body.appendChild(el);
el.click();
el.remove();
if (!this.dishBuffer) {
this.dishBuffer = new Uint8Array(Utils.strToCharcode(this.dishStr)).buffer;
}
this.downloadFile();
};
/**
* Handler for copy click events.
* Copies the output to the clipboard.
*/
OutputWaiter.prototype.copyClick = function() {
// Create invisible textarea to populate with the raw dishStr (not the printable version that
// contains dots instead of the actual bytes)
const textarea = document.createElement("textarea");
textarea.style.position = "fixed";
textarea.style.top = 0;
textarea.style.left = 0;
textarea.style.width = 0;
textarea.style.height = 0;
textarea.style.border = "none";
textarea.value = this.dishStr;
document.body.appendChild(textarea);
// Select and copy the contents of this textarea
let success = false;
try {
textarea.select();
success = this.dishStr && document.execCommand("copy");
} catch (err) {
success = false;
}
if (success) {
this.app.alert("Copied raw output successfully.", "success", 2000);
} else {
this.app.alert("Sorry, the output could not be copied.", "danger", 2000);
}
// Clean up
document.body.removeChild(textarea);
};
@@ -154,7 +296,17 @@ OutputWaiter.prototype.saveClick = function() {
OutputWaiter.prototype.switchClick = function() {
this.switchOrigData = this.manager.input.get();
document.getElementById("undo-switch").disabled = false;
this.app.setInput(this.app.dishStr);
if (this.dishBuffer) {
this.manager.input.setFile(new File([this.dishBuffer], "output.dat"));
this.manager.input.handleLoaderMessage({
data: {
progress: 100,
fileBuffer: this.dishBuffer
}
});
} else {
this.app.setInput(this.dishStr);
}
};
@@ -169,7 +321,7 @@ OutputWaiter.prototype.undoSwitchClick = function() {
/**
* Handler for file switch click events.
* Moves a files data for items created via Utils.displayFilesAsHTML to the input.
* Moves a file's data for items created via Utils.displayFilesAsHTML to the input.
*/
OutputWaiter.prototype.fileSwitch = function(e) {
e.preventDefault();
@@ -241,4 +393,14 @@ OutputWaiter.prototype.setStatusMsg = function(msg) {
el.textContent = msg;
};
/**
* Returns true if the output contains carriage returns
*
* @returns {boolean}
*/
OutputWaiter.prototype.containsCR = function() {
return this.dishStr.indexOf("\r") >= 0;
};
export default OutputWaiter;

View File

@@ -1,5 +1,6 @@
import HTMLOperation from "./HTMLOperation.js";
import Sortable from "sortablejs";
import Utils from "../core/Utils.js";
/**
@@ -191,7 +192,7 @@ RecipeWaiter.prototype.favDrop = function(e) {
*
* @fires Manager#statechange
*/
RecipeWaiter.prototype.ingChange = function() {
RecipeWaiter.prototype.ingChange = function(e) {
window.dispatchEvent(this.manager.statechange);
};
@@ -252,7 +253,7 @@ RecipeWaiter.prototype.breakpointClick = function(e) {
*/
RecipeWaiter.prototype.operationDblclick = function(e) {
e.target.remove();
window.dispatchEvent(this.manager.statechange);
this.opRemove(e);
};
@@ -265,7 +266,7 @@ RecipeWaiter.prototype.operationDblclick = function(e) {
*/
RecipeWaiter.prototype.operationChildDblclick = function(e) {
e.target.parentNode.remove();
window.dispatchEvent(this.manager.statechange);
this.opRemove(e);
};
@@ -420,6 +421,7 @@ RecipeWaiter.prototype.dropdownToggleClick = function(e) {
* @param {event} e
*/
RecipeWaiter.prototype.opAdd = function(e) {
log.debug(`'${e.target.querySelector(".arg-title").textContent}' added to recipe`);
window.dispatchEvent(this.manager.statechange);
};
@@ -432,7 +434,34 @@ RecipeWaiter.prototype.opAdd = function(e) {
* @param {event} e
*/
RecipeWaiter.prototype.opRemove = function(e) {
log.debug("Operation removed from recipe");
window.dispatchEvent(this.manager.statechange);
};
/**
* Sets register values.
*
* @param {number} opIndex
* @param {number} numPrevRegisters
* @param {string[]} registers
*/
RecipeWaiter.prototype.setRegisters = function(opIndex, numPrevRegisters, registers) {
const op = document.querySelector(`#rec-list .operation:nth-child(${opIndex + 1})`),
prevRegList = op.querySelector(".register-list");
// Remove previous div
if (prevRegList) prevRegList.remove();
let registerList = [];
for (let i = 0; i < registers.length; i++) {
registerList.push(`$R${numPrevRegisters + i} = ${Utils.escapeHtml(Utils.truncate(Utils.printable(registers[i]), 100))}`);
}
const registerListEl = `<div class="register-list">
${registerList.join("<br>")}
</div>`;
op.insertAdjacentHTML("beforeend", registerListEl);
};
export default RecipeWaiter;

View File

@@ -1,4 +1,3 @@
import Utils from "../core/Utils.js";
import ChefWorker from "worker-loader?inline&fallback=false!../core/ChefWorker.js";
/**
@@ -22,8 +21,10 @@ const WorkerWaiter = function(app, manager) {
* Sets up the ChefWorker and associated listeners.
*/
WorkerWaiter.prototype.registerChefWorker = function() {
log.debug("Registering new ChefWorker");
this.chefWorker = new ChefWorker();
this.chefWorker.addEventListener("message", this.handleChefMessage.bind(this));
this.setLogLevel();
let docURL = document.location.href.split(/[#?]/)[0];
const index = docURL.lastIndexOf("/");
@@ -41,8 +42,10 @@ WorkerWaiter.prototype.registerChefWorker = function() {
*/
WorkerWaiter.prototype.handleChefMessage = function(e) {
const r = e.data;
log.debug("Receiving '" + r.action + "' from ChefWorker");
switch (r.action) {
case "bakeSuccess":
case "bakeComplete":
this.bakingComplete(r.data);
break;
case "bakeError":
@@ -53,19 +56,24 @@ WorkerWaiter.prototype.handleChefMessage = function(e) {
break;
case "workerLoaded":
this.app.workerLoaded = true;
log.debug("ChefWorker loaded");
this.app.loaded();
break;
case "statusMessage":
this.manager.output.setStatusMsg(r.data);
break;
case "optionUpdate":
log.debug(`Setting ${r.data.option} to ${r.data.value}`);
this.app.options[r.data.option] = r.data.value;
break;
case "setRegisters":
this.manager.recipe.setRegisters(r.data.opIndex, r.data.numPrevRegisters, r.data.registers);
break;
case "highlightsCalculated":
this.manager.highlighter.displayHighlights(r.data.pos, r.data.direction);
break;
default:
console.error("Unrecognised message from ChefWorker", e);
log.error("Unrecognised message from ChefWorker", e);
break;
}
};
@@ -108,10 +116,10 @@ WorkerWaiter.prototype.bakingComplete = function(response) {
this.app.handleError(response.error);
}
this.app.dishStr = response.type === "html" ? Utils.stripHtmlTags(response.result, true) : response.result;
this.app.progress = response.progress;
this.manager.recipe.updateBreakpointIndicator(response.progress);
this.manager.output.set(response.result, response.type, response.duration);
log.debug("--- Bake complete ---");
};
@@ -144,7 +152,7 @@ WorkerWaiter.prototype.bake = function(input, recipeConfig, options, progress, s
* Asks the ChefWorker to run a silent bake, forcing the browser to load and cache all the relevant
* JavaScript code needed to do a real bake.
*
* @param {Objectp[]} [recipeConfig]
* @param {Object[]} [recipeConfig]
*/
WorkerWaiter.prototype.silentBake = function(recipeConfig) {
this.chefWorker.postMessage({
@@ -177,4 +185,19 @@ WorkerWaiter.prototype.highlight = function(recipeConfig, direction, pos) {
};
/**
* Sets the console log level in the worker.
*
* @param {string} level
*/
WorkerWaiter.prototype.setLogLevel = function(level) {
if (!this.chefWorker) return;
this.chefWorker.postMessage({
action: "setLogLevel",
data: log.getLevel()
});
};
export default WorkerWaiter;

View File

@@ -1,17 +1,17 @@
<!-- htmlmin:ignore --><!--
CyberChef - The Cyber Swiss Army Knife
@copyright Crown Copyright 2016
@license Apache-2.0
Copyright 2016 Crown Copyright
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -24,7 +24,7 @@
<head>
<meta charset="UTF-8">
<title>CyberChef</title>
<meta name="copyright" content="Crown Copyright 2016" />
<meta name="description" content="The Cyber Swiss Army Knife - a web app for encryption, encoding, compression and data analysis" />
<meta name="keywords" content="base64, hex, decode, encode, encrypt, decrypt, compress, decompress, regex, regular expressions, hash, crypt, hexadecimal, user agent, url, certificate, x.509, parser, JSON, gzip, md5, sha1, aes, des, blowfish, xor" />
@@ -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");
}
@@ -135,11 +138,11 @@
<ul id="search-results" class="op-list"></ul>
<div id="categories" class="panel-group no-select"></div>
</div>
<div id="recipe" class="split split-horizontal no-select">
<div class="title no-select">Recipe</div>
<ul id="rec-list" class="list-area no-select"></ul>
<div id="controls" class="no-select">
<div id="operational-controls">
<div id="bake-group">
@@ -152,13 +155,13 @@
<div>Auto Bake</div>
</label>
</div>
<div class="btn-group" style="padding-top: 10px;">
<button type="button" class="btn btn-default" id="step"><img aria-hidden="true" src="<%- require('../static/images/step-16x16.png') %>" alt="Footstep Icon"/> Step through</button>
<button type="button" class="btn btn-default" id="clr-breaks"><img aria-hidden="true" src="<%- require('../static/images/erase-16x16.png') %>" alt="Eraser Icon"/> Clear breakpoints</button>
</div>
</div>
<div class="btn-group-vertical" id="extra-controls">
<button type="button" class="btn btn-default" id="save"><img aria-hidden="true" src="<%- require('../static/images/save-16x16.png') %>" alt="Save Icon"/> Save recipe</button>
<button type="button" class="btn btn-default" id="load"><img aria-hidden="true" src="<%- require('../static/images/open_yellow-16x16.png') %>" alt="Open Icon"/> Load recipe</button>
@@ -166,7 +169,7 @@
</div>
</div>
</div>
<div class="split split-horizontal" id="IO">
<div id="input" class="split no-select">
<div class="title no-select">
@@ -181,14 +184,29 @@
<div class="textarea-wrapper no-select">
<div id="input-highlighter" class="no-select"></div>
<textarea id="input-text"></textarea>
<div id="input-file">
<div style="position: relative; height: 100%;">
<div class="card">
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon"/>
<div class="card-body">
<button type="button" class="close" id="input-file-close">&times;</button>
Name: <span id="input-file-name"></span><br>
Size: <span id="input-file-size"></span><br>
Type: <span id="input-file-type"></span><br>
Loaded: <span id="input-file-loaded"></span>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="output" class="split">
<div class="title no-select">
<label for="output-text">Output</label>
<div class="btn-group io-btn-group">
<button type="button" class="btn btn-default btn-sm" id="save-to-file" title="Save to file"><img aria-hidden="true" src="<%- require('../static/images/save_as-16x16.png') %>" alt="Save Icon"/> Save to file</button>
<button type="button" class="btn btn-default btn-sm" id="copy-output" title="Copy output"><img aria-hidden="true" src="<%- require('../static/images/copy-16x16.png') %>" alt="Copy Icon"/> Copy raw output</button>
<button type="button" class="btn btn-default btn-sm" id="switch" title="Move output to input"><img aria-hidden="true" src="<%- require('../static/images/switch-16x16.png') %>" alt="Switch Icon"/> Move output to input</button>
<button type="button" class="btn btn-default btn-sm" id="undo-switch" title="Undo move" disabled="disabled"><img aria-hidden="true" src="<%- require('../static/images/undo-16x16.png') %>" alt="Undo Icon"/> Undo</button>
<button type="button" class="btn btn-default btn-sm" id="maximise-output" title="Maximise"><img aria-hidden="true" src="<%- require('../static/images/maximise-16x16.png') %>" alt="Maximise Icon"/> Max</button>
@@ -201,6 +219,26 @@
<div id="output-highlighter" class="no-select"></div>
<div id="output-html"></div>
<textarea id="output-text" readonly="readonly"></textarea>
<img id="show-file-overlay" aria-hidden="true" src="<%- require('../static/images/file-32x32.png') %>" alt="Show file overlay" title="Show file overlay"/>
<div id="output-file">
<div style="position: relative; height: 100%;">
<div class="card">
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon"/>
<div class="card-body">
Size: <span id="output-file-size"></span><br>
<button id="output-file-download" type="button" class="btn btn-primary">Download</button>
<div class="input-group">
<span class="input-group-btn">
<button id="output-file-slice" type="button" class="btn btn-default" title="View slice">&#x1f50d;</button>
</span>
<input type="number" class="form-control" id="output-file-slice-from" placeholder="From" value="0" step="1024" min="0">
<div class="input-group-addon">to</div>
<input type="number" class="form-control" id="output-file-slice-to" placeholder="To" value="1024" step="1024" min="0">
</div>
</div>
</div>
</div>
</div>
<div id="output-loader">
<div class="loader"></div>
<div class="loading-msg"></div>
@@ -210,7 +248,7 @@
</div>
</div>
</div>
<div class="modal" id="save-modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
@@ -261,7 +299,7 @@
</div>
</div>
</div>
<div class="modal" id="load-modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
@@ -287,7 +325,7 @@
</div>
</div>
</div>
<div class="modal" id="options-modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
@@ -307,27 +345,46 @@
</div>
<div class="option-item">
<input type="checkbox" option="updateUrl" id="updateUrl" checked />
<label for="updateUrl"> Update the URL when the input or recipe changes </label>
<label for="updateUrl"> Update the URL when the input or recipe changes</label>
</div>
<div class="option-item">
<input type="checkbox" option="showHighlighter" id="showHighlighter" checked />
<label for="showHighlighter"> Highlight selected bytes in output and input (when possible) </label>
<label for="showHighlighter"> Highlight selected bytes in output and input (when possible)</label>
</div>
<div class="option-item">
<input type="checkbox" option="treatAsUtf8" id="treatAsUtf8" checked />
<label for="treatAsUtf8"> Treat output as UTF-8 if possible </label>
<label for="treatAsUtf8"> Treat output as UTF-8 if possible</label>
</div>
<div class="option-item">
<input type="checkbox" option="wordWrap" id="wordWrap" checked />
<label for="wordWrap"> Word wrap the input and output </label>
<label for="wordWrap"> Word wrap the input and output</label>
</div>
<div class="option-item">
<input type="checkbox" option="showErrors" id="showErrors" checked />
<label for="showErrors"> Operation error reporting (recommended) </label>
<label for="showErrors"> Operation error reporting (recommended)</label>
</div>
<div class="option-item">
<input type="checkbox" option="useMetaKey" id="useMetaKey" />
<label for="useMetaKey"> Use meta key for keybindings (Windows ⊞/Command ⌘)</label>
</div>
<div class="option-item">
<input type="number" option="errorTimeout" id="errorTimeout" />
<label for="errorTimeout"> Operation error timeout in ms (0 for never) </label>
<label for="errorTimeout"> Operation error timeout in ms (0 for never)</label>
</div>
<div class="option-item">
<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">
<option value="silent">Silent</option>
<option value="error">Error</option>
<option value="warn">Warn</option>
<option value="info">Info</option>
<option value="debug">Debug</option>
<option value="trace">Trace</option>
</select>
<label for="logLevel"> Console logging level</label>
</div>
</div>
<div class="modal-footer">
@@ -337,7 +394,7 @@
</div>
</div>
</div>
<div class="modal" id="favourites-modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
@@ -364,7 +421,7 @@
</div>
</div>
</div>
<div class="modal" id="support-modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
@@ -380,6 +437,11 @@
</p>
<p>&copy; Crown Copyright 2016.</p>
<p>Released under the Apache Licence, Version 2.0.</p>
<p>
<a href="https://gitter.im/gchq/CyberChef">
<img src="<%- require('../static/images/gitter-badge.svg') %>">
</a>
</p>
<br>
<br>
<div>
@@ -396,6 +458,10 @@
<img aria-hidden="true" src="<%- require('../static/images/speech-16x16.png') %>" alt="Speech Balloon Icon"/>
About
</a></li>
<li role="presentation"><a href="#keybindings" aria-controls="messages" role="tab" data-toggle="tab">
<img aria-hidden="true" src="<%- require('../static/images/code-16x16.png') %>" alt="List Icon"/>
Keybindings
</a></li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="faqs">
@@ -412,8 +478,10 @@
<li><a href="#recipe=Translate_DateTime_Format('Standard%20date%20and%20time','DD/MM/YYYY%20HH:mm:ss','UTC','dddd%20Do%20MMMM%20YYYY%20HH:mm:ss%20Z%20z','Australia/Queensland')&input=MTUvMDYvMjAxNSAyMDo0NTowMA">Convert a date and time to a different time zone</a></li>
<li><a href="#recipe=Parse_IPv6_address()&input=MjAwMTowMDAwOjQxMzY6ZTM3ODo4MDAwOjYzYmY6M2ZmZjpmZGQy">Parse a Teredo IPv6 address</a></li>
<li><a href="#recipe=From_Hexdump()Gunzip()&input=MDAwMDAwMDAgIDFmIDhiIDA4IDAwIDEyIGJjIGYzIDU3IDAwIGZmIDBkIGM3IGMxIDA5IDAwIDIwICB8Li4uLi6881cu/y7HwS4uIHwKMDAwMDAwMTAgIDA4IDA1IGQwIDU1IGZlIDA0IDJkIGQzIDA0IDFmIGNhIDhjIDQ0IDIxIDViIGZmICB8Li7QVf4uLdMuLsouRCFb/3wKMDAwMDAwMjAgIDYwIGM3IGQ3IDAzIDE2IGJlIDQwIDFmIDc4IDRhIDNmIDA5IDg5IDBiIDlhIDdkICB8YMfXLi6%2BQC54Sj8uLi4ufXwKMDAwMDAwMzAgIDRlIGM4IDRlIDZkIDA1IDFlIDAxIDhiIDRjIDI0IDAwIDAwIDAwICAgICAgICAgICB8TshObS4uLi5MJC4uLnw">Convert data from a hexdump, then decompress</a></li>
<li><a href="#recipe=RC4(%7B'option':'UTF8','string':'secret'%7D,'Hex','Hex')Disassemble_x86('64','Full%20x86%20architecture',16,0,true,true)&input=MjFkZGQyNTQwMTYwZWU2NWZlMDc3NzEwM2YyYTM5ZmJlNWJjYjZhYTBhYWJkNDE0ZjkwYzZjYWY1MzEyNzU0YWY3NzRiNzZiM2JiY2QxOTNjYjNkZGZkYmM1YTI2NTMzYTY4NmI1OWI4ZmVkNGQzODBkNDc0NDIwMWFlYzIwNDA1MDcxMzhlMmZlMmIzOTUwNDQ2ZGIzMWQyYmM2MjliZTRkM2YyZWIwMDQzYzI5M2Q3YTVkMjk2MmMwMGZlNmRhMzAwNzJkOGM1YTZiNGZlN2Q4NTlhMDQwZWVhZjI5OTczMzYzMDJmNWEwZWMxOQ">Decrypt and disassemble shellcode</a></li>
<li><a href="#recipe=Fork('%5C%5Cn','%5C%5Cn',false)From_UNIX_Timestamp('Seconds%20(s)')&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA">Display multiple timestamps as full dates</a></li>
<li><a href="#recipe=Fork('%5C%5Cn','%5C%5Cn',false)Conditional_Jump('1',2,10)To_Hex('Space')Return()To_Base64('A-Za-z0-9%2B/%3D')&input=U29tZSBkYXRhIHdpdGggYSAxIGluIGl0ClNvbWUgZGF0YSB3aXRoIGEgMiBpbiBpdA">Carry out different operations on data of different types</a></li>
<li><a href="#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">Carry out different operations on data of different types</a></li>
<li><a href="#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">Use parts of the input as arguments to operations</a></li>
</ul>
</div>
<blockquote>
@@ -422,8 +490,9 @@
</a>
</blockquote>
<div class="collapse" id="faq-load-files">
<p>Yes! Just drag your file over the input box and drop it. The contents of the file will be converted into hexadecimal and the 'From Hex' operation will be added to the beginning of the recipe (if it's not already there). This is so that special characters like carriage returns aren't removed by your browser.</p>
<p>Please note that loading large files is likely to cause a crash. There's not a lot that can be done about this - browsers just aren't very good at handling and displaying large amounts of data.</p>
<p>Yes! Just drag your file over the input box and drop it.</p>
<p>CyberChef can handle files up to around 500MB (depending on your browser), however some of the operations may take a very long time to run over this much data.</p>
<p>If the output is larger than a certain threshold (default 1MiB), it will be presented to you as a file available for download. Slices of the file can be viewed in the output if you need to inspect them.</p>
</div>
<blockquote>
<a data-toggle="collapse" data-target="#faq-fork">
@@ -447,28 +516,31 @@
<div role="tabpanel" class="tab-pane" id="about" style="padding: 20px;">
<h5><strong>What</strong></h5>
<p>A simple, intuitive web app for analysing and decoding data without having to deal with complex tools or programming languages. CyberChef encourages both technical and non-technical people to explore data formats, encryption and compression.</p><br>
<h5><strong>Why</strong></h5>
<p>Digital data comes in all shapes, sizes and formats in the modern world CyberChef helps to make sense of this data all on one easy-to-use platform.</p><br>
<h5><strong>How</strong></h5>
<p>The interface is designed with simplicity at its heart. Complex techniques are now as trivial as drag-and-drop. Simple functions can be combined to build up a "recipe", potentially resulting in complex analysis, which can be shared with other users and used with their input.</p>
<p>For those comfortable writing code, CyberChef is a quick and efficient way to prototype solutions to a problem which can then be scripted once proven to work.</p><br>
<h5><strong>Who</strong></h5>
<p>It is expected that CyberChef will be useful for cybersecurity and antivirus companies. It should also appeal to the academic world and any individuals or companies involved in the analysis of digital data, be that software developers, analysts, mathematicians or casual puzzle solvers.</p><br>
<h5><strong>Aim</strong></h5>
<p>It is hoped that by releasing CyberChef through <a href="https://github.com/gchq/CyberChef">GitHub</a>, contributions can be added which can be rolled out into future versions of the tool.</p><br>
<br>
<p>There are around 150 useful operations in CyberChef for anyone working on anything vaguely Internet-related, whether you just want to convert a timestamp to a different format, decompress gzipped data, create a SHA3 hash, or parse an X.509 certificate to find out who issued it.</p>
<p>There are around 200 useful operations in CyberChef for anyone working on anything vaguely Internet-related, whether you just want to convert a timestamp to a different format, decompress gzipped data, create a SHA3 hash, or parse an X.509 certificate to find out who issued it.</p>
<p>Its the Cyber Swiss Army Knife.</p>
</div>
<div role="tabpanel" class="tab-pane" id="keybindings" style="padding: 20px;">
<table class="table table-condensed table-bordered table-hover" id="keybList"></table>
</div>
</div>
</div>
</div>
@@ -481,7 +553,7 @@
</div>
</div>
</div>
<div class="modal" id="confirm-modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
@@ -502,6 +574,6 @@
</div>
</div>
</div>
</body>
</html>

View File

@@ -38,14 +38,17 @@ function main() {
];
const defaultOptions = {
updateUrl: true,
showHighlighter: true,
treatAsUtf8: true,
wordWrap: true,
showErrors: true,
errorTimeout: 4000,
attemptHighlight: true,
theme: "classic",
updateUrl: true,
showHighlighter: true,
treatAsUtf8: true,
wordWrap: true,
showErrors: true,
errorTimeout: 4000,
attemptHighlight: true,
theme: "classic",
useMetaKey: false,
ioDisplayThreshold: 512,
logLevel: "info"
};
document.removeEventListener("DOMContentLoaded", main, false);
@@ -53,9 +56,6 @@ function main() {
window.app.setup();
}
// Fix issues with browsers that don't support console.log()
window.console = console || {log: function() {}, error: function() {}};
window.compileTime = moment.tz(COMPILE_TIME, "DD/MM/YYYY HH:mm:ss z", "UTC").valueOf();
window.compileMessage = COMPILE_MSG;

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

0
src/web/static/images/fork_me.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="92" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h34v20H0z"/><path fill="#46BC99" d="M34 0h58v20H34z"/><path fill="url(#b)" d="M0 0h92v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="17" y="15" fill="#010101" fill-opacity=".3">chat</text><text x="17" y="14">chat</text><text x="62" y="15" fill="#010101" fill-opacity=".3">on gitter</text><text x="62" y="14">on gitter</text></g></svg>

After

Width:  |  Height:  |  Size: 733 B

View File

@@ -124,6 +124,12 @@ button.dropdown-toggle {
background-color: var(--secondary-background-colour);
}
.register-list {
background-color: var(--fc-operation-border-colour);
font-family: var(--fixed-width-font-family);
padding: 10px;
}
.op-icon {
float: right;
margin-left: 10px;
@@ -195,3 +201,13 @@ button.dropdown-toggle {
background-color: var(--disabled-bg-colour) !important;
border-color: var(--disabled-border-colour) !important;
}
.break .register-list {
color: var(--fc-breakpoint-operation-font-colour) !important;
background-color: var(--fc-breakpoint-operation-border-colour) !important;
}
.disabled .register-list {
color: var(--disabled-font-colour) !important;
background-color: var(--disabled-border-colour) !important;
}

View File

@@ -28,3 +28,56 @@
margin: 0;
padding: 0;
}
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 400px;
height: 150px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-family: var(--primary-font-family);
color: var(--primary-font-colour);
line-height: 30px;
background-color: var(--primary-background-colour);
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.card>img {
float: left;
width: 128px;
height: 128px;
margin-left: 10px;
margin-top: 11px;
}
.card-body .close {
position: absolute;
right: 10px;
top: 10px;
}
.card-body {
float: left;
padding: 16px;
width: 250px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
user-select: text;
}
.card-body>.btn {
margin-bottom: 15px;
margin-top: 5px;
}
.card input[type=number] {
padding-right: 6px;
padding-left: 6px;
}

View File

@@ -39,7 +39,7 @@
}
.textarea-wrapper textarea,
.textarea-wrapper div {
.textarea-wrapper>div {
font-family: var(--fixed-width-font-family);
font-size: var(--fixed-width-font-size);
color: var(--fixed-width-font-colour);
@@ -77,6 +77,25 @@
transition: all 0.5s ease;
}
#input-file,
#output-file {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: var(--title-background-colour);
display: none;
}
#show-file-overlay {
position: absolute;
right: 15px;
top: 15px;
cursor: pointer;
display: none;
}
.io-btn-group {
float: right;
margin-top: -4px;
@@ -92,6 +111,8 @@
font-family: var(--fixed-width-font-family);
font-weight: normal;
font-size: 8pt;
display: flex;
align-items: center;
}
#input-info {

View File

@@ -64,7 +64,8 @@ a:focus {
.alert,
.modal-content,
.tooltip-inner,
.dropdown-menu {
.dropdown-menu,
.input-group-addon {
border-radius: 0 !important;
}
@@ -187,6 +188,15 @@ optgroup {
color: var(--primary-font-colour);
}
.input-group-addon:not(:first-child):not(:last-child) {
border-left: 0;
border-right: 0;
}
.input-group-btn:first-child>.btn {
border-right: 0;
}
/* Bootstrap-switch */

View File

@@ -1,3 +1,5 @@
/* eslint no-console: 0 */
/**
* TestRunner.js
*
@@ -25,6 +27,10 @@ import "./tests/operations/Hash.js";
import "./tests/operations/Image.js";
import "./tests/operations/MorseCode.js";
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";

File diff suppressed because it is too large Load Diff

View File

@@ -310,4 +310,26 @@ TestRegister.addTests([
}
],
},
{
name: "CSS selector",
input: '<div id="test">\n<p class="a">hello</p>\n<p>world</p>\n<p class="a">again</p>\n</div>',
expectedOutput: '<p class="a">hello</p>\n<p class="a">again</p>',
recipeConfig: [
{
"op": "CSS selector",
"args": ["#test p.a", "\\n"]
}
]
},
{
name: "XPath expression",
input: '<div id="test">\n<p class="a">hello</p>\n<p>world</p>\n<p class="a">again</p>\n</div>',
expectedOutput: '<p class="a">hello</p>\n<p class="a">again</p>',
recipeConfig: [
{
"op": "XPath expression",
"args": ["/div/p[@class=\"a\"]", "\\n"]
}
]
}
]);

View File

@@ -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",
@@ -60,14 +60,15 @@ TestRegister.addTests([
expectedOutput: "U29tZSBkYXRhIHdpdGggYSAxIGluIGl0\n53 6f 6d 65 20 64 61 74 61 20 77 69 74 68 20 61 20 32 20 69 6e 20 69 74\n",
recipeConfig: [
{"op": "Fork", "args": ["\\n", "\\n", false]},
{"op": "Conditional Jump", "args": ["1", "2", "10"]},
{"op": "Conditional Jump", "args": ["1", false, "skipReturn", "10"]},
{"op": "To Hex", "args": ["Space"]},
{"op": "Return", "args": []},
{"op": "Label", "args": ["skipReturn"]},
{"op": "To Base64", "args": ["A-Za-z0-9+/="]}
]
},
{
name: "Jump: skips 0",
name: "Jump: Empty Label",
input: [
"should be changed",
].join("\n"),
@@ -77,7 +78,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "Jump",
args: [0, 10],
args: ["", 10],
},
{
op: "Find / Replace",
@@ -105,7 +106,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "Jump",
args: [1, 10],
args: ["skipReplace", 10],
},
{
op: "Find / Replace",
@@ -120,6 +121,10 @@ TestRegister.addTests([
true,
],
},
{
op: "Label",
args: ["skipReplace"]
},
],
},
{
@@ -137,7 +142,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "Conditional Jump",
args: ["match", 0, 0],
args: ["match", false, "", 0],
},
{
op: "Find / Replace",
@@ -212,7 +217,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "Conditional Jump",
args: ["match", 1, 10],
args: ["match", false, "skip match", 10],
},
{
op: "Find / Replace",
@@ -227,6 +232,9 @@ TestRegister.addTests([
true,
],
},
{
op: "Label", args: ["skip match"],
},
{
op: "Find / Replace",
args: [
@@ -251,9 +259,13 @@ TestRegister.addTests([
"replaced",
].join("\n"),
recipeConfig: [
{
op: "Label",
args: ["back to the beginning"],
},
{
op: "Jump",
args: [1],
args: ["skip replace"],
},
{
op: "Find / Replace",
@@ -268,9 +280,13 @@ TestRegister.addTests([
true,
],
},
{
op: "Label",
args: ["skip replace"],
},
{
op: "Conditional Jump",
args: ["match", -2, 10],
args: ["match", false, "back to the beginning", 10],
},
],
},

View File

@@ -294,6 +294,116 @@ TestRegister.addTests([
}
]
},
{
name: "HAS-160",
input: "Hello, World!",
expectedOutput: "8f6dd8d7c8a04b1cb3831adc358b1e4ac2ed5984",
recipeConfig: [
{
"op": "HAS-160",
"args": []
}
]
},
{
name: "Whirlpool-0",
input: "Hello, World!",
expectedOutput: "1c327026f565a0105a827efbfb3d3635cdb042c0aabb8416e96deb128e6c5c8684b13541cf31c26c1488949df050311c6999a12eb0e7002ad716350f5c7700ca",
recipeConfig: [
{
"op": "Whirlpool",
"args": ["Whirlpool-0"]
}
]
},
{
name: "Whirlpool-T",
input: "Hello, World!",
expectedOutput: "16c581089b6a6f356ae56e16a63a4c613eecd82a2a894b293f5ee45c37a31d09d7a8b60bfa7e414bd4a7166662cea882b5cf8c96b7d583fc610ad202591bcdb1",
recipeConfig: [
{
"op": "Whirlpool",
"args": ["Whirlpool-T"]
}
]
},
{
name: "Whirlpool",
input: "Hello, World!",
expectedOutput: "3d837c9ef7bb291bd1dcfc05d3004af2eeb8c631dd6a6c4ba35159b8889de4b1ec44076ce7a8f7bfa497e4d9dcb7c29337173f78d06791f3c3d9e00cc6017f0b",
recipeConfig: [
{
"op": "Whirlpool",
"args": ["Whirlpool"]
}
]
},
{
name: "Snefru 2 128",
input: "Hello, World!",
expectedOutput: "a4ad2b8848580511d0884fb4233a7e7a",
recipeConfig: [
{
"op": "Snefru",
"args": ["2", "128"]
}
]
},
{
name: "Snefru 4 128",
input: "Hello, World!",
expectedOutput: "d154eae2c9ffbcd2e1bdaf0b84736126",
recipeConfig: [
{
"op": "Snefru",
"args": ["4", "128"]
}
]
},
{
name: "Snefru 8 128",
input: "Hello, World!",
expectedOutput: "6f3d55b69557abb0a3c4e9de9d29ba5d",
recipeConfig: [
{
"op": "Snefru",
"args": ["8", "128"]
}
]
},
{
name: "Snefru 2 256",
input: "Hello, World!",
expectedOutput: "65736daba648de28ef4c4a316b4684584ecf9f22ddb5c457729e6bf0f40113c4",
recipeConfig: [
{
"op": "Snefru",
"args": ["2", "256"]
}
]
},
{
name: "Snefru 4 256",
input: "Hello, World!",
expectedOutput: "71b0ea4b3e33f2e58bcc67c8a8de060b99ec0107355bbfdc18d8f65f0194ffcc",
recipeConfig: [
{
"op": "Snefru",
"args": ["4", "256"]
}
]
},
{
name: "Snefru 8 256",
input: "Hello, World!",
expectedOutput: "255cd401414c79588cf689e8d5ff0536a2cfab83fcae36e654f202b09bc4b8a7",
recipeConfig: [
{
"op": "Snefru",
"args": ["8", "256"]
}
]
},
{
name: "HMAC SHA256",
input: "Hello, World!",

View File

@@ -0,0 +1,34 @@
/**
* NetBIOS tests.
*
* @author bwhitn [brian.m.whitney@outlook.com]
*
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import TestRegister from "../../TestRegister.js";
TestRegister.addTests([
{
name: "Encode NetBIOS name",
input: "The NetBIOS name",
expectedOutput: "FEGIGFCAEOGFHEECEJEPFDCAGOGBGNGF",
recipeConfig: [
{
op: "Encode NetBIOS Name",
args: [65],
},
],
},
{
name: "Decode NetBIOS Name",
input: "FEGIGFCAEOGFHEECEJEPFDCAGOGBGNGF",
expectedOutput: "The NetBIOS name",
recipeConfig: [
{
op: "Decode NetBIOS Name",
args: [65],
},
],
},
]);

View File

@@ -0,0 +1,23 @@
/**
* OTP HOTP tests.
*
* @author bwhitn [brian.m.whitney@outlook.com]
*
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import TestRegister from "../../TestRegister.js";
TestRegister.addTests([
{
name: "Generate HOTP",
input: "12345678901234567890",
expectedOutput: "URI: otpauth://hotp/OTPAuthentication?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ\n\nPassword: 755224",
recipeConfig: [
{
op: "Generate HOTP",
args: ["", 32, 6, 0],
},
],
},
]);

View File

@@ -0,0 +1,68 @@
/**
* PHP tests.
*
* @author Jarmo van Lenthe
*
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import TestRegister from "../../TestRegister.js";
TestRegister.addTests([
{
name: "PHP Deserialize empty array",
input: "a:0:{}",
expectedOutput: "{}",
recipeConfig: [
{
op: "PHP Deserialize",
args: [true],
},
],
},
{
name: "PHP Deserialize integer",
input: "i:10;",
expectedOutput: "10",
recipeConfig: [
{
op: "PHP Deserialize",
args: [true],
},
],
},
{
name: "PHP Deserialize string",
input: "s:17:\"PHP Serialization\";",
expectedOutput: "\"PHP Serialization\"",
recipeConfig: [
{
op: "PHP Deserialize",
args: [true],
},
],
},
{
name: "PHP Deserialize array (JSON)",
input: "a:2:{s:1:\"a\";i:10;i:0;a:1:{s:2:\"ab\";b:1;}}",
expectedOutput: "{\"a\": 10,\"0\": {\"ab\": true}}",
recipeConfig: [
{
op: "PHP Deserialize",
args: [true],
},
],
},
{
name: "PHP Deserialize array (non-JSON)",
input: "a:2:{s:1:\"a\";i:10;i:0;a:1:{s:2:\"ab\";b:1;}}",
expectedOutput: "{\"a\": 10,0: {\"ab\": true}}",
recipeConfig: [
{
op: "PHP Deserialize",
args: [false],
},
],
},
]);

View File

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

View File

@@ -10,8 +10,8 @@ import TestRegister from "../../TestRegister.js";
TestRegister.addTests([
{
name: "SeqUtils - Numeric sort photos",
input: "Photo-1.jpg\nPhoto-4.jpg\nPhoto-2.jpg\nPhoto-3.jpg\n",
expectedOutput: "Photo-1.jpg\nPhoto-2.jpg\nPhoto-3.jpg\nPhoto-4.jpg\n",
input: "Photo-1.jpg\nPhoto-4.jpg\nPhoto-2.jpg\nPhoto-3.jpg",
expectedOutput: "Photo-1.jpg\nPhoto-2.jpg\nPhoto-3.jpg\nPhoto-4.jpg",
recipeConfig: [
{
"op": "Sort",

View File

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

View File

@@ -35,7 +35,8 @@ module.exports = {
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
moment: "moment-timezone"
moment: "moment-timezone",
log: "loglevel"
}),
new webpack.BannerPlugin({
banner: banner,