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

Compare commits

...

335 Commits

Author SHA1 Message Date
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
89ca2cc631 6.0.2 2017-09-20 22:26:54 +01:00
n1474335
e2cae035f2 Accessibility checks are now made before trying to access local storage. Fixes #174. 2017-09-20 22:26:47 +01:00
n1474335
d79a0e737a Added HAS-160 and Whirlpool hashing operations 2017-09-20 09:43:14 +00:00
n1474335
c60ec7c170 6.0.1 2017-09-20 01:18:46 +01:00
n1474335
e42b19d324 Removed excess auto-baking 2017-09-20 01:16:15 +01:00
n1474335
3be370aefa 6.0.0 2017-09-20 00:56:32 +01:00
n1474335
b13917fbdc Merge branch 'feature-threading' 2017-09-20 00:55:37 +01:00
n1474335
9028761821 Loading message fade and cancelBake bugfix 2017-09-20 00:48:37 +01:00
n1474335
f6b52b7c82 Operations can now set options from within the worker 2017-09-20 00:37:57 +01:00
n1474335
13f07abb8a Highlighting now works with the web worker 2017-09-19 23:34:03 +01:00
n1474335
8c960f0661 Moved worker handling code from App.js to WorkerWaiter.js 2017-09-17 19:27:02 +01:00
n1474335
77203a4363 Excluded MetaConfig from JSDoc 2017-09-17 14:59:11 +01:00
n1474335
d3246b7c8b Merged upstream master 2017-09-17 14:53:17 +01:00
n1474335
c93edec55b Switched to val-loader and compiling MetaConfig 2017-09-17 13:47:33 +01:00
n1474335
83d258c396 5.20.0 2017-09-14 15:06:11 +00:00
n1474335
81acb5bdab Merge branch 'feature-hashing' 2017-09-14 15:01:29 +00:00
n1474335
0b79019e0f Added test file. 2017-09-14 14:59:28 +00:00
n1474335
be07298741 Added tests for all hashing operations 2017-09-14 14:54:56 +00:00
n1474335
2d779fdcd0 Added SHA2 512/224 and 512/256 variants as well as RIPEMD 128, 160, 256 and 320. 2017-09-14 14:53:46 +00:00
n1474335
35382faf28 Updated all dependencies and set node version to 8.4 to fix docstrap bug in travis builds (until it is fixed) 2017-09-14 10:20:16 +00:00
n1474335
dba0b104ab Added docs to GH Pages 2017-09-13 15:21:31 +00:00
n1474335
174cabdc74 Added 'CRC-16 Checksum' operation 2017-09-12 15:50:46 +00:00
n1474335
3c52a9faab Added Keccak and Shake to 'Generate all hashes' operation 2017-09-12 15:39:48 +00:00
n1474335
73561993a7 Added SHA3, Keccak and Shake hashing algorithms 2017-09-12 15:31:51 +00:00
n1474335
6e875393d9 Merged all SHA-2 operations into one with a size argument 2017-09-12 14:48:56 +00:00
n1474335
bbd85a491b Added MD6 to 'Generate all hashes' operation 2017-09-12 14:23:36 +00:00
n1474335
a736be7ca8 Added MD6 operation. Closes #53 2017-09-12 14:20:05 +00:00
n1474335
f1fe0b944f 5.19.3 2017-09-07 10:01:31 +00:00
n1474335
c1e40dd109 Fixed typo in .travis.yml 2017-09-07 10:01:12 +00:00
n1474335
73823e3eb9 Improved domain name regex 2017-09-06 16:43:30 +00:00
n1474335
bf833991bf 5.19.2 2017-09-06 16:09:36 +00:00
n1474335
615357c977 Corrected travis config logic 2017-09-06 16:06:37 +00:00
n1474335
19f23127b9 Updated travis config to create a new branch when building a tagged release 2017-09-06 16:04:11 +00:00
n1474335
2ea6d9437f 5.19.1 2017-09-06 10:28:41 +00:00
n1474335
833b6d67a6 Updated package-lock.json to fix tagged builds 2017-09-06 10:28:35 +00:00
n1474335
04c1adc5f0 5.19.0 2017-09-05 17:59:16 +00:00
n1474335
8e8f6a0284 Merge branch 'feature-otp' 2017-09-05 17:59:02 +00:00
n1474335
68bf1d123e Added 'Generate HOTP' and 'Generate TOTP' operations 2017-09-05 17:53:54 +00:00
n1474335
2c03689195 5.18.1 2017-09-05 17:26:29 +00:00
n1474335
45f41b1140 Merge branch 'bug-number-input-change' 2017-09-05 17:21:49 +00:00
n1474335
8fa6f3f45c Fixed bug where clicking up and down arrows on numeric inputs would not trigger an auto-bake 2017-09-05 16:51:57 +00:00
n1474335
78a842deb7 5.18.0 2017-09-05 15:37:18 +00:00
n1474335
3657ff4f79 Merge branch 'feature-bitshift' 2017-09-05 15:37:10 +00:00
n1474335
d4d12c3db0 Whitespace is now removed from hex and binary even when the delimiter is set to 'None' 2017-09-05 14:30:06 +00:00
n1474335
1b628ac213 Added 'Bit shift left' and 'Bit shift right' operations 2017-09-05 14:26:09 +00:00
n1474335
5fcc259efb Merge branch 'Synchro-master' 2017-09-04 15:22:17 +00:00
Marcus Bointon
9545205f19 Optimise PNGs 2017-09-04 12:22:12 +02:00
n1474335
cb6708c02e XOR Brute Force operation now has a variable key length 2017-08-31 00:24:24 +01:00
n1474335
4f403d4450 5.17.0 2017-08-30 15:57:58 +00:00
n1474335
f2d8f930fb Merge branch 'bwhitn-vbe' 2017-08-30 15:57:18 +00:00
n1474335
4a86340d50 Tidied up 'Microsoft Script Decoder' operation 2017-08-30 15:56:51 +00:00
bwhitn
f8e9e9ba85 added sample to description 2017-08-29 12:04:15 -07:00
bwhitn
934ed1af09 Fixed spelling errors, syntax errors, and improved the test for script decoding 2017-08-29 10:46:34 -07:00
bwhitn
0fc2a219a7 Changed the name. Small logic change. Changed from split join to regex replace. 2017-08-28 17:55:54 -04:00
bwhitn
aa5939c051 Took out logging call. My bad. 2017-08-27 20:53:53 -04:00
bwhitn
9c5f06101e Fixed the replace statements 2017-08-27 18:45:42 -04:00
bwhitn
2a7c0252a0 Fixed regex issue 2017-08-27 11:49:28 -04:00
bwhitn
414726ecd4 Fixed small syntax error 2017-08-27 09:44:26 -04:00
bwhitn
fdc8a15595 Added Decode VBE 2017-08-27 09:29:26 -04:00
n1474335
d012fd3a65 5.16.3 2017-08-25 11:53:08 +00:00
n1474335
a5ed824674 Merge branch 'bug-substitute' 2017-08-25 11:50:07 +00:00
n1474335
d6705c682f Fixed bug in Substitute where commas would not encode correctly 2017-08-25 11:44:31 +00:00
n1474335
d68523a54e Added status message mechanism for the Worker to report to the app 2017-08-25 01:24:12 +01:00
n1474335
6af82680f1 Fixed tests and Node version to work with modules 2017-08-25 00:44:22 +01:00
n1474335
a4aee761c2 Added module and threading support for the inline version. 2017-08-25 00:25:49 +01:00
n1474335
33b606d48f 5.16.2 2017-08-21 15:25:41 +00:00
n1474335
cc44be7ef9 Fixed recipe saving 2017-08-21 15:25:35 +00:00
n1474335
e0eb972a54 Operations with no arguments can now be added to the recipe without causing errors 2017-08-18 16:12:49 +00:00
n1474335
19c54a99cd Improved web app title construction 2017-08-18 16:01:55 +00:00
n1474335
9d60ec22ee Updated links in index.html 2017-08-18 15:55:04 +01:00
n1474335
cb4eeccfd3 Updated links in README 2017-08-18 15:47:38 +01:00
n1474335
62e50caa99 5.16.1 2017-08-16 14:11:55 +00:00
n1474335
0192566d19 Improved recipe config generation for complex objects. Fixes #180 2017-08-16 14:11:50 +00:00
n1474335
8a8b70f2ab 5.16.0 2017-08-16 13:01:50 +00:00
n1474335
af311001cf Merge branch 'feature-pretty-recipe-format' 2017-08-16 13:01:08 +00:00
n1474335
040229418e 5.15.0 2017-08-15 17:32:31 +00:00
n1474335
c259963542 Fixed string escape test configs 2017-08-15 17:32:21 +00:00
n1474335
ca75f7fa0b Merge branch 'artemisbot-features/string_escape_unescape' 2017-08-15 17:30:12 +00:00
n1474335
4b22a409e7 Tidied up string escape operations 2017-08-15 17:29:48 +00:00
n1474335
55806db00f Merge branch 'features/string_escape_unescape' of https://github.com/artemisbot/CyberChef into artemisbot-features/string_escape_unescape 2017-08-15 17:16:39 +00:00
n1474335
83c757ebd4 Lint 2017-08-15 17:12:09 +00:00
n1474335
a19b02aa8c Updated URL regexes to match more unescaped special characters 2017-08-15 16:44:45 +00:00
n1474335
cf1ba60a10 Added new 'pretty' recipe format to make URLs more readable 2017-08-15 16:26:42 +00:00
n1474335
2a4c9afdf2 5.14.0 2017-08-10 12:42:00 +00:00
n1474335
ab76933158 Merge branch 'feature-themes' 2017-08-10 12:41:18 +00:00
n1474335
d4d7bcab7a Added GeoCities theme 2017-08-10 12:35:30 +00:00
n1474335
a61cf6a68a Added module framework 2017-08-09 20:09:23 +01:00
n1474335
146a307b59 5.13.1 2017-08-08 14:47:55 +00:00
n1474335
875946b4e8 Modified babel to target Node 6.5 2017-08-08 14:47:48 +00:00
n1474335
81f2a460ed Added apploaded event to signify when the app has completed loading 2017-08-08 13:08:06 +00:00
Matt C
6698a2ac13 Added tests + fixes for PR
- actually removed prev func
- shuffled some stuff around
2017-08-07 16:08:50 +01:00
Matt C
9161cc693d Removes need for runParseEscapedString
- Fixes examples
- Actually makes it work
2017-08-04 15:54:00 +01:00
n1474335
be689e293d Removed dev commands from PublicKey.js 2017-08-04 14:44:12 +00:00
n1474335
53c22d5e29 5.13.0 2017-08-04 14:00:24 +00:00
n1474335
5c0c80829e Merge branch 'artemisbot-features/jpath' 2017-08-04 14:00:05 +00:00
n1474335
55aedfe901 Jsonpath lib now imported from npm with workaround instead of serving locally. 2017-08-04 13:59:32 +00:00
n1474335
4b87d66131 Merge branch 'features/jpath' of https://github.com/artemisbot/CyberChef into artemisbot-features/jpath 2017-08-04 13:36:03 +00:00
n1474335
fc9497f067 Updated dependencies 2017-08-04 13:35:52 +00:00
Matt C
3186335f47 Merge Vel0z/string_escaping_unescaping
Updated to new project format
2017-08-04 11:50:45 +01:00
Matt C
31bfd8664a Added JSONPath tests & changed lib 2017-08-03 14:50:16 +01:00
n1474335
ab1c9e27dc Added more loading messages 2017-08-03 10:57:54 +00:00
n1474335
c07cc913c8 5.12.4 2017-08-01 19:27:24 +00:00
n1474335
b8fb5493c5 Merge branch 'artemisbot-bug/text-overflow' 2017-08-01 19:25:04 +00:00
n1474335
e8e5eb9c53 Fixed some edge cases for popover triggering 2017-08-01 19:23:30 +00:00
n1474335
a15034b03e Merge branch 'bug/text-overflow' of https://github.com/artemisbot/CyberChef into artemisbot-bug/text-overflow 2017-08-01 15:40:31 +00:00
n1474335
1435acdc28 5.12.3 2017-08-01 14:42:52 +00:00
n1474335
37f164f11c Merge branch 'master' of github.com:gchq/CyberChef 2017-08-01 14:42:17 +00:00
n1474335
7a2f071269 Dependencies in the node version are now kept external in the webpack build 2017-08-01 14:42:09 +00:00
n1474335
9f19afc943 Removed auto-bake threshold as long bakes can now be cancelled manually 2017-07-30 12:51:21 +01:00
n1474335
6742bef289 Separated out Diff and Windows Filetime operations into their own namespaces as they rely on libraries not used by the rest of the operations in their group 2017-07-30 12:36:50 +01:00
Matt C
9ee0964d0e Fixed hover issue - now allows scrolling 2017-07-29 00:45:41 +01:00
Matt C
33ecbfa95b Fixed arrow issue 2017-07-28 21:47:47 +01:00
n1474335
e977a1006c Merge branch 'master' into feature-threading 2017-07-28 16:55:07 +01:00
n1474335
559c13a003 5.12.2 2017-07-28 16:44:48 +01:00
n1474335
8f00345430 Merge branch 'seo-structured-data' 2017-07-28 16:44:28 +01:00
n1474335
f1ebab0c2d Added the ability to cancel bakes 2017-07-28 16:38:53 +01:00
n1474335
98884d851a Added staleness indicator to the output 2017-07-28 15:43:23 +01:00
n1474335
78d0369e71 Added loader for long bakes and improved initial loading sequence 2017-07-28 14:43:44 +01:00
n1474335
7a56af8ffa Page title changes to reflect recipe 2017-07-27 15:33:24 +00:00
n1474335
ed2bfbd27c Added structured data to help search engines 2017-07-27 15:33:01 +00:00
Matt C
e0905255ba Forgot about package-lock.json
oops
2017-07-25 16:36:01 +01:00
Matt C
de80db73f2 Adds initial JPath functionality 2017-07-25 16:27:59 +01:00
Matt C
90ed62add2 Fixes gchq/CyberChef#137
Changes data-trigger to focus so scrolling works and sets max height.
2017-07-25 11:49:23 +01:00
n1474335
d46e279933 Added link to 'Last build' notice showing commits since last release 2017-07-24 16:38:38 +00:00
n1474335
77e074efc2 5.12.1 2017-07-24 15:27:14 +00:00
n1474335
bd000d2d2c Merge branch 'lib-update' 2017-07-24 15:26:12 +00:00
n1474335
5f1c88104d Introduced key-spacing eslint rule 2017-07-24 14:55:48 +00:00
n1474335
a61df0832f Updated dependencies and linted 2017-07-24 13:49:16 +00:00
n1474335
58361e58f8 5.12.0 2017-07-19 16:03:24 +00:00
n1474335
96b4361b31 Merge branch 'feature-bcd' 2017-07-19 15:55:36 +00:00
n1474335
c773edceb9 Added BCD operations with tests 2017-07-19 15:29:37 +00:00
n1474335
bcaef8ba54 5.11.7 2017-07-18 16:17:59 +00:00
n1474335
c7d0b0ccc5 Merge branch 'feature-xor-brute-scheme' 2017-07-18 16:17:13 +00:00
n1474335
38792a0f02 Added differential schemes to 'XOR Brute Force' operation 2017-07-18 16:09:22 +00:00
n1474335
cda557e1b9 Removed sessionStorage as it is no longer used and marginally affects performance 2017-07-18 14:28:51 +00:00
n1474335
8d09f7c7eb 5.11.6 2017-07-17 13:31:37 +00:00
n1474335
4845a56435 Fixed Diff test for new higlighting class 2017-07-17 13:31:28 +00:00
n1474335
f164dcdd70 Fixed Diff highlighting classes 2017-07-17 13:19:08 +00:00
n1474335
cc3aad17e1 OperationConfig now loaded into App via value-loader so that operation code is only included in the worker 2017-07-16 23:33:47 +01:00
n1474335
6c8da6b070 Added ellipsis to overflowing args 2017-07-13 15:11:21 +00:00
n1474335
09df753ca9 5.11.5 2017-07-12 14:34:50 +00:00
n1474335
72ec9df1b1 Fixed option naming conventions 2017-07-12 14:34:45 +00:00
n1474335
086d5272ac 5.11.4 2017-07-12 12:49:22 +00:00
n1474335
2555de7712 Fixed bug in firefox where recipes containing an = character would not load from the URL 2017-07-12 12:49:10 +00:00
n1474335
645e540c66 5.11.3 2017-07-10 11:49:48 +00:00
n1474335
d16e1a4451 Fixed bug in 'Show Base64 offsets' where highlights did not show 2017-07-10 11:49:41 +00:00
n1474335
7c5dd2bd78 5.11.2 2017-07-07 13:30:27 +00:00
n1474335
2f0121f0e4 Merge branch 'bwhitn-unzipmod' 2017-07-07 13:28:21 +00:00
n1474335
7e310a8de7 Moved file switch listener to correct block 2017-07-07 13:27:47 +00:00
n1474335
c460c2bf6b Replaced hexToByteArray with fromHex and byteArrayToHex with toHex. Switched displayFilesAsHTML operation to use template strings and introduced markup formatting method. 2017-07-07 13:23:58 +00:00
n1474335
760ab688b2 Create ChefWorker and move bake process into it 2017-07-05 00:14:47 +01:00
n1474335
ff78c72d54 Configured webpack-dev-server 2017-07-03 23:15:57 +01:00
n1474335
2400de337b Merge branch 'unzipmod' of https://github.com/bwhitn/CyberChef into bwhitn-unzipmod 2017-07-03 15:28:12 +00:00
n1474335
85d41085de 5.11.1 2017-07-03 15:25:52 +00:00
n1474335
48d45d026e Merge branch 'bwhitn-filetimemod' 2017-07-03 15:25:39 +00:00
n1474335
183c57643b Tidied up changes to filetime operations and brought tests up to date 2017-07-03 15:25:14 +00:00
n1474335
e7cea889ab Merge branch 'filetimemod' of https://github.com/bwhitn/CyberChef into bwhitn-filetimemod 2017-07-03 15:19:42 +00:00
n1474335
61c799447b Improved banner CSS 2017-07-03 15:18:47 +00:00
bwhitn
ad25daf206 Allow hex and decimal format for Windows Filetime format as those are the formats they are typically represented in 2017-07-02 20:04:25 -04:00
bwhitn
4143bba89f This adds the ability to move the file data from Utils.displayFilesAsHTML to the input. 2017-07-01 00:40:22 -04:00
n1474335
8eb7d65b74 5.11.0 2017-06-28 19:56:07 +01:00
n1474335
51798553e1 Merge branch 'artemisbot-features/bifid' 2017-06-28 19:55:19 +01:00
n1474335
323928ff86 Tidied up Bifid operations 2017-06-28 19:54:34 +01:00
n1474335
fe3aeabd0a Merge branch 'features/bifid' of https://github.com/artemisbot/CyberChef into artemisbot-features/bifid 2017-06-28 19:27:42 +01:00
n1474335
ec7a55dba6 5.10.7 2017-06-27 14:13:30 +00:00
n1474335
b4fe708d70 Merge branch 'bug-display-as-html' 2017-06-27 14:13:24 +00:00
n1474335
c3469bd545 Correctly escape filenames in displayFilesAsHTML 2017-06-27 14:04:30 +00:00
n1474335
92018b761d 5.10.6 2017-06-26 21:54:15 +01:00
n1474335
bb45ff0515 Merge branch 'bug-preloader' 2017-06-26 21:53:21 +01:00
n1474335
df1405e998 Fixed mildly infuriating bug where the preloader rings overlap 2017-06-26 21:47:57 +01:00
n1474335
62ec018bb2 Update README.md 2017-06-26 16:35:51 +01:00
n1474335
14b7c4bf23 Improved support for different alphabets in 'Substitute' operation 2017-06-23 13:21:19 +00:00
n1474335
5c774a3ce2 Updated to allow delimiter to be set 2017-06-23 12:18:08 +00:00
Matt C
246480daef Fixed styling errors 2017-06-22 17:13:31 +01:00
Matt C
91c6f682e7 Added Bifid Cipher Encode & Decode
Bifid Cipher + Tests
2017-06-21 22:28:17 +01:00
n1474335
a417a6469c Added package-lock.json 2017-06-20 14:45:20 +00:00
n1474335
2821bdd52b 5.10.5 2017-06-19 16:50:07 +00:00
n1474335
d37300be39 Merge branch 'bug-astral' 2017-06-19 16:49:17 +00:00
n1474335
15b83072bb Added support for astral characters to charcode ops 2017-06-19 15:40:36 +00:00
n1474335
6d2e2259db 5.10.4 2017-06-16 15:44:42 +00:00
n1474335
5b70614212 Merge branch 'tidy-links' 2017-06-16 15:38:37 +00:00
n1474335
4363da534d Updated links to new hash variant and cleaned up About and Option panes 2017-06-16 15:36:42 +00:00
n1474335
685f7a4f00 Create CODE_OF_CONDUCT.md 2017-06-16 15:32:55 +01:00
n1474335
6e26f25418 5.10.3 2017-06-16 11:26:03 +00:00
n1474335
d8051ce9a2 Merge branch 'feature-hashurls' 2017-06-16 11:23:16 +00:00
n1474335
00e7d8a390 The recipe and input are now stored in the hash part of the URL 2017-06-16 11:04:35 +00:00
bwhitn
213ec028b8 Merge pull request #2 from gchq/master
Current
2017-06-15 19:43:22 -04:00
n1474335
61951e76ac 5.10.2 2017-06-15 14:25:03 +00:00
n1474335
47cf763b3f Merge branch 'bug-findreplace' 2017-06-15 14:23:35 +00:00
n1474335
04aac03d6e Fixed global matching for simple strings in 'Find / Replace' operation. Closes #25. 2017-06-15 14:21:30 +00:00
n1474335
3faef2c9c9 5.10.1 2017-06-13 16:31:00 +00:00
n1474335
eda17d1671 Added mixed content note to 'HTTP request' error message. 2017-06-13 16:30:55 +00:00
n1474335
fbd6ead6b7 5.10.0 2017-06-13 15:34:36 +00:00
n1474335
bf2950abdd Merge branch 'bwhitn-master' 2017-06-13 15:33:59 +00:00
n1474335
3eacc325a3 Improved descriptions for timestamp operations. 2017-06-13 15:33:37 +00:00
n1474335
15decd9cd9 Merge branch 'master' of https://github.com/bwhitn/CyberChef into bwhitn-master 2017-06-13 14:12:20 +00:00
Bwhit1
c0142adba9 changed error to conform with the rest of the functions and simplified repetative math. 2017-06-12 15:39:54 -04:00
bwhitn
ec01333c83 removed decimal.min.js 2017-06-12 05:48:22 -07:00
bwhitn
8110384ea2 Fixed the names under the DateTime Category 2017-06-12 05:33:24 -07:00
bwhitn
0c2c0d7b8b Added DateTime test, added filetime functions to Catagories, and removed unused variables. 2017-06-12 05:28:41 -07:00
Bwhit1
ae38bb0927 Completion of simple filetime conversion 2017-06-11 20:05:30 -04:00
Bwhit1
003e076b00 modifications to FILETIME 2017-06-10 23:42:02 -04:00
n1474335
2c2a0eb7d9 5.9.2 2017-06-09 15:36:53 +00:00
n1474335
e7f5b17184 Manual bake now triggers when recipes are loaded from the URL. Fixes #93. 2017-06-09 15:36:15 +00:00
n1474335
69e12b1067 5.9.1 2017-06-09 15:21:45 +00:00
n1474335
fef446687a Loading messages won't repeat as often and cycle more slowly 2017-06-09 15:21:39 +00:00
n1474335
3affce8f98 5.9.0 2017-06-09 14:54:27 +00:00
n1474335
0b91468edc Merge branch 'tlwr-feature-http-request' 2017-06-09 14:53:39 +00:00
n1474335
127364e8a4 Added error handling for non-CORS requests. 2017-06-09 14:53:15 +00:00
bwhitn
a144f65dcf Create DateTime.js 2017-06-08 08:28:21 -07:00
bwhitn
96ec3a869b Use decimal.min.js and add the file. 2017-06-08 08:19:26 -07:00
n1474335
52426bc1a4 Merge branch 'master' into feature-http-request 2017-06-08 16:08:20 +01:00
n1474335
cbab995c6d Added error handling and CORS support 2017-06-08 15:03:55 +00:00
bwhitn
d27fa43120 Add conversions for from/to Windows Filetime to UNIX Epoch. Decimal.js is used to prevent rounding errors during conversion. 2017-06-08 07:23:11 -07:00
n1474335
45a9da5b30 5.8.0 2017-06-08 11:10:35 +00:00
n1474335
aed22aebb2 Merge branch 'davejm-feature-remove-exif' 2017-06-08 11:09:53 +00:00
n1474335
369d213da5 Tidying 'Remove EXIF' 2017-06-08 11:09:31 +00:00
n1474335
e92775eec2 Merge branch 'feature-remove-exif' of https://github.com/davejm/CyberChef into davejm-feature-remove-exif 2017-06-08 10:56:06 +00:00
bwhitn
2c0f48f4e5 Merge pull request #1 from gchq/master
update
2017-06-07 23:55:41 -04:00
toby
a5f1c430a3 Add "HTTP request" operation 2017-06-07 22:46:05 -04:00
David Moodie
e4a91b5397 Use functions from utils 2017-06-07 21:41:02 +01:00
David Moodie
cbcd45cd70 Do nothing if input is empty for removeEXIF operation 2017-06-07 20:01:45 +01:00
David Moodie
0968912954 Only reference JPEGs in removeEXIF description to be more clear 2017-06-07 19:51:09 +01:00
David Moodie
3bfe22c0f7 Remove piexifjs dep and extract removeEXIF to lib module 2017-06-07 19:49:44 +01:00
David Moodie
6cf64d794f Change == to === 2017-06-04 17:23:53 +01:00
David Moodie
6741ba0783 Add remove EXIF operation 2017-06-04 17:08:39 +01:00
n1474335
f1e7bc3363 5.7.3 2017-06-01 10:24:38 +00:00
n1474335
2dbe2d044e Merge branch 'qistoph-numsort' 2017-06-01 10:23:49 +00:00
n1474335
ea3630e018 Tidied _numericSort and added @author tag 2017-06-01 10:21:10 +00:00
Chris van Marle
c6391d958d Add numeric sorting tests 2017-05-30 08:53:18 +02:00
Chris van Marle
71aa4033a4 Add numeric sorting 2017-05-29 15:24:41 +02:00
n1474335
57dcd961d5 Option selects now default to the first option if not previously set 2017-05-24 10:29:47 +01:00
n1474335
83878d6b05 Update ControlsWaiter.js 2017-05-23 16:53:37 +01:00
n1474335
9055fc72d2 Added Boostrap grid CSS 2017-05-23 13:09:55 +01:00
n1474335
fb4ab56b47 Merge pull request #141 from tlwr/master
Add repo option to Travis
2017-05-21 19:36:12 +01:00
toby
51e195ed17 Add repo option to Travis 2017-05-20 23:56:58 -04:00
n1474335
9947c574d2 5.7.2 2017-05-20 13:49:29 +01:00
n1474335
693abdacf6 Merge branch 'tlwr-master' 2017-05-20 13:49:17 +01:00
toby
dd3b42fb53 Fix bug with UTF16LE in Encode/Decode ops 2017-05-19 16:11:08 -04:00
n1474335
8e72d7d0d6 5.7.1 2017-05-19 16:28:43 +00:00
n1474335
a6317212d9 Merge branch 'feature-loading-messages' 2017-05-19 16:25:30 +00:00
n1474335
347adf688a Added loading messages to preloader 2017-05-19 16:23:52 +00:00
n1474335
3ee67927a5 Moved bootstrap from devDependencies to dependencies and removed bootstrap-sass. 2017-05-19 14:17:34 +00:00
n1474335
7ecd36efcf Cleaned up extraneous character encoding functions in Utils.js 2017-05-19 14:05:57 +00:00
n1474335
714ce8a8a3 5.7.0 2017-05-19 13:45:30 +00:00
n1474335
7b18a2f46f Merge branch 'tlwr-feature-ebcdic' 2017-05-19 13:44:18 +00:00
n1474335
19103a64e5 Tidied up character encoding names 2017-05-19 13:43:55 +00:00
n1474335
a13f1d27e2 Merge branch 'feature-ebcdic' of https://github.com/tlwr/CyberChef into tlwr-feature-ebcdic 2017-05-19 13:16:54 +00:00
n1474335
9a6e4b1e85 5.6.0 2017-05-19 13:12:04 +00:00
n1474335
67b78fc230 Merge branch 'feature-render-images' 2017-05-19 13:11:12 +00:00
n1474335
5e79187176 Added ico and removed tiff from supported image types 2017-05-19 12:58:42 +00:00
n1474335
491a82cd67 Added 'Render Image' operation 2017-05-19 11:15:48 +00:00
n1474335
87c2ec678f 5.5.0 2017-05-19 00:22:50 +01:00
n1474335
70135ab3ea Really fix compile message formatting 2017-05-19 00:22:41 +01:00
n1474335
ad18d84f14 Fix option defaults and compile message formatting 2017-05-19 00:10:36 +01:00
n1474335
77e47e3fa4 Merge branch 'feature-sass' 2017-05-18 23:51:29 +01:00
n1474335
04432385b3 Merge branch 'master' into feature-sass 2017-05-18 23:50:43 +01:00
n1474335
cbdc24d869 Travis CI now adds a compile message 2017-05-18 23:46:03 +01:00
n1474335
2b3e471f96 Added version number to About/Support pane 2017-05-18 23:40:53 +01:00
n1474335
488d54493a Added modern browser warning for theme support 2017-05-18 23:29:04 +01:00
n1474335
a418f63a44 Added postcss-loader to provide fallbacks for older browsers 2017-05-18 23:24:24 +01:00
toby
0e285151f3 Add codepage numbers to CharEnc IO_FORMAT 2017-05-17 18:22:04 -04:00
n1474335
e4ad7768d5 Finalised the dark theme 2017-05-17 23:02:52 +01:00
toby
f800fab1a3 Add author tags to Encode text & Decode text ops 2017-05-17 17:31:28 -04:00
toby
dc61aeeeb8 Improve format of en/decoding text descriptions 2017-05-17 17:18:08 -04:00
toby
4b1d0fd011 Merge branch 'feature-ebcdic' of github.com:tlwr/CyberChef into feature-ebcdic 2017-05-17 11:30:20 -04:00
toby
2b7ba594fc Split "Text encoding" op into two ops
This commit splits "Text encoding" into two operations:
+ Encode text `string -> byteArray`
+ Decode text `byteArray -> string`

Base64 and Hex support are removed "Encode text" and "Decode text" as
they have their own operations.

Encode and decode operations now have support for the following
encodings:
+ IBM EBCDIC US-Canada
+ IBM EBCDIC International
+ Windows-874 Thai
+ Japanese Shift-JIS
+ Simplified Chinese GBK
+ Korean
+ Traditional Chinese Big5
+ UTF-16, little endian
+ UTF-16, big endian
+ Windows-1250 Central European
+ Windows-1251 Cyrillic
+ Windows-1252 Latin
+ Windows-1253 Greek
+ Windows-1254 Turkish
+ Windows-1255 Hebrew
+ Windows-1256 Arabic
+ Windows-1257 Baltic
+ Windows-1258 Vietnam
+ US-ASCII
+ Russian Cyrillic KOI8-R
+ Simplified Chinese GB2312
+ KOI8-U Ukrainian Cyrillic
+ ISO-8859-1 Latin 1 (Western European)
+ ISO-8859-2 Latin 2 (Central European)
+ ISO-8859-3 Latin 3
+ ISO-8859-4 Baltic
+ ISO-8859-5 Cyrillic
+ ISO-8859-6 Arabic
+ ISO-8859-7 Greek
+ ISO-8859-8 Hebrew
+ ISO-8859-9 Turkish
+ ISO-8859-10 Latin 6
+ ISO-8859-11 Latin (Thai)
+ ISO-8859-13 Latin 7 (Estonian)
+ ISO-8859-14 Latin 8 (Celtic)
+ ISO-8859-15 Latin 9
+ ISO-8859-16 Latin 10
+ ISO-2022 JIS Japanese
+ EUC Japanese
+ EUC Korean
+ Simplified Chinese GB18030
+ UTF-7
+ UTF-8
2017-05-17 11:17:11 -04:00
n1474335
d87b14af13 5.4.1 2017-05-17 14:45:19 +00:00
n1474335
83623d23cf Merge branch 'bug-timestamp' 2017-05-17 14:45:10 +00:00
n1474335
07fba53b73 'To UNIX Timestamp' operation now defaults to UTC instead of your local timezone. 2017-05-17 14:37:36 +00:00
n1474335
823b276ef5 Changed bootstrap-sass back to bootstrap (with less). Theme now loads before the preloader is shown. 2017-05-13 16:45:19 +01:00
n1474335
0e72d78731 5.4.0 2017-05-13 16:10:26 +01:00
n1474335
768609e357 Whitespace 2017-05-13 16:09:32 +01:00
n1474335
526a157421 Merge branch 'tlwr-feature-exif' 2017-05-13 16:08:39 +01:00
n1474335
62154309fb Newlines 2017-05-13 16:08:14 +01:00
n1474335
ad74e6c475 Merge branch 'feature-exif' of https://github.com/tlwr/CyberChef into tlwr-feature-exif 2017-05-13 15:59:48 +01:00
toby
82d28242cc Add JSDoc to the runEXIF function in Image.js 2017-05-09 15:34:18 -04:00
toby
60fddf837d Change let to const in "Extract EXIF" 2017-05-08 12:49:13 -04:00
toby
ee25df0c28 Add desc and fix error message for "Extract EXIF" 2017-05-08 12:48:43 -04:00
toby
478af40359 Fix "Extract EXIF" tests 2017-05-08 12:45:42 -04:00
Toby Lorne
6bf06a9629 Merge branch 'master' into feature-ebcdic 2017-05-07 18:21:30 -04:00
toby
3c15bd9e29 Add "{To,From} EBCDIC" operations
This adds operations
+ "To EBCDIC"
+ "From EBCDIC"

This makes use of the npm codepage package but it is not installed as a
dependency.

Instead I used the `make.sh` script to export pages 37 and 500.

To my knowledge there is no way currently to only import individual code pages
from the npm package (hence the included script).

If we were to import the package directly it increases the build size by
2.7MB.
2017-05-07 18:07:56 -04:00
toby
71796e3dbf Merge remote-tracking branch 'upstream/master' into feature-exif 2017-05-06 23:38:04 -04:00
n1474335
280f1ee2df Dark theme tweaks and improvements 2017-05-07 01:13:47 +01:00
n1474335
0dc72d8301 Merged master 2017-05-06 16:22:25 +01:00
n1474335
c43b67ea90 5.3.5 2017-05-06 16:15:29 +01:00
n1474335
244421b69e Merge branch 'graingert-prefer-const' 2017-05-06 16:14:58 +01:00
n1474335
1adedff61a Filter out source-map warnings in webpack output 2017-05-06 16:14:30 +01:00
n1474335
6abd10f9e2 Tidying codebase 2017-05-06 16:12:09 +01:00
n1474335
5efe9bd91d Merge branch 'prefer-const' of https://github.com/graingert/CyberChef into graingert-prefer-const 2017-05-06 14:11:38 +01:00
n1474335
dc7a7267c9 5.3.4 2017-05-06 14:00:41 +01:00
Thomas Grainger
b365ce3195 fix the first 100 prefer-const errors 2017-05-03 00:41:01 +01:00
Thomas Grainger
0a3233d289 move spider images to .json file 2017-05-03 00:41:01 +01:00
Thomas Grainger
15aea9e9ea auto-fix prefer-const 2017-05-03 00:41:01 +01:00
n1474335
80cdf0c014 Merge conflict 2017-05-02 23:03:28 +01:00
n1474335
d1d394eec7 Fix search-results layout bug. 2017-05-02 20:56:57 +01:00
toby
1b8a25ec88 Add "Extract EXIF" operation 2017-04-29 16:06:16 -04:00
n1474335
2e4076bb75 Added option to change the theme. 2017-04-25 00:21:38 +01:00
n1474335
d71ac2e894 Moved from Sass to CSS with custom properties, allowing for dynamic theme changes. 2017-04-24 23:59:35 +01:00
n1474335
e3f41fea9c Developing a dark theme. 2017-04-23 17:09:12 +01:00
n1474335
a4d93f23d6 Added preloader to hide elements before they are correctly rendered 2017-04-15 20:46:57 +01:00
n1474335
e0e5670d0e Styles rewritten in a modular format with themable properties stored in variables. 2017-04-13 17:59:37 +01:00
n1474335
e120422b05 Styles now imported through Sass. Less removed. 2017-04-09 14:06:59 +01:00
Dale Myers
fadd7158ed Add string escape/unescape operations
These operations are useful for taking the contents of a string, and making it
suitable for use as a stand alone string. For example, in an IDE you might see
a string which is represented as: "Say \"Hello\"". The escaped double quotes
are shown to make it clear that they do not end the string, despite the fact
that they are not truly part of the string. In order to get the raw string, you
would need to copy this, then manually remove the backslashes. The new
String_.run_unescape operation does this automatically.

The String_.run_escape is the inverse. It allows you to take a string like the
one above, and paste it between two quotes without having to manually escape
it.
2017-01-23 21:39:08 +00:00
174 changed files with 27036 additions and 4550 deletions

View File

@@ -4,9 +4,11 @@
"targets": { "targets": {
"chrome": 40, "chrome": 40,
"firefox": 35, "firefox": 35,
"edge": 14 "edge": 14,
"node": "6.5",
}, },
"modules": false "modules": false,
"useBuiltIns": true
}] }]
] ]
} }

View File

@@ -1 +1,2 @@
src/core/lib/** src/core/lib/**
src/core/config/MetaConfig.js

View File

@@ -28,11 +28,7 @@
// modify rules from base configurations // modify rules from base configurations
"no-unused-vars": ["error", { "no-unused-vars": ["error", {
"args": "none", "args": "none",
"vars": "local", "vars": "all"
// 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]"
}], }],
"no-empty": ["error", { "no-empty": ["error", {
"allowEmptyCatch": true "allowEmptyCatch": true
@@ -52,6 +48,9 @@
"no-trailing-spaces": "warn", "no-trailing-spaces": "warn",
"eol-last": "error", "eol-last": "error",
"func-call-spacing": "error", "func-call-spacing": "error",
"key-spacing": ["warn", {
"mode": "minimum"
}],
"indent": ["error", 4, { "indent": ["error", 4, {
"ArrayExpression": "first", "ArrayExpression": "first",
"SwitchCase": 1 "SwitchCase": 1
@@ -93,6 +92,10 @@
"moment": false, "moment": false,
"COMPILE_TIME": false, "COMPILE_TIME": false,
"COMPILE_MSG": false "COMPILE_MSG": false,
"PKG_VERSION": false,
"ENVIRONMENT_IS_WORKER": false,
"ENVIRONMENT_IS_NODE": false,
"ENVIRONMENT_IS_WEB": false
} }
} }

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@ docs/*
!docs/*.conf.json !docs/*.conf.json
!docs/*.ico !docs/*.ico
.vscode .vscode
src/core/config/MetaConfig.js

View File

@@ -1,6 +1,6 @@
language: node_js language: node_js
node_js: node_js:
- node - "8.4"
install: npm install install: npm install
before_script: before_script:
- npm install -g grunt - npm install -g grunt
@@ -9,7 +9,7 @@ script:
- grunt test - grunt test
- grunt docs - grunt docs
- grunt node - grunt node
- grunt prod - grunt prod --msg="$COMPILE_MSG"
before_deploy: before_deploy:
- grunt copy:ghPages - grunt copy:ghPages
deploy: deploy:
@@ -19,9 +19,10 @@ deploy:
local_dir: build/prod/ local_dir: build/prod/
target_branch: gh-pages target_branch: gh-pages
on: on:
repo: gchq/CyberChef
branch: master branch: master
- provider: releases - provider: releases
skip_cleaup: true skip_cleanup: true
api_key: api_key:
secure: "HV1WSKv4l/0Y2bKKs1iBJocBcmLj08PCRUeEM/jTwA4jqJ8EiLHWiXtER/D5sEg2iibRVKd2OQjfrmS6bo4AiwdeVgAKmv0FtS2Jw+391N8Nd5AkEANHa5Om/IpHLTL2YRAjpJTsDpY72bMUTJIwjQA3TFJkgrpOw6KYfohOcgbxLpZ4XuNJRU3VL4Hsxdv5V9aOVmfFOmMOVPQlakXy7NgtW5POp1f2WJwgcZxylkR1CjwaqMyXmSoVl46pyH3tr5+dptsQoKSGdi6sIHGA60oDotFPcm+0ifa47wZw+vapuuDi4tdNxhrHGaDMG8xiE0WFDHwQUDlk2/+W7j9SEX0H3Em7us371JXRp56EDwEcDa34VpVkC6i8HGcHK55hnxVbMZXGf3qhOFD8wY7qMbjMRvIpucrMHBi86OfkDfv0vDj2LyvIl5APj/AX50BrE0tfH1MZbH26Jkx4NdlkcxQ14GumarmUqfmVvbX/fsoA6oUuAAE9ZgRRi3KHO4wci6KUcRfdm+XOeUkaBFsL86G3EEYIvrtBTuaypdz+Cx7nd1iPZyWMx5Y1gXnVzha4nBdV4+7l9JIsFggD8QVpw2uHXQiS1KXFjOeqA3DBD8tjMB7q26Fl2fD3jkOo4BTbQ2NrRIZUu/iL+fOmMPsyMt2qulB0yaSBCfkbEq8xrUA=" secure: "HV1WSKv4l/0Y2bKKs1iBJocBcmLj08PCRUeEM/jTwA4jqJ8EiLHWiXtER/D5sEg2iibRVKd2OQjfrmS6bo4AiwdeVgAKmv0FtS2Jw+391N8Nd5AkEANHa5Om/IpHLTL2YRAjpJTsDpY72bMUTJIwjQA3TFJkgrpOw6KYfohOcgbxLpZ4XuNJRU3VL4Hsxdv5V9aOVmfFOmMOVPQlakXy7NgtW5POp1f2WJwgcZxylkR1CjwaqMyXmSoVl46pyH3tr5+dptsQoKSGdi6sIHGA60oDotFPcm+0ifa47wZw+vapuuDi4tdNxhrHGaDMG8xiE0WFDHwQUDlk2/+W7j9SEX0H3Em7us371JXRp56EDwEcDa34VpVkC6i8HGcHK55hnxVbMZXGf3qhOFD8wY7qMbjMRvIpucrMHBi86OfkDfv0vDj2LyvIl5APj/AX50BrE0tfH1MZbH26Jkx4NdlkcxQ14GumarmUqfmVvbX/fsoA6oUuAAE9ZgRRi3KHO4wci6KUcRfdm+XOeUkaBFsL86G3EEYIvrtBTuaypdz+Cx7nd1iPZyWMx5Y1gXnVzha4nBdV4+7l9JIsFggD8QVpw2uHXQiS1KXFjOeqA3DBD8tjMB7q26Fl2fD3jkOo4BTbQ2NrRIZUu/iL+fOmMPsyMt2qulB0yaSBCfkbEq8xrUA="
file: file:

46
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at oss@gchq.gov.uk. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -1,7 +1,18 @@
"use strict";
const webpack = require("webpack"); const webpack = require("webpack");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin");
const NodeExternals = require("webpack-node-externals");
const Inliner = require("web-resource-inliner"); const Inliner = require("web-resource-inliner");
const fs = require("fs");
/**
* Grunt configuration for building the app in various formats.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
module.exports = function (grunt) { module.exports = function (grunt) {
grunt.file.defaultEncoding = "utf8"; grunt.file.defaultEncoding = "utf8";
@@ -10,15 +21,15 @@ module.exports = function (grunt) {
// Tasks // Tasks
grunt.registerTask("dev", grunt.registerTask("dev",
"A persistent task which creates a development build whenever source files are modified.", "A persistent task which creates a development build whenever source files are modified.",
["clean:dev", "webpack:webDev"]); ["clean:dev", "concurrent:dev"]);
grunt.registerTask("node", grunt.registerTask("node",
"Compiles CyberChef into a single NodeJS module.", "Compiles CyberChef into a single NodeJS module.",
["clean:node", "webpack:node", "chmod:build"]); ["clean:node", "webpack:metaConf", "webpack:node", "chmod:build"]);
grunt.registerTask("test", grunt.registerTask("test",
"A task which runs all the tests in test/tests.", "A task which runs all the tests in test/tests.",
["clean:test", "webpack:tests", "execute:test"]); ["clean:test", "webpack:metaConf", "webpack:tests", "execute:test"]);
grunt.registerTask("docs", grunt.registerTask("docs",
"Compiles documentation in the /docs directory.", "Compiles documentation in the /docs directory.",
@@ -26,7 +37,7 @@ module.exports = function (grunt) {
grunt.registerTask("prod", grunt.registerTask("prod",
"Creates a production-ready build. Use the --msg flag to add a compile message.", "Creates a production-ready build. Use the --msg flag to add a compile message.",
["eslint", "clean:prod", "webpack:webProd", "inline", "chmod"]); ["eslint", "clean:prod", "webpack:metaConf", "webpack:web", "inline", "chmod"]);
grunt.registerTask("default", grunt.registerTask("default",
"Lints the code base", "Lints the code base",
@@ -34,8 +45,10 @@ module.exports = function (grunt) {
grunt.registerTask("inline", grunt.registerTask("inline",
"Compiles a production build of CyberChef into a single, portable web page.", "Compiles a production build of CyberChef into a single, portable web page.",
runInliner); ["webpack:webInline", "runInliner", "clean:inlineScripts"]);
grunt.registerTask("runInliner", runInliner);
grunt.registerTask("doc", "docs"); grunt.registerTask("doc", "docs");
grunt.registerTask("tests", "test"); grunt.registerTask("tests", "test");
grunt.registerTask("lint", "eslint"); grunt.registerTask("lint", "eslint");
@@ -51,30 +64,28 @@ module.exports = function (grunt) {
grunt.loadNpmTasks("grunt-exec"); grunt.loadNpmTasks("grunt-exec");
grunt.loadNpmTasks("grunt-execute"); grunt.loadNpmTasks("grunt-execute");
grunt.loadNpmTasks("grunt-accessibility"); grunt.loadNpmTasks("grunt-accessibility");
grunt.loadNpmTasks("grunt-concurrent");
// Project configuration // Project configuration
const compileTime = grunt.template.today("dd/mm/yyyy HH:MM:ss") + " UTC", const compileTime = grunt.template.today("UTC:dd/mm/yyyy HH:MM:ss") + " UTC",
banner = "/**\n" + pkg = grunt.file.readJSON("package.json"),
"* CyberChef - The Cyber Swiss Army Knife\n" + webpackConfig = require("./webpack.config.js"),
"*\n" + BUILD_CONSTANTS = {
"* @copyright Crown Copyright 2016\n" + COMPILE_TIME: JSON.stringify(compileTime),
"* @license Apache-2.0\n" + COMPILE_MSG: JSON.stringify(grunt.option("compile-msg") || grunt.option("msg") || ""),
"*\n" + PKG_VERSION: JSON.stringify(pkg.version),
"* Copyright 2016 Crown Copyright\n" + ENVIRONMENT_IS_WORKER: function() {
"*\n" + return typeof importScripts === "function";
'* Licensed under the Apache License, Version 2.0 (the "License");\n' + },
"* you may not use this file except in compliance with the License.\n" + ENVIRONMENT_IS_NODE: function() {
"* You may obtain a copy of the License at\n" + return typeof process === "object" && typeof require === "function";
"*\n" + },
"* http://www.apache.org/licenses/LICENSE-2.0\n" + ENVIRONMENT_IS_WEB: function() {
"*\n" + return typeof window === "object";
"* Unless required by applicable law or agreed to in writing, software\n" + }
'* distributed under the License is distributed on an "AS IS" BASIS,\n' + },
"* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + moduleEntryPoints = listEntryModules();
"* See the License for the specific language governing permissions and\n" +
"* limitations under the License.\n" +
"*/\n";
/** /**
* Compiles a production build of CyberChef into a single, portable web page. * Compiles a production build of CyberChef into a single, portable web page.
@@ -103,20 +114,36 @@ module.exports = function (grunt) {
}); });
} }
/**
* Generates an entry list for all the modules.
*/
function listEntryModules() {
const path = "./src/core/config/modules/";
let entryModules = {};
fs.readdirSync(path).forEach(file => {
if (file !== "Default.js" && file !== "OpModules.js")
entryModules[file.split(".js")[0]] = path + file;
});
return entryModules;
}
grunt.initConfig({ grunt.initConfig({
clean: { clean: {
dev: ["build/dev/*"], dev: ["build/dev/*", "src/core/config/MetaConfig.js"],
prod: ["build/prod/*"], prod: ["build/prod/*", "src/core/config/MetaConfig.js"],
test: ["build/test/*"], test: ["build/test/*", "src/core/config/MetaConfig.js"],
node: ["build/node/*"], node: ["build/node/*", "src/core/config/MetaConfig.js"],
docs: ["docs/*", "!docs/*.conf.json", "!docs/*.ico"], docs: ["docs/*", "!docs/*.conf.json", "!docs/*.ico", "!docs/*.png"],
inlineScripts: ["build/prod/scripts.js"],
}, },
eslint: { eslint: {
options: { options: {
configFile: "./.eslintrc.json" configFile: "./.eslintrc.json"
}, },
configs: ["Gruntfile.js"], configs: ["Gruntfile.js"],
core: ["src/core/**/*.js", "!src/core/lib/**/*"], core: ["src/core/**/*.js", "!src/core/lib/**/*", "!src/core/config/MetaConfig.js"],
web: ["src/web/**/*.js"], web: ["src/web/**/*.js"],
node: ["src/node/**/*.js"], node: ["src/node/**/*.js"],
tests: ["test/**/*.js"], tests: ["test/**/*.js"],
@@ -133,9 +160,16 @@ module.exports = function (grunt) {
src: [ src: [
"src/**/*.js", "src/**/*.js",
"!src/core/lib/**/*", "!src/core/lib/**/*",
"!src/core/config/MetaConfig.js"
], ],
} }
}, },
concurrent: {
options: {
logConcurrentOutput: true
},
dev: ["webpack:metaConfDev", "webpack-dev-server:start"]
},
accessibility: { accessibility: {
options: { options: {
accessibilityLevel: "WCAG2A", accessibilityLevel: "WCAG2A",
@@ -149,104 +183,47 @@ module.exports = function (grunt) {
} }
}, },
webpack: { webpack: {
options: { options: webpackConfig,
plugins: [ metaConf: {
new webpack.ProvidePlugin({ target: "node",
$: "jquery", entry: "./src/core/config/OperationConfig.js",
jQuery: "jquery",
moment: "moment-timezone"
}),
new webpack.BannerPlugin({
banner: banner,
raw: true,
entryOnly: true
}),
new webpack.DefinePlugin({
COMPILE_TIME: JSON.stringify(compileTime),
COMPILE_MSG: JSON.stringify(grunt.option("compile-msg") || grunt.option("msg") || "")
}),
new ExtractTextPlugin("styles.css"),
],
resolve: {
alias: {
jquery: "jquery/src/jquery"
}
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader?compact=false"
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: "css-loader?minimize"
})
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
use: [
{ loader: "css-loader?minimize" },
{ loader: "less-loader" }
]
})
},
{
test: /\.(ico|eot|ttf|woff|woff2)$/,
loader: "url-loader",
options: {
limit: 10000
}
},
{ // First party images are saved as files to be cached
test: /\.(png|jpg|gif|svg)$/,
exclude: /node_modules/,
loader: "file-loader",
options: {
name: "images/[name].[ext]"
}
},
{ // Third party images are inlined
test: /\.(png|jpg|gif|svg)$/,
exclude: /web\/static/,
loader: "url-loader",
options: {
limit: 10000
}
},
]
},
stats: {
children: false
}
},
webDev: {
target: "web",
entry: "./src/web/index.js",
output: { output: {
filename: "scripts.js", filename: "MetaConfig.js",
path: __dirname + "/build/dev" path: __dirname + "/src/core/config/",
library: "MetaConfig",
libraryTarget: "commonjs2",
libraryExport: "default"
}, },
plugins: [ externals: [NodeExternals()],
new HtmlWebpackPlugin({ },
filename: "index.html", metaConfDev: {
template: "./src/web/html/index.html", target: "node",
compileTime: compileTime entry: "./src/core/config/OperationConfig.js",
}) output: {
], filename: "MetaConfig.js",
path: __dirname + "/src/core/config/",
library: "MetaConfig",
libraryTarget: "commonjs2",
libraryExport: "default"
},
externals: [NodeExternals()],
watch: true watch: true
}, },
webProd: { web: {
target: "web", target: "web",
entry: "./src/web/index.js", entry: Object.assign({
main: "./src/web/index.js"
}, moduleEntryPoints),
output: { output: {
filename: "scripts.js",
path: __dirname + "/build/prod" path: __dirname + "/build/prod"
}, },
resolve: {
alias: {
"./config/modules/OpModules.js": "./config/modules/Default.js"
}
},
plugins: [ plugins: [
new webpack.DefinePlugin(BUILD_CONSTANTS),
new webpack.optimize.UglifyJsPlugin({ new webpack.optimize.UglifyJsPlugin({
compress: { compress: {
"screw_ie8": true, "screw_ie8": true,
@@ -256,10 +233,12 @@ module.exports = function (grunt) {
}, },
comments: false, comments: false,
}), }),
new HtmlWebpackPlugin({ // Main version new HtmlWebpackPlugin({
filename: "index.html", filename: "index.html",
template: "./src/web/html/index.html", template: "./src/web/html/index.html",
chunks: ["main"],
compileTime: compileTime, compileTime: compileTime,
version: pkg.version,
minify: { minify: {
removeComments: true, removeComments: true,
collapseWhitespace: true, collapseWhitespace: true,
@@ -267,10 +246,31 @@ module.exports = function (grunt) {
minifyCSS: true minifyCSS: true
} }
}), }),
new HtmlWebpackPlugin({ // Inline version ]
},
webInline: {
target: "web",
entry: "./src/web/index.js",
output: {
filename: "scripts.js",
path: __dirname + "/build/prod"
},
plugins: [
new webpack.DefinePlugin(BUILD_CONSTANTS),
new webpack.optimize.UglifyJsPlugin({
compress: {
"screw_ie8": true,
"dead_code": true,
"unused": true,
"warnings": false
},
comments: false,
}),
new HtmlWebpackPlugin({
filename: "cyberchef.htm", filename: "cyberchef.htm",
template: "./src/web/html/index.html", template: "./src/web/html/index.html",
compileTime: compileTime, compileTime: compileTime,
version: pkg.version,
inline: true, inline: true,
minify: { minify: {
removeComments: true, removeComments: true,
@@ -284,34 +284,95 @@ module.exports = function (grunt) {
tests: { tests: {
target: "node", target: "node",
entry: "./test/index.js", entry: "./test/index.js",
externals: [NodeExternals()],
output: { output: {
filename: "index.js", filename: "index.js",
path: __dirname + "/build/test" path: __dirname + "/build/test"
} },
plugins: [
new webpack.DefinePlugin(BUILD_CONSTANTS)
]
}, },
node: { node: {
target: "node", target: "node",
entry: "./src/node/index.js", entry: "./src/node/index.js",
externals: [NodeExternals()],
output: { output: {
filename: "CyberChef.js", filename: "CyberChef.js",
path: __dirname + "/build/node", path: __dirname + "/build/node",
library: "CyberChef", library: "CyberChef",
libraryTarget: "commonjs2" libraryTarget: "commonjs2"
},
plugins: [
new webpack.DefinePlugin(BUILD_CONSTANTS)
]
}
},
"webpack-dev-server": {
options: {
webpack: webpackConfig,
host: "0.0.0.0",
disableHostCheck: true,
overlay: true,
inline: false,
clientLogLevel: "error",
stats: {
children: false,
chunks: false,
modules: false,
warningsFilter: /source-map/,
}
},
start: {
webpack: {
target: "web",
entry: Object.assign({
main: "./src/web/index.js"
}, moduleEntryPoints),
resolve: {
alias: {
"./config/modules/OpModules.js": "./config/modules/Default.js"
}
},
plugins: [
new webpack.DefinePlugin(BUILD_CONSTANTS),
new HtmlWebpackPlugin({
filename: "index.html",
template: "./src/web/html/index.html",
chunks: ["main"],
compileTime: compileTime,
version: pkg.version,
})
]
} }
} }
}, },
copy: { copy: {
ghPages: { ghPages: {
options: { options: {
process: function (content) { process: function (content, srcpath) {
// Add Google Analytics code to index.html // Add Google Analytics code to index.html
content = content.replace("</body></html>", if (srcpath.indexOf("index.html") >= 0) {
grunt.file.read("src/web/static/ga.html") + "</body></html>"); content = content.replace("</body></html>",
return grunt.template.process(content); grunt.file.read("src/web/static/ga.html") + "</body></html>");
} return grunt.template.process(content, srcpath);
} else {
return content;
}
},
noProcess: ["**", "!**/*.html"]
}, },
src: "build/prod/index.html", files: [
dest: "build/prod/index.html" {
src: "build/prod/index.html",
dest: "build/prod/index.html"
},
{
expand: true,
src: "docs/**",
dest: "build/prod/"
}
]
} }
}, },
chmod: { chmod: {

View File

@@ -1,14 +1,17 @@
# CyberChef # CyberChef
[![Build Status](https://travis-ci.org/gchq/CyberChef.svg?branch=master)](https://travis-ci.org/gchq/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) [![dependencies Status](https://david-dm.org/gchq/CyberChef/status.svg)](https://david-dm.org/gchq/CyberChef)
![](https://reposs.herokuapp.com/?path=gchq/CyberChef&color=brightgreen) [![devDependencies Status](https://david-dm.org/gchq/CyberChef/dev-status.svg)](https://david-dm.org/gchq/CyberChef?type=dev)
[![npm](http://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)
#### *The Cyber Swiss Army Knife* #### *The Cyber Swiss Army Knife*
CyberChef is a simple, intuitive web app for carrying out all manner of "cyber" operations within a web browser. These operations include creating hexdumps, simple encoding like XOR or Base64, more complex encryption like AES, DES and Blowfish, data compression and decompression, calculating hashes and checksums, IPv6 and X.509 parsing, and much more. CyberChef is a simple, intuitive web app for carrying out all manner of "cyber" operations within a web browser. These operations include simple encoding like XOR or Base64, more complex encryption like AES, DES and Blowfish, creating binary and hexdumps, compression and decompression of data, calculating hashes and checksums, IPv6 and X.509 parsing, changing character encodings, and much more.
The tool is designed to enable both technical and non-technical analysts to manipulate data in complex ways without having to deal with complex tools or algorithms. It was conceived, designed, built and incrementally improved by an analyst in their 10% innovation time over several years. Every effort has been made to structure the code in a readable and extendable format, however it should be noted that the analyst is not a professional developer and the code has not been peer-reviewed for compliance with a formal specification. The tool is designed to enable both technical and non-technical analysts to manipulate data in complex ways without having to deal with complex tools or algorithms. It was conceived, designed, built and incrementally improved by an analyst in their 10% innovation time over several years. Every effort has been made to structure the code in a readable and extendable format, however it should be noted that the analyst is not a professional developer.
## Live demo ## Live demo
@@ -24,9 +27,9 @@ Cryptographic operations in CyberChef should not be relied upon to provide secur
There are four main areas in CyberChef: 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 data you want to operate on.
2. The **output** box in the bottom right, where the outcome of the specified processing will be displayed. 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. 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 drag the operations that you want to use and specify arguments and options. 4. The **recipe** area in the middle, where you can drag the operations that you want to use and specify arguments and options.
You can use as many operations as you like in simple or complex ways. Some examples are as follows: You can use as many operations as you like in simple or complex ways. Some examples are as follows:
@@ -34,8 +37,10 @@ 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] - [Convert a date and time to a different time zone][3]
- [Parse a Teredo IPv6 address][4] - [Parse a Teredo IPv6 address][4]
- [Convert data from a hexdump, then decompress][5] - [Convert data from a hexdump, then decompress][5]
- [Display multiple timestamps as full dates][6] - [Decrypt and disassemble shellcode][6]
- [Carry out different operations on data of different types][7] - [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]
## Features ## Features
@@ -56,7 +61,7 @@ You can use as many operations as you like in simple or complex ways. Some examp
- Search - 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. - 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 - 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][10]).
- Save to file and load from file - 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 (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).
- CyberChef is entirely client-side - CyberChef is entirely client-side
@@ -66,12 +71,16 @@ You can use as many operations as you like in simple or complex ways. Some examp
## Browser support ## Browser support
CyberChef is built to support Google Chrome 40+, Mozilla Firefox 35+ and Microsoft Edge 14+. CyberChef is built to support
- Google Chrome 40+
- Mozilla Firefox 35+
- Microsoft Edge 14+
## Contributing ## Contributing
An installation walkthrough, how-to guides for adding new operations, descriptions of the repository structure, available data types and coding conventions can all be found in the project [wiki pages](https://github.com/gchq/CyberChef/wiki). An installation walkthrough, how-to guides for adding new operations and themes, descriptions of the repository structure, available data types and coding conventions can all be found in the project [wiki pages](https://github.com/gchq/CyberChef/wiki).
- Sign the [GCHQ Contributor Licence Agreement](https://github.com/gchq/Gaffer/wiki/GCHQ-OSS-Contributor-License-Agreement-V1.0) - Sign the [GCHQ Contributor Licence Agreement](https://github.com/gchq/Gaffer/wiki/GCHQ-OSS-Contributor-License-Agreement-V1.0)
- Push your changes to your fork. - Push your changes to your fork.
@@ -84,10 +93,12 @@ CyberChef is released under the [Apache 2.0 Licence](https://www.apache.org/lice
[1]: https://gchq.github.io/CyberChef [1]: https://gchq.github.io/CyberChef
[2]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22From%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%2Ctrue%5D%7D%5D&input=VTI4Z2JHOXVaeUJoYm1RZ2RHaGhibXR6SUdadmNpQmhiR3dnZEdobElHWnBjMmd1 [2]: https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true)&input=VTI4Z2JHOXVaeUJoYm1RZ2RHaGhibXR6SUdadmNpQmhiR3dnZEdobElHWnBjMmd1
[3]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22Translate%20DateTime%20Format%22%2C%22args%22%3A%5B%22Standard%20date%20and%20time%22%2C%22DD%2FMM%2FYYYY%20HH%3Amm%3Ass%22%2C%22UTC%22%2C%22dddd%20Do%20MMMM%20YYYY%20HH%3Amm%3Ass%20Z%20z%22%2C%22Australia%2FQueensland%22%5D%7D%5D&input=MTUvMDYvMjAxNSAyMDo0NTowMA [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=%5B%7B%22op%22%3A%22Parse%20IPv6%20address%22%2C%22args%22%3A%5B%5D%7D%5D&input=MjAwMTowMDAwOjQxMzY6ZTM3ODo4MDAwOjYzYmY6M2ZmZjpmZGQy [4]: https://gchq.github.io/CyberChef/#recipe=Parse_IPv6_address()&input=MjAwMTowMDAwOjQxMzY6ZTM3ODo4MDAwOjYzYmY6M2ZmZjpmZGQy
[5]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22From%20Hexdump%22%2C%22args%22%3A%5B%5D%7D%2C%7B%22op%22%3A%22Gunzip%22%2C%22args%22%3A%5B%5D%7D%5D&input=MDAwMDAwMDAgIDFmIDhiIDA4IDAwIDEyIGJjIGYzIDU3IDAwIGZmIDBkIGM3IGMxIDA5IDAwIDIwICB8Li4uLi6881cu%2Fy7HwS4uIHwKMDAwMDAwMTAgIDA4IDA1IGQwIDU1IGZlIDA0IDJkIGQzIDA0IDFmIGNhIDhjIDQ0IDIxIDViIGZmICB8Li7QVf4uLdMuLsouRCFb%2F3wKMDAwMDAwMjAgIDYwIGM3IGQ3IDAzIDE2IGJlIDQwIDFmIDc4IDRhIDNmIDA5IDg5IDBiIDlhIDdkICB8YMfXLi6%2BQC54Sj8uLi4ufXwKMDAwMDAwMzAgIDRlIGM4IDRlIDZkIDA1IDFlIDAxIDhiIDRjIDI0IDAwIDAwIDAwICAgICAgICAgICB8TshObS4uLi5MJC4uLnw [5]: https://gchq.github.io/CyberChef/#recipe=From_Hexdump()Gunzip()&input=MDAwMDAwMDAgIDFmIDhiIDA4IDAwIDEyIGJjIGYzIDU3IDAwIGZmIDBkIGM3IGMxIDA5IDAwIDIwICB8Li4uLi6881cu/y7HwS4uIHwKMDAwMDAwMTAgIDA4IDA1IGQwIDU1IGZlIDA0IDJkIGQzIDA0IDFmIGNhIDhjIDQ0IDIxIDViIGZmICB8Li7QVf4uLdMuLsouRCFb/3wKMDAwMDAwMjAgIDYwIGM3IGQ3IDAzIDE2IGJlIDQwIDFmIDc4IDRhIDNmIDA5IDg5IDBiIDlhIDdkICB8YMfXLi6%2BQC54Sj8uLi4ufXwKMDAwMDAwMzAgIDRlIGM4IDRlIDZkIDA1IDFlIDAxIDhiIDRjIDI0IDAwIDAwIDAwICAgICAgICAgICB8TshObS4uLi5MJC4uLnw
[6]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22Fork%22%2C%22args%22%3A%5B%22%5C%5Cn%22%2C%22%5C%5Cn%22%5D%7D%2C%7B%22op%22%3A%22From%20UNIX%20Timestamp%22%2C%22args%22%3A%5B%22Seconds%20(s)%22%5D%7D%5D&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA [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=%5B%7B%22op%22%3A%22Fork%22%2C%22args%22%3A%5B%22%5C%5Cn%22%2C%22%5C%5Cn%22%5D%7D%2C%7B%22op%22%3A%22Conditional%20Jump%22%2C%22args%22%3A%5B%221%22%2C%222%22%2C%2210%22%5D%7D%2C%7B%22op%22%3A%22To%20Hex%22%2C%22args%22%3A%5B%22Space%22%5D%7D%2C%7B%22op%22%3A%22Return%22%2C%22args%22%3A%5B%5D%7D%2C%7B%22op%22%3A%22To%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%5D%7D%5D&input=U29tZSBkYXRhIHdpdGggYSAxIGluIGl0ClNvbWUgZGF0YSB3aXRoIGEgMiBpbiBpdA [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=%5B%7B%22op%22%3A%22XOR%22%2C%22args%22%3A%5B%7B%22option%22%3A%22Hex%22%2C%22string%22%3A%223a%22%7D%2Cfalse%2Cfalse%5D%7D%2C%7B%22op%22%3A%22To%20Hexdump%22%2C%22args%22%3A%5B%2216%22%2Cfalse%2Cfalse%5D%7D%5D&input=VGhlIGFuc3dlciB0byB0aGUgdWx0aW1hdGUgcXVlc3Rpb24gb2YgbGlmZSwgdGhlIFVuaXZlcnNlLCBhbmQgZXZlcnl0aGluZyBpcyA0Mi4 [8]: 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
[9]: https://gchq.github.io/CyberChef/#recipe=Register('key%3D(%5B%5C%5Cda-f%5D*)',true,false)Find_/_Replace(%7B'option':'Regex','string':'.*data%3D(.*)'%7D,'$1',true,false,true)RC4(%7B'option':'Hex','string':'$R0'%7D,'Hex','Latin1')&input=aHR0cDovL21hbHdhcmV6LmJpei9iZWFjb24ucGhwP2tleT0wZTkzMmE1YyZkYXRhPThkYjdkNWViZTM4NjYzYTU0ZWNiYjMzNGUzZGIxMQ
[10]: https://gchq.github.io/CyberChef/#recipe=XOR(%7B'option':'Hex','string':'3a'%7D,'',false)To_Hexdump(16,false,false)&input=VGhlIGFuc3dlciB0byB0aGUgdWx0aW1hdGUgcXVlc3Rpb24gb2YgbGlmZSwgdGhlIFVuaXZlcnNlLCBhbmQgZXZlcnl0aGluZyBpcyA0Mi4

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -19,7 +19,7 @@
"outputSourcePath": true, "outputSourcePath": true,
"dateFormat": "ddd MMM Do YYYY", "dateFormat": "ddd MMM Do YYYY",
"sort": false, "sort": false,
"logoFile": "../build/prod/images/cyberchef-32x32.png", "logoFile": "cyberchef-32x32.png",
"cleverLinks": false, "cleverLinks": false,
"monospaceLinks": false, "monospaceLinks": false,
"protocol": "html://", "protocol": "html://",

9253
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "cyberchef", "name": "cyberchef",
"version": "5.3.4", "version": "6.4.0",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>", "author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef", "homepage": "https://gchq.github.io/CyberChef",
@@ -30,61 +30,76 @@
"main": "build/node/CyberChef.js", "main": "build/node/CyberChef.js",
"bugs": "https://github.com/gchq/CyberChef/issues", "bugs": "https://github.com/gchq/CyberChef/issues",
"devDependencies": { "devDependencies": {
"babel-core": "^6.24.0", "babel-core": "^6.26.0",
"babel-loader": "^6.4.0", "babel-loader": "^7.1.2",
"babel-polyfill": "^6.23.0", "babel-preset-env": "^1.6.0",
"babel-preset-env": "^1.2.2", "css-loader": "^0.28.7",
"css-loader": "^0.27.3",
"exports-loader": "^0.6.4", "exports-loader": "^0.6.4",
"extract-text-webpack-plugin": "^2.1.0", "extract-text-webpack-plugin": "^3.0.1",
"file-loader": "^0.10.1", "file-loader": "^1.1.4",
"grunt": ">=0.4.5", "grunt": ">=1.0.1",
"grunt-accessibility": "~5.0.0", "grunt-accessibility": "~5.0.0",
"grunt-chmod": "~1.1.1", "grunt-chmod": "~1.1.1",
"grunt-contrib-clean": "~1.0.0", "grunt-concurrent": "^2.3.1",
"grunt-contrib-clean": "~1.1.0",
"grunt-contrib-copy": "~1.0.0", "grunt-contrib-copy": "~1.0.0",
"grunt-eslint": "^19.0.0", "grunt-eslint": "^20.1.0",
"grunt-exec": "~1.0.1", "grunt-exec": "~3.0.0",
"grunt-execute": "^0.2.2", "grunt-execute": "^0.2.2",
"grunt-jsdoc": "^2.1.0", "grunt-jsdoc": "^2.2.0",
"grunt-webpack": "^2.0.1", "grunt-webpack": "^3.0.2",
"html-webpack-plugin": "^2.28.0", "html-webpack-plugin": "^2.30.1",
"imports-loader": "^0.7.1", "imports-loader": "^0.7.1",
"ink-docstrap": "^1.1.4", "ink-docstrap": "^1.3.0",
"jsdoc-babel": "^0.3.0", "jsdoc-babel": "^0.3.0",
"less": "^2.7.2", "less": "^2.7.2",
"less-loader": "^4.0.2", "less-loader": "^4.0.5",
"style-loader": "^0.15.0", "postcss-css-variables": "^0.8.0",
"url-loader": "^0.5.8", "postcss-import": "^11.0.0",
"web-resource-inliner": "^4.1.0", "postcss-loader": "^2.0.6",
"webpack": "^2.2.1" "style-loader": "^0.19.0",
"url-loader": "^0.5.9",
"val-loader": "^1.0.2",
"web-resource-inliner": "^4.2.0",
"webpack": "^3.6.0",
"webpack-dev-server": "^2.9.1",
"webpack-node-externals": "^1.6.0",
"worker-loader": "^1.0.0"
}, },
"dependencies": { "dependencies": {
"babel-polyfill": "^6.26.0",
"bootstrap": "^3.3.7", "bootstrap": "^3.3.7",
"bootstrap-colorpicker": "^2.5.1", "bootstrap-colorpicker": "^2.5.2",
"bootstrap-switch": "^3.3.4", "bootstrap-switch": "^3.3.4",
"crypto-api": "^0.6.2", "crypto-api": "^0.7.5",
"crypto-js": "^3.1.9-1", "crypto-js": "^3.1.9-1",
"diff": "^3.2.0", "diff": "^3.3.1",
"escodegen": "^1.8.1", "escodegen": "^1.9.0",
"esmangle": "^1.0.1", "esmangle": "^1.0.1",
"esprima": "^3.1.3", "esprima": "^4.0.0",
"exif-parser": "^0.1.12",
"google-code-prettify": "^1.0.5", "google-code-prettify": "^1.0.5",
"jquery": "^3.1.1", "jquery": "^3.2.1",
"js-crc": "^0.2.0",
"js-sha3": "^0.6.1",
"jsbn": "^1.1.0", "jsbn": "^1.1.0",
"jsrsasign": "7.1.3", "jsonpath": "^0.2.12",
"jsrsasign": "8.0.4",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"moment": "^2.17.1", "moment": "^2.18.1",
"moment-timezone": "^0.5.11", "moment-timezone": "^0.5.13",
"node-md6": "^0.1.0",
"otp": "^0.1.3",
"sladex-blowfish": "^0.8.1", "sladex-blowfish": "^0.8.1",
"sortablejs": "^1.5.1", "sortablejs": "^1.6.1",
"split.js": "^1.2.0", "split.js": "^1.3.5",
"vkbeautify": "^0.99.1", "vkbeautify": "^0.99.3",
"xmldom": "^0.1.27", "xmldom": "^0.1.27",
"xpath": "0.0.24", "xpath": "0.0.24",
"zlibjs": "^0.2.0" "zlibjs": "^0.3.1"
}, },
"scripts": { "scripts": {
"start": "grunt dev",
"build": "grunt prod", "build": "grunt prod",
"test": "grunt test", "test": "grunt test",
"docs": "grunt docs" "docs": "grunt docs"

15
postcss.config.js Normal file
View File

@@ -0,0 +1,15 @@
module.exports = {
plugins: [
require("postcss-import"),
require("autoprefixer")({
browsers: [
"Chrome >= 40",
"Firefox >= 35",
"Edge >= 14"
]
}),
require("postcss-css-variables")({
preserve: true
}),
]
};

View File

@@ -30,7 +30,6 @@ const Chef = function() {
* @returns {string} response.result - The output of the recipe * @returns {string} response.result - The output of the recipe
* @returns {string} response.type - The data type of the result * @returns {string} response.type - The data type of the result
* @returns {number} response.progress - The position that we have got to in the recipe * @returns {number} response.progress - The position that we have got to in the recipe
* @returns {number} response.options - The app options object (which may have been changed)
* @returns {number} response.duration - The number of ms it took to execute the recipe * @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) * @returns {number} response.error - The error object thrown by a failed operation (false if no error)
*/ */
@@ -40,12 +39,7 @@ Chef.prototype.bake = async function(inputText, recipeConfig, options, progress,
containsFc = recipe.containsFlowControl(), containsFc = recipe.containsFlowControl(),
error = false; error = false;
// Reset attemptHighlight flag if (containsFc && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
if (options.hasOwnProperty("attemptHighlight")) {
options.attemptHighlight = true;
}
if (containsFc) options.attemptHighlight = false;
// Clean up progress // Clean up progress
if (progress >= recipeConfig.length) { if (progress >= recipeConfig.length) {
@@ -86,7 +80,6 @@ Chef.prototype.bake = async function(inputText, recipeConfig, options, progress,
this.dish.get(Dish.STRING), this.dish.get(Dish.STRING),
type: Dish.enumLookup(this.dish.type), type: Dish.enumLookup(this.dish.type),
progress: progress, progress: progress,
options: options,
duration: new Date().getTime() - startTime, duration: new Date().getTime() - startTime,
error: error error: error
}; };
@@ -123,4 +116,38 @@ Chef.prototype.silentBake = function(recipeConfig) {
return new Date().getTime() - startTime; return new Date().getTime() - startTime;
}; };
/**
* Calculates highlight offsets if possible.
*
* @param {Object[]} recipeConfig
* @param {string} direction
* @param {Object} pos - The position object for the highlight.
* @param {number} pos.start - The start offset.
* @param {number} pos.end - The end offset.
* @returns {Object}
*/
Chef.prototype.calculateHighlights = function(recipeConfig, direction, pos) {
const recipe = new Recipe(recipeConfig);
const highlights = recipe.generateHighlightList();
if (!highlights) return false;
for (let i = 0; i < highlights.length; i++) {
// Remove multiple highlights before processing again
pos = [pos[0]];
const func = direction === "forward" ? highlights[i].f : highlights[i].b;
if (typeof func == "function") {
pos = func(pos, highlights[i].args);
}
}
return {
pos: pos,
direction: direction
};
};
export default Chef; export default Chef;

197
src/core/ChefWorker.js Normal file
View File

@@ -0,0 +1,197 @@
/**
* Web Worker to handle communications between the front-end and the core.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import "babel-polyfill";
import Chef from "./Chef.js";
import OperationConfig from "./config/MetaConfig.js";
import OpModules from "./config/modules/Default.js";
// Set up Chef instance
self.chef = new Chef();
self.OpModules = OpModules;
self.OperationConfig = OperationConfig;
// Tell the app that the worker has loaded and is ready to operate
self.postMessage({
action: "workerLoaded",
data: {}
});
/**
* Respond to message from parent thread.
*
* Messages should have the following format:
* {
* action: "bake" | "silentBake",
* data: {
* input: {string},
* recipeConfig: {[Object]},
* options: {Object},
* progress: {number},
* step: {boolean}
* } | undefined
* }
*/
self.addEventListener("message", function(e) {
// Handle message
const r = e.data;
switch (r.action) {
case "bake":
bake(r.data);
break;
case "silentBake":
silentBake(r.data);
break;
case "docURL":
// Used to set the URL of the current document so that scripts can be
// imported into an inline worker.
self.docURL = r.data;
break;
case "highlight":
calculateHighlights(
r.data.recipeConfig,
r.data.direction,
r.data.pos
);
break;
default:
break;
}
});
/**
* Baking handler
*
* @param {Object} data
*/
async function bake(data) {
// Ensure the relevant modules are loaded
loadRequiredModules(data.recipeConfig);
try {
const response = await self.chef.bake(
data.input, // The user's input
data.recipeConfig, // The configuration of the recipe
data.options, // Options set by the user
data.progress, // The current position in the recipe
data.step // Whether or not to take one step or execute the whole recipe
);
self.postMessage({
action: "bakeSuccess",
data: response
});
} catch (err) {
self.postMessage({
action: "bakeError",
data: err
});
}
}
/**
* Silent baking handler
*/
function silentBake(data) {
const duration = self.chef.silentBake(data.recipeConfig);
self.postMessage({
action: "silentBakeComplete",
data: duration
});
}
/**
* Checks that all required modules are loaded and loads them if not.
*
* @param {Object} recipeConfig
*/
function loadRequiredModules(recipeConfig) {
recipeConfig.forEach(op => {
let module = self.OperationConfig[op.op].module;
if (!OpModules.hasOwnProperty(module)) {
console.log("Loading module " + module);
self.sendStatusMessage("Loading module " + module);
self.importScripts(self.docURL + "/" + module + ".js");
}
});
}
/**
* Calculates highlight offsets if possible.
*
* @param {Object[]} recipeConfig
* @param {string} direction
* @param {Object} pos - The position object for the highlight.
* @param {number} pos.start - The start offset.
* @param {number} pos.end - The end offset.
*/
function calculateHighlights(recipeConfig, direction, pos) {
pos = self.chef.calculateHighlights(recipeConfig, direction, pos);
self.postMessage({
action: "highlightsCalculated",
data: pos
});
}
/**
* Send status update to the app.
*
* @param {string} msg
*/
self.sendStatusMessage = function(msg) {
self.postMessage({
action: "statusMessage",
data: msg
});
};
/**
* Send an option value update to the app.
*
* @param {string} option
* @param {*} value
*/
self.setOption = function(option, value) {
self.postMessage({
action: "optionUpdate",
data: {
option: option,
value: 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

@@ -13,22 +13,6 @@ import Dish from "./Dish.js";
*/ */
const FlowControl = { const FlowControl = {
/**
* @constant
* @default
*/
FORK_DELIM: "\\n",
/**
* @constant
* @default
*/
MERGE_DELIM: "\\n",
/**
* @constant
* @default
*/
FORK_IGNORE_ERRORS: false,
/** /**
* Fork operation. * Fork operation.
* *
@@ -107,15 +91,72 @@ const FlowControl = {
/** /**
* @constant * Register operation.
* @default *
* @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.
*/ */
JUMP_NUM: 0, runRegister: function(state) {
/** const ings = state.opList[state.progress].getIngValues(),
* @constant extractorStr = ings[0],
* @default i = ings[1],
*/ m = ings[2];
MAX_JUMPS: 10,
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. * Jump operation.

View File

@@ -73,7 +73,7 @@ Ingredient.prepare = function(data, type) {
case "byteArray": case "byteArray":
if (typeof data == "string") { if (typeof data == "string") {
data = data.replace(/\s+/g, ""); data = data.replace(/\s+/g, "");
return Utils.hexToByteArray(data); return Utils.fromHex(data);
} else { } else {
return data; return data;
} }

View File

@@ -1,5 +1,7 @@
import Dish from "./Dish.js"; import Dish from "./Dish.js";
import Ingredient from "./Ingredient.js"; import Ingredient from "./Ingredient.js";
import OperationConfig from "./config/MetaConfig.js";
import OpModules from "./config/modules/OpModules.js";
/** /**
@@ -11,10 +13,10 @@ import Ingredient from "./Ingredient.js";
* *
* @class * @class
* @param {string} operationName * @param {string} operationName
* @param {Object} operationConfig
*/ */
const Operation = function(operationName, operationConfig) { const Operation = function(operationName) {
this.name = operationName; this.name = operationName;
this.module = "";
this.description = ""; this.description = "";
this.inputType = -1; this.inputType = -1;
this.outputType = -1; this.outputType = -1;
@@ -25,8 +27,8 @@ const Operation = function(operationName, operationConfig) {
this.disabled = false; this.disabled = false;
this.ingList = []; this.ingList = [];
if (operationConfig) { if (OperationConfig.hasOwnProperty(this.name)) {
this._parseConfig(operationConfig); this._parseConfig(OperationConfig[this.name]);
} }
}; };
@@ -38,19 +40,28 @@ const Operation = function(operationName, operationConfig) {
* @param {Object} operationConfig * @param {Object} operationConfig
*/ */
Operation.prototype._parseConfig = function(operationConfig) { Operation.prototype._parseConfig = function(operationConfig) {
this.module = operationConfig.module;
this.description = operationConfig.description; this.description = operationConfig.description;
this.inputType = Dish.typeEnum(operationConfig.inputType); this.inputType = Dish.typeEnum(operationConfig.inputType);
this.outputType = Dish.typeEnum(operationConfig.outputType); this.outputType = Dish.typeEnum(operationConfig.outputType);
this.run = operationConfig.run;
this.highlight = operationConfig.highlight; this.highlight = operationConfig.highlight;
this.highlightReverse = operationConfig.highlightReverse; this.highlightReverse = operationConfig.highlightReverse;
this.flowControl = operationConfig.flowControl; this.flowControl = operationConfig.flowControl;
this.run = OpModules[this.module][this.name];
for (let a = 0; a < operationConfig.args.length; a++) { for (let a = 0; a < operationConfig.args.length; a++) {
const ingredientConfig = operationConfig.args[a]; const ingredientConfig = operationConfig.args[a];
const ingredient = new Ingredient(ingredientConfig); const ingredient = new Ingredient(ingredientConfig);
this.addIngredient(ingredient); this.addIngredient(ingredient);
} }
if (this.highlight === "func") {
this.highlight = OpModules[this.module][`${this.name}-highlight`];
}
if (this.highlightReverse === "func") {
this.highlightReverse = OpModules[this.module][`${this.name}-highlightReverse`];
}
}; };

View File

@@ -1,5 +1,4 @@
import Operation from "./Operation.js"; import Operation from "./Operation.js";
import OperationConfig from "./config/OperationConfig.js";
/** /**
@@ -30,8 +29,7 @@ const Recipe = function(recipeConfig) {
Recipe.prototype._parseConfig = function(recipeConfig) { Recipe.prototype._parseConfig = function(recipeConfig) {
for (let c = 0; c < recipeConfig.length; c++) { for (let c = 0; c < recipeConfig.length; c++) {
const operationName = recipeConfig[c].op; const operationName = recipeConfig[c].op;
const operationConfig = OperationConfig[operationName]; const operation = new Operation(operationName);
const operation = new Operation(operationName, operationConfig);
operation.setIngValues(recipeConfig[c].args); operation.setIngValues(recipeConfig[c].args);
operation.setBreakpoint(recipeConfig[c].breakpoint); operation.setBreakpoint(recipeConfig[c].breakpoint);
operation.setDisabled(recipeConfig[c].disabled); operation.setDisabled(recipeConfig[c].disabled);
@@ -147,7 +145,7 @@ Recipe.prototype.lastOpIndex = function(startIndex) {
*/ */
Recipe.prototype.execute = async function(dish, startFrom) { Recipe.prototype.execute = async function(dish, startFrom) {
startFrom = startFrom || 0; startFrom = startFrom || 0;
let op, input, output, numJumps = 0; let op, input, output, numJumps = 0, numRegisters = 0;
for (let i = startFrom; i < this.opList.length; i++) { for (let i = startFrom; i < this.opList.length; i++) {
op = this.opList[i]; op = this.opList[i];
@@ -164,15 +162,17 @@ Recipe.prototype.execute = async function(dish, startFrom) {
if (op.isFlowControl()) { if (op.isFlowControl()) {
// Package up the current state // Package up the current state
let state = { let state = {
"progress" : i, "progress": i,
"dish" : dish, "dish": dish,
"opList" : this.opList, "opList": this.opList,
"numJumps" : numJumps "numJumps": numJumps,
"numRegisters": numRegisters
}; };
state = await op.run(state); state = await op.run(state);
i = state.progress; i = state.progress;
numJumps = state.numJumps; numJumps = state.numJumps;
numRegisters = state.numRegisters;
} else { } else {
output = await op.run(input, op.getIngValues()); output = await op.run(input, op.getIngValues());
dish.set(output, op.outputType); dish.set(output, op.outputType);
@@ -217,4 +217,37 @@ Recipe.prototype.fromString = function(recipeStr) {
this._parseConfig(recipeConfig); this._parseConfig(recipeConfig);
}; };
/**
* Generates a list of all the highlight functions assigned to operations in the recipe, if the
* entire recipe supports highlighting.
*
* @returns {Object[]} highlights
* @returns {function} highlights[].f
* @returns {function} highlights[].b
* @returns {Object[]} highlights[].args
*/
Recipe.prototype.generateHighlightList = function() {
const highlights = [];
for (let i = 0; i < this.opList.length; i++) {
let op = this.opList[i];
if (op.isDisabled()) continue;
// If any breakpoints are set, do not attempt to highlight
if (op.isBreakpoint()) return false;
// If any of the operations do not support highlighting, fail immediately.
if (op.highlight === false || op.highlight === undefined) return false;
highlights.push({
f: op.highlight,
b: op.highlightReverse,
args: op.getIngValues()
});
}
return highlights;
};
export default Recipe; export default Recipe;

View File

@@ -23,6 +23,16 @@ const Utils = {
* Utils.chr(97); * Utils.chr(97);
*/ */
chr: function(o) { chr: function(o) {
// Detect astral symbols
// Thanks to @mathiasbynens for this solution
// https://mathiasbynens.be/notes/javascript-unicode
if (o > 0xffff) {
o -= 0x10000;
const high = String.fromCharCode(o >>> 10 & 0x3ff | 0xd800);
o = 0xdc00 | o & 0x3ff;
return high + String.fromCharCode(o);
}
return String.fromCharCode(o); return String.fromCharCode(o);
}, },
@@ -38,6 +48,18 @@ const Utils = {
* Utils.ord('a'); * Utils.ord('a');
*/ */
ord: function(c) { ord: function(c) {
// Detect astral symbols
// Thanks to @mathiasbynens for this solution
// https://mathiasbynens.be/notes/javascript-unicode
if (c.length === 2) {
const high = c.charCodeAt(0);
const low = c.charCodeAt(1);
if (high >= 0xd800 && high < 0xdc00 &&
low >= 0xdc00 && low < 0xe000) {
return (high - 0xd800) * 0x400 + low - 0xdc00 + 0x10000;
}
}
return c.charCodeAt(0); return c.charCodeAt(0);
}, },
@@ -212,11 +234,11 @@ const Utils = {
* @returns {string} * @returns {string}
*/ */
printable: function(str, preserveWs) { printable: function(str, preserveWs) {
if (typeof window !== "undefined" && window.app && !window.app.options.treatAsUtf8) { if (ENVIRONMENT_IS_WEB() && window.app && !window.app.options.treatAsUtf8) {
str = Utils.byteArrayToChars(Utils.strToByteArray(str)); str = Utils.byteArrayToChars(Utils.strToByteArray(str));
} }
const re = /[\0-\x08\x0B-\x0C\x0E-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/g; const re = /[\0-\x08\x0B-\x0C\x0E-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uD7FF\uE000-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/g;
const wsRe = /[\x09-\x10\x0D\u2028\u2029]/g; const wsRe = /[\x09-\x10\x0D\u2028\u2029]/g;
str = str.replace(re, "."); str = str.replace(re, ".");
@@ -259,6 +281,22 @@ const Utils = {
}, },
/**
* Escape a string containing regex control characters so that it can be safely
* used in a regex without causing unintended behaviours.
*
* @param {string} str
* @returns {string}
*
* @example
* // returns "\[example\]"
* Utils.escapeRegex("[example]");
*/
escapeRegex: function(str) {
return str.replace(/([.*+?^=!:${}()|[\]/\\])/g, "\\$1");
},
/** /**
* Expand an alphabet range string into a list of the characters in that range. * Expand an alphabet range string into a list of the characters in that range.
* *
@@ -302,48 +340,6 @@ const Utils = {
}, },
/**
* Translates a hex string into an array of bytes.
*
* @param {string} hexStr
* @returns {byteArray}
*
* @example
* // returns [0xfe, 0x09, 0xa7]
* Utils.hexToByteArray("fe09a7");
*/
hexToByteArray: function(hexStr) {
// TODO: Handle errors i.e. input string is not hex
if (!hexStr) return [];
hexStr = hexStr.replace(/\s+/g, "");
const byteArray = [];
for (let i = 0; i < hexStr.length; i += 2) {
byteArray.push(parseInt(hexStr.substr(i, 2), 16));
}
return byteArray;
},
/**
* Translates an array of bytes to a hex string.
*
* @param {byteArray} byteArray
* @returns {string}
*
* @example
* // returns "fe09a7"
* Utils.byteArrayToHex([0xfe, 0x09, 0xa7]);
*/
byteArrayToHex: function(byteArray) {
if (!byteArray) return "";
let hexStr = "";
for (let i = 0; i < byteArray.length; i++) {
hexStr += Utils.hex(byteArray[i]) + " ";
}
return hexStr.slice(0, hexStr.length-1);
},
/** /**
* Converts a string to a byte array. * Converts a string to a byte array.
* Treats the string as UTF-8 if any values are over 255. * Treats the string as UTF-8 if any values are over 255.
@@ -388,15 +384,19 @@ const Utils = {
let wordArray = CryptoJS.enc.Utf8.parse(str), let wordArray = CryptoJS.enc.Utf8.parse(str),
byteArray = Utils.wordArrayToByteArray(wordArray); byteArray = Utils.wordArrayToByteArray(wordArray);
if (typeof window !== "undefined" && str.length !== wordArray.sigBytes) { if (str.length !== wordArray.sigBytes) {
window.app.options.attemptHighlight = false; if (ENVIRONMENT_IS_WORKER()) {
self.setOption("attemptHighlight", false);
} else if (ENVIRONMENT_IS_WEB()) {
window.app.options.attemptHighlight = false;
}
} }
return byteArray; return byteArray;
}, },
/** /**
* Converts a string to a charcode array * Converts a string to a unicode charcode array
* *
* @param {string} str * @param {string} str
* @returns {byteArray} * @returns {byteArray}
@@ -409,12 +409,23 @@ const Utils = {
* Utils.strToCharcode("你好"); * Utils.strToCharcode("你好");
*/ */
strToCharcode: function(str) { strToCharcode: function(str) {
const byteArray = new Array(str.length); const charcode = [];
let i = str.length;
while (i--) { for (let i = 0; i < str.length; i++) {
byteArray[i] = str.charCodeAt(i); let ord = str.charCodeAt(i);
// Detect and merge astral symbols
if (i < str.length - 1 && ord >= 0xd800 && ord < 0xdc00) {
const low = str[i + 1].charCodeAt(0);
if (low >= 0xdc00 && low < 0xe000) {
ord = Utils.ord(str[i] + str[++i]);
}
}
charcode.push(ord);
} }
return byteArray;
return charcode;
}, },
@@ -441,8 +452,13 @@ const Utils = {
let wordArray = new CryptoJS.lib.WordArray.init(words, byteArray.length), let wordArray = new CryptoJS.lib.WordArray.init(words, byteArray.length),
str = CryptoJS.enc.Utf8.stringify(wordArray); str = CryptoJS.enc.Utf8.stringify(wordArray);
if (typeof window !== "undefined" && str.length !== wordArray.sigBytes) if (str.length !== wordArray.sigBytes) {
window.app.options.attemptHighlight = false; if (ENVIRONMENT_IS_WORKER()) {
self.setOption("attemptHighlight", false);
} else if (ENVIRONMENT_IS_WEB()) {
window.app.options.attemptHighlight = false;
}
}
return str; return str;
} catch (err) { } catch (err) {
// If it fails, treat it as ANSI // If it fails, treat it as ANSI
@@ -498,127 +514,6 @@ const Utils = {
}, },
/**
* Mapping of Unicode code points to Windows-1251
* @private
* @constant
*/
UNIC_WIN1251_MAP: {
0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10, 11: 11, 12: 12, 13: 13,
14: 14, 15: 15, 16: 16, 17: 17, 18: 18, 19: 19, 20: 20, 21: 21, 22: 22, 23: 23, 24: 24,
25: 25, 26: 26, 27: 27, 28: 28, 29: 29, 30: 30, 31: 31, 32: 32, 33: 33, 34: 34, 35: 35,
36: 36, 37: 37, 38: 38, 39: 39, 40: 40, 41: 41, 42: 42, 43: 43, 44: 44, 45: 45, 46: 46,
47: 47, 48: 48, 49: 49, 50: 50, 51: 51, 52: 52, 53: 53, 54: 54, 55: 55, 56: 56, 57: 57,
58: 58, 59: 59, 60: 60, 61: 61, 62: 62, 63: 63, 64: 64, 65: 65, 66: 66, 67: 67, 68: 68,
69: 69, 70: 70, 71: 71, 72: 72, 73: 73, 74: 74, 75: 75, 76: 76, 77: 77, 78: 78, 79: 79,
80: 80, 81: 81, 82: 82, 83: 83, 84: 84, 85: 85, 86: 86, 87: 87, 88: 88, 89: 89, 90: 90,
91: 91, 92: 92, 93: 93, 94: 94, 95: 95, 96: 96, 97: 97, 98: 98, 99: 99, 100: 100, 101: 101,
102: 102, 103: 103, 104: 104, 105: 105, 106: 106, 107: 107, 108: 108, 109: 109, 110: 110,
111: 111, 112: 112, 113: 113, 114: 114, 115: 115, 116: 116, 117: 117, 118: 118, 119: 119,
120: 120, 121: 121, 122: 122, 123: 123, 124: 124, 125: 125, 126: 126, 127: 127, 1027: 129,
8225: 135, 1046: 198, 8222: 132, 1047: 199, 1168: 165, 1048: 200, 1113: 154, 1049: 201,
1045: 197, 1050: 202, 1028: 170, 160: 160, 1040: 192, 1051: 203, 164: 164, 166: 166,
167: 167, 169: 169, 171: 171, 172: 172, 173: 173, 174: 174, 1053: 205, 176: 176, 177: 177,
1114: 156, 181: 181, 182: 182, 183: 183, 8221: 148, 187: 187, 1029: 189, 1056: 208,
1057: 209, 1058: 210, 8364: 136, 1112: 188, 1115: 158, 1059: 211, 1060: 212, 1030: 178,
1061: 213, 1062: 214, 1063: 215, 1116: 157, 1064: 216, 1065: 217, 1031: 175, 1066: 218,
1067: 219, 1068: 220, 1069: 221, 1070: 222, 1032: 163, 8226: 149, 1071: 223, 1072: 224,
8482: 153, 1073: 225, 8240: 137, 1118: 162, 1074: 226, 1110: 179, 8230: 133, 1075: 227,
1033: 138, 1076: 228, 1077: 229, 8211: 150, 1078: 230, 1119: 159, 1079: 231, 1042: 194,
1080: 232, 1034: 140, 1025: 168, 1081: 233, 1082: 234, 8212: 151, 1083: 235, 1169: 180,
1084: 236, 1052: 204, 1085: 237, 1035: 142, 1086: 238, 1087: 239, 1088: 240, 1089: 241,
1090: 242, 1036: 141, 1041: 193, 1091: 243, 1092: 244, 8224: 134, 1093: 245, 8470: 185,
1094: 246, 1054: 206, 1095: 247, 1096: 248, 8249: 139, 1097: 249, 1098: 250, 1044: 196,
1099: 251, 1111: 191, 1055: 207, 1100: 252, 1038: 161, 8220: 147, 1101: 253, 8250: 155,
1102: 254, 8216: 145, 1103: 255, 1043: 195, 1105: 184, 1039: 143, 1026: 128, 1106: 144,
8218: 130, 1107: 131, 8217: 146, 1108: 186, 1109: 190
},
/**
* Mapping of Windows-1251 code points to Unicode
* @private
* @constant
*/
WIN1251_UNIC_MAP: {
0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10, 11: 11, 12: 12, 13: 13,
14: 14, 15: 15, 16: 16, 17: 17, 18: 18, 19: 19, 20: 20, 21: 21, 22: 22, 23: 23, 24: 24,
25: 25, 26: 26, 27: 27, 28: 28, 29: 29, 30: 30, 31: 31, 32: 32, 33: 33, 34: 34, 35: 35,
36: 36, 37: 37, 38: 38, 39: 39, 40: 40, 41: 41, 42: 42, 43: 43, 44: 44, 45: 45, 46: 46,
47: 47, 48: 48, 49: 49, 50: 50, 51: 51, 52: 52, 53: 53, 54: 54, 55: 55, 56: 56, 57: 57,
58: 58, 59: 59, 60: 60, 61: 61, 62: 62, 63: 63, 64: 64, 65: 65, 66: 66, 67: 67, 68: 68,
69: 69, 70: 70, 71: 71, 72: 72, 73: 73, 74: 74, 75: 75, 76: 76, 77: 77, 78: 78, 79: 79,
80: 80, 81: 81, 82: 82, 83: 83, 84: 84, 85: 85, 86: 86, 87: 87, 88: 88, 89: 89, 90: 90,
91: 91, 92: 92, 93: 93, 94: 94, 95: 95, 96: 96, 97: 97, 98: 98, 99: 99, 100: 100, 101: 101,
102: 102, 103: 103, 104: 104, 105: 105, 106: 106, 107: 107, 108: 108, 109: 109, 110: 110,
111: 111, 112: 112, 113: 113, 114: 114, 115: 115, 116: 116, 117: 117, 118: 118, 119: 119,
120: 120, 121: 121, 122: 122, 123: 123, 124: 124, 125: 125, 126: 126, 127: 127, 160: 160,
164: 164, 166: 166, 167: 167, 169: 169, 171: 171, 172: 172, 173: 173, 174: 174, 176: 176,
177: 177, 181: 181, 182: 182, 183: 183, 187: 187, 168: 1025, 128: 1026, 129: 1027,
170: 1028, 189: 1029, 178: 1030, 175: 1031, 163: 1032, 138: 1033, 140: 1034, 142: 1035,
141: 1036, 161: 1038, 143: 1039, 192: 1040, 193: 1041, 194: 1042, 195: 1043, 196: 1044,
197: 1045, 198: 1046, 199: 1047, 200: 1048, 201: 1049, 202: 1050, 203: 1051, 204: 1052,
205: 1053, 206: 1054, 207: 1055, 208: 1056, 209: 1057, 210: 1058, 211: 1059, 212: 1060,
213: 1061, 214: 1062, 215: 1063, 216: 1064, 217: 1065, 218: 1066, 219: 1067, 220: 1068,
221: 1069, 222: 1070, 223: 1071, 224: 1072, 225: 1073, 226: 1074, 227: 1075, 228: 1076,
229: 1077, 230: 1078, 231: 1079, 232: 1080, 233: 1081, 234: 1082, 235: 1083, 236: 1084,
237: 1085, 238: 1086, 239: 1087, 240: 1088, 241: 1089, 242: 1090, 243: 1091, 244: 1092,
245: 1093, 246: 1094, 247: 1095, 248: 1096, 249: 1097, 250: 1098, 251: 1099, 252: 1100,
253: 1101, 254: 1102, 255: 1103, 184: 1105, 144: 1106, 131: 1107, 186: 1108, 190: 1109,
179: 1110, 191: 1111, 188: 1112, 154: 1113, 156: 1114, 158: 1115, 157: 1116, 162: 1118,
159: 1119, 165: 1168, 180: 1169, 150: 8211, 151: 8212, 145: 8216, 146: 8217, 130: 8218,
147: 8220, 148: 8221, 132: 8222, 134: 8224, 135: 8225, 149: 8226, 133: 8230, 137: 8240,
139: 8249, 155: 8250, 136: 8364, 185: 8470, 153: 8482
},
/**
* Converts a Unicode string to Windows-1251 encoding
*
* @param {string} unicStr
* @returns {string} String encoded in Windows-1251
*
* @example
* // returns "îáíîâëåííàÿ òåõíè÷êà ïî Áîèíãó. îðèãèíàë ó ìåíÿ. çàáåðåòå êîãäà áóäåòå â ÊÈ"
* Utils.unicodeToWin1251("обновленная техничка по Боингу. оригинал у меня. заберете когда будете в КИ");
*/
unicodeToWin1251: function(unicStr) {
const res = [];
for (let i = 0; i < unicStr.length; i++) {
const ord = unicStr.charCodeAt(i);
if (!(ord in Utils.UNIC_WIN1251_MAP))
throw "Character '" + unicStr.charAt(i) + "' isn't supported by Windows-1251";
res.push(String.fromCharCode(Utils.UNIC_WIN1251_MAP[ord]));
}
return res.join("");
},
/**
* Converts a Windows-1251 string to Unicode encoding
*
* @param {string} win1251Str
* @returns {string} String encoded in Unicode
*
* @example
* // returns "обновленная техничка по Боингу. оригинал у меня. заберете когда будете в КИ"
* Utils.unicodeToWin1251("îáíîâëåííàÿ òåõíè÷êà ïî Áîèíãó. îðèãèíàë ó ìåíÿ. çàáåðåòå êîãäà áóäåòå â ÊÈ");
*/
win1251ToUnicode: function(win1251Str) {
const res = [];
for (let i = 0; i < win1251Str.length; i++) {
const ord = win1251Str.charCodeAt(i);
if (!(ord in Utils.WIN1251_UNIC_MAP))
throw "Character '" + win1251Str.charAt(i) + "' isn't supported by Windows-1251";
res.push(String.fromCharCode(Utils.WIN1251_UNIC_MAP[ord]));
}
return res.join("");
},
/** /**
* Base64's the input byte array using the given alphabet, returning a string. * Base64's the input byte array using the given alphabet, returning a string.
* *
@@ -706,7 +601,7 @@ const Utils = {
i = 0; i = 0;
if (removeNonAlphChars) { if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[\[\]\\\-^$]/g, "\\$&") + "]", "g"); const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
data = data.replace(re, ""); data = data.replace(re, "");
} }
@@ -898,7 +793,7 @@ const Utils = {
if (removeScriptAndStyle) { if (removeScriptAndStyle) {
htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, ""); htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, "");
} }
return htmlStr.replace(/<[^>\n]+>/g, ""); return htmlStr.replace(/<[^>]+>/g, "");
}, },
@@ -924,7 +819,7 @@ const Utils = {
"`": "&#x60;" "`": "&#x60;"
}; };
return str.replace(/[&<>"'\/`]/g, function (match) { return str.replace(/[&<>"'/`]/g, function (match) {
return HTML_CHARS[match]; return HTML_CHARS[match];
}); });
}, },
@@ -957,6 +852,139 @@ const Utils = {
}, },
/**
* Encodes a URI fragment (#) or query (?) using a minimal amount of percent-encoding.
*
* RFC 3986 defines legal characters for the fragment and query parts of a URL to be as follows:
*
* fragment = *( pchar / "/" / "?" )
* query = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* pct-encoded = "%" HEXDIG HEXDIG
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*
* Meaning that the list of characters that need not be percent-encoded are alphanumeric plus:
* -._~!$&'()*+,;=:@/?
*
* & and = are still escaped as they are used to serialise the key-value pairs in CyberChef
* fragments. + is also escaped so as to prevent it being decoded to a space.
*
* @param {string} str
* @returns {string}
*/
encodeURIFragment: function(str) {
const LEGAL_CHARS = {
"%2D": "-",
"%2E": ".",
"%5F": "_",
"%7E": "~",
"%21": "!",
"%24": "$",
//"%26": "&",
"%27": "'",
"%28": "(",
"%29": ")",
"%2A": "*",
//"%2B": "+",
"%2C": ",",
"%3B": ";",
//"%3D": "=",
"%3A": ":",
"%40": "@",
"%2F": "/",
"%3F": "?"
};
str = encodeURIComponent(str);
return str.replace(/%[0-9A-F]{2}/g, function (match) {
return LEGAL_CHARS[match] || match;
});
},
/**
* Generates a "pretty" recipe format from a recipeConfig object.
*
* "Pretty" CyberChef recipe formats are designed to be included in the fragment (#) or query (?)
* parts of the URL. They can also be loaded into CyberChef through the 'Load' interface. In order
* to make this format as readable as possible, various special characters are used unescaped. This
* reduces the amount of percent-encoding included in the URL which is typically difficult to read,
* as well as substantially increasing the overall length. These characteristics can be quite
* offputting for users.
*
* @param {Object[]} recipeConfig
* @param {boolean} newline - whether to add a newline after each operation
* @returns {string}
*/
generatePrettyRecipe: function(recipeConfig, newline) {
let prettyConfig = "",
name = "",
args = "",
disabled = "",
bp = "";
recipeConfig.forEach(op => {
name = op.op.replace(/ /g, "_");
args = JSON.stringify(op.args)
.slice(1, -1) // Remove [ and ] as they are implied
// We now need to switch double-quoted (") strings to single-quotes (') as these do not
// need to be percent-encoded.
.replace(/'/g, "\\'") // Escape single quotes
.replace(/\\"/g, '"') // Unescape double quotes
.replace(/(^|,|{|:)"/g, "$1'") // Replace opening " with '
.replace(/"(,|:|}|$)/g, "'$1"); // Replace closing " with '
disabled = op.disabled ? "/disabled": "";
bp = op.breakpoint ? "/breakpoint" : "";
prettyConfig += `${name}(${args}${disabled}${bp})`;
if (newline) prettyConfig += "\n";
});
return prettyConfig;
},
/**
* Converts a recipe string to the JSON representation of the recipe.
* Accepts either stringified JSON or bespoke "pretty" recipe format.
*
* @param {string} recipe
* @returns {Object[]}
*/
parseRecipeConfig: function(recipe) {
recipe = recipe.trim();
if (recipe.length === 0) return [];
if (recipe[0] === "[") return JSON.parse(recipe);
// Parse bespoke recipe format
recipe = recipe.replace(/\n/g, "");
let m,
recipeRegex = /([^(]+)\(((?:'[^'\\]*(?:\\.[^'\\]*)*'|[^)/])*)(\/[^)]+)?\)/g,
recipeConfig = [],
args;
while ((m = recipeRegex.exec(recipe))) {
// Translate strings in args back to double-quotes
args = m[2]
.replace(/"/g, '\\"') // Escape double quotes
.replace(/(^|,|{|:)'/g, '$1"') // Replace opening ' with "
.replace(/([^\\])'(,|:|}|$)/g, '$1"$2') // Replace closing ' with "
.replace(/\\'/g, "'"); // Unescape single quotes
args = "[" + args + "]";
let op = {
op: m[1].replace(/_/g, " "),
args: JSON.parse(args)
};
if (m[3] && m[3].indexOf("disabled") > 0) op.disabled = true;
if (m[3] && m[3].indexOf("breakpoint") > 0) op.breakpoint = true;
recipeConfig.push(op);
}
return recipeConfig;
},
/** /**
* Expresses a number of milliseconds in a human readable format. * Expresses a number of milliseconds in a human readable format.
* *
@@ -1015,17 +1043,20 @@ const Utils = {
* @param {Object[]} files * @param {Object[]} files
* @returns {html} * @returns {html}
*/ */
displayFilesAsHTML: function(files){ displayFilesAsHTML: function(files) {
/* <NL> and <SP> used to denote newlines and spaces in HTML markup.
* If a non-html operation is used, all markup will be removed but these
* whitespace chars will remain for formatting purposes.
*/
const formatDirectory = function(file) { const formatDirectory = function(file) {
const html = "<div class='panel panel-default'>" + const html = `<div class='panel panel-default' style='white-space: normal;'>
"<div class='panel-heading' role='tab'>" + <div class='panel-heading' role='tab'>
"<h4 class='panel-title'>" + <h4 class='panel-title'>
file.fileName + <NL>${Utils.escapeHtml(file.fileName)}
// The following line is for formatting when HTML is stripped </h4>
"<span style='display: none'>\n0 bytes\n</span>" + </div>
"</h4>" + </div>`;
"</div>" +
"</div>";
return html; return html;
}; };
@@ -1036,45 +1067,52 @@ const Utils = {
); );
const blobUrl = URL.createObjectURL(blob); const blobUrl = URL.createObjectURL(blob);
const downloadAnchorElem = "<a href='" + blobUrl + "' " + const viewFileElem = `<a href='#collapse${i}'
"title='Download " + Utils.escapeHtml(file.fileName) + "' " + class='collapsed'
"download='" + Utils.escapeHtml(file.fileName) + "'>\u21B4</a>"; data-toggle='collapse'
aria-expanded='true'
aria-controls='collapse${i}'
title="Show/hide contents of '${Utils.escapeHtml(file.fileName)}'">&#x1f441;&#xfe0f;</a>`;
const expandFileContentsElem = "<a href='#collapse" + i + "' " + const downloadFileElem = `<a href='${blobUrl}'
"class='collapsed' " + title='Download ${Utils.escapeHtml(file.fileName)}'
"data-toggle='collapse' " + download='${Utils.escapeHtml(file.fileName)}'>&#x1f4be;</a>`;
"aria-expanded='true' " +
"aria-controls='collapse" + i + "' " +
"title=\"Show/hide contents of '" + Utils.escapeHtml(file.fileName) + "'\">&#x1F50D</a>";
const html = "<div class='panel panel-default'>" + const hexFileData = Utils.toHexFast(new Uint8Array(file.bytes));
"<div class='panel-heading' role='tab' id='heading" + i + "'>" +
"<h4 class='panel-title'>" + const switchToInputElem = `<a href='#switchFileToInput${i}'
"<div>" + class='file-switch'
Utils.escapeHtml(file.fileName) + title='Move file to input as hex'
" " + expandFileContentsElem + fileValue='${hexFileData}'>&#x21e7;</a>`;
" " + downloadAnchorElem +
"<span class='pull-right'>" + const html = `<div class='panel panel-default' style='white-space: normal;'>
// These are for formatting when stripping HTML <div class='panel-heading' role='tab' id='heading${i}'>
"<span style='display: none'>\n</span>" + <h4 class='panel-title'>
file.size.toLocaleString() + " bytes" + <div>
"<span style='display: none'>\n</span>" + ${Utils.escapeHtml(file.fileName)}<NL>
"</span>" + ${viewFileElem}<SP>
"</div>" + ${downloadFileElem}<SP>
"</h4>" + ${switchToInputElem}<SP>
"</div>" + <span class='pull-right'>
"<div id='collapse" + i + "' class='panel-collapse collapse' " + <NL>${file.size.toLocaleString()} bytes
"role='tabpanel' aria-labelledby='heading" + i + "'>" + </span>
"<div class='panel-body'>" + </div>
"<pre><code>" + Utils.escapeHtml(file.contents) + "</pre></code></div>" + </h4>
"</div>" + </div>
"</div>"; <div id='collapse${i}' class='panel-collapse collapse'
role='tabpanel' aria-labelledby='heading${i}'>
<div class='panel-body'>
<NL><NL><pre><code>${Utils.escapeHtml(file.contents)}</code></pre>
</div>
</div>
</div>`;
return html; return html;
}; };
let html = "<div style='padding: 5px;'>" + let html = `<div style='padding: 5px; white-space: normal;'>
files.length + ${files.length} file(s) found<NL>
" file(s) found</div>\n"; </div>`;
files.forEach(function(file, i) { files.forEach(function(file, i) {
if (typeof file.contents !== "undefined") { if (typeof file.contents !== "undefined") {
html += formatFile(file, i); html += formatFile(file, i);
@@ -1082,14 +1120,53 @@ const Utils = {
html += formatDirectory(file); html += formatDirectory(file);
} }
}); });
return html;
return html.replace(/(?:(<pre>(?:\n|.)*<\/pre>)|\s{2,})/g, "$1") // Remove whitespace from markup
.replace(/<NL>/g, "\n") // Replace <NP> with newlines
.replace(/<SP>/g, " "); // Replace <SP> with spaces
},
/**
* Parses URI parameters into a JSON object.
*
* @param {string} paramStr - The serialised query or hash section of a URI
* @returns {object}
*
* @example
* // returns {a: 'abc', b: '123'}
* Utils.parseURIParams("?a=abc&b=123")
* Utils.parseURIParams("#a=abc&b=123")
*/
parseURIParams: function(paramStr) {
if (paramStr === "") return {};
// Cut off ? or # and split on &
if (paramStr[0] === "?" ||
paramStr[0] === "#") {
paramStr = paramStr.substr(1);
}
const params = paramStr.split("&");
const result = {};
for (let i = 0; i < params.length; i++) {
const param = params[i].split("=");
if (param.length !== 2) {
result[params[i]] = true;
} else {
result[param[0]] = decodeURIComponent(param[1].replace(/\+/g, " "));
}
}
return result;
}, },
/** /**
* Actual modulo function, since % is actually the remainder function in JS. * Actual modulo function, since % is actually the remainder function in JS.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @returns {number} * @returns {number}
@@ -1102,7 +1179,7 @@ const Utils = {
/** /**
* Finds the greatest common divisor of two numbers. * Finds the greatest common divisor of two numbers.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @returns {number} * @returns {number}
@@ -1118,7 +1195,7 @@ const Utils = {
/** /**
* Finds the modular inverse of two values. * Finds the modular inverse of two values.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @returns {number} * @returns {number}
@@ -1167,7 +1244,8 @@ const Utils = {
"Forward slash": /\//g, "Forward slash": /\//g,
"Backslash": /\\/g, "Backslash": /\\/g,
"0x": /0x/g, "0x": /0x/g,
"\\x": /\\x/g "\\x": /\\x/g,
"None": /\s+/g // Included here to remove whitespace when there shouldn't be any
}, },

View File

@@ -46,6 +46,8 @@ const Categories = [
"From Base58", "From Base58",
"To Base", "To Base",
"From Base", "From Base",
"To BCD",
"From BCD",
"To HTML Entity", "To HTML Entity",
"From HTML Entity", "From HTML Entity",
"URL Encode", "URL Encode",
@@ -61,7 +63,8 @@ const Categories = [
"Hex to PEM", "Hex to PEM",
"Parse ASN.1 hex string", "Parse ASN.1 hex string",
"Change IP format", "Change IP format",
"Text encoding", "Encode text",
"Decode text",
"Swap endianness", "Swap endianness",
] ]
}, },
@@ -88,6 +91,8 @@ const Categories = [
"Vigenère Decode", "Vigenère Decode",
"To Morse Code", "To Morse Code",
"From Morse Code", "From Morse Code",
"Bifid Cipher Encode",
"Bifid Cipher Decode",
"Affine Cipher Encode", "Affine Cipher Encode",
"Affine Cipher Decode", "Affine Cipher Decode",
"Atbash Cipher", "Atbash Cipher",
@@ -117,6 +122,8 @@ const Categories = [
"AND", "AND",
"ADD", "ADD",
"SUB", "SUB",
"Bit shift left",
"Bit shift right",
"Rotate left", "Rotate left",
"Rotate right", "Rotate right",
"ROT13", "ROT13",
@@ -125,6 +132,7 @@ const Categories = [
{ {
name: "Networking", name: "Networking",
ops: [ ops: [
"HTTP request",
"Strip HTTP headers", "Strip HTTP headers",
"Parse User Agent", "Parse User Agent",
"Parse IP range", "Parse IP range",
@@ -143,7 +151,8 @@ const Categories = [
{ {
name: "Language", name: "Language",
ops: [ ops: [
"Text encoding", "Encode text",
"Decode text",
"Unescape Unicode Characters", "Unescape Unicode Characters",
] ]
}, },
@@ -166,7 +175,6 @@ const Categories = [
"Tail", "Tail",
"Count occurrences", "Count occurrences",
"Expand alphabet range", "Expand alphabet range",
"Parse escaped string",
"Drop bytes", "Drop bytes",
"Take bytes", "Take bytes",
"Pad lines", "Pad lines",
@@ -181,6 +189,8 @@ const Categories = [
"Parse UNIX file permissions", "Parse UNIX file permissions",
"Swap endianness", "Swap endianness",
"Parse colour code", "Parse colour code",
"Escape string",
"Unescape string",
] ]
}, },
{ {
@@ -190,6 +200,8 @@ const Categories = [
"Translate DateTime Format", "Translate DateTime Format",
"From UNIX Timestamp", "From UNIX Timestamp",
"To UNIX Timestamp", "To UNIX Timestamp",
"Windows Filetime to UNIX Timestamp",
"UNIX Timestamp to Windows Filetime",
"Extract dates", "Extract dates",
] ]
}, },
@@ -206,7 +218,9 @@ const Categories = [
"Extract dates", "Extract dates",
"Regular expression", "Regular expression",
"XPath expression", "XPath expression",
"JPath expression",
"CSS selector", "CSS selector",
"Extract EXIF",
] ]
}, },
{ {
@@ -233,20 +247,24 @@ const Categories = [
"MD2", "MD2",
"MD4", "MD4",
"MD5", "MD5",
"MD6",
"SHA0", "SHA0",
"SHA1", "SHA1",
"SHA224", "SHA2",
"SHA256",
"SHA384",
"SHA512",
"SHA3", "SHA3",
"RIPEMD-160", "Keccak",
"Shake",
"RIPEMD",
"HAS-160",
"Whirlpool",
"Snefru",
"HMAC", "HMAC",
"Fletcher-8 Checksum", "Fletcher-8 Checksum",
"Fletcher-16 Checksum", "Fletcher-16 Checksum",
"Fletcher-32 Checksum", "Fletcher-32 Checksum",
"Fletcher-64 Checksum", "Fletcher-64 Checksum",
"Adler-32 Checksum", "Adler-32 Checksum",
"CRC-16 Checksum",
"CRC-32 Checksum", "CRC-32 Checksum",
"TCP/IP Checksum", "TCP/IP Checksum",
] ]
@@ -268,7 +286,9 @@ const Categories = [
"CSS Beautify", "CSS Beautify",
"CSS Minify", "CSS Minify",
"XPath expression", "XPath expression",
"JPath expression",
"CSS selector", "CSS selector",
"Microsoft Script Decoder",
"Strip HTML tags", "Strip HTML tags",
"Diff", "Diff",
"To Snake case", "To Snake case",
@@ -283,7 +303,13 @@ const Categories = [
"Frequency distribution", "Frequency distribution",
"Detect File Type", "Detect File Type",
"Scan for Embedded Files", "Scan for Embedded Files",
"Disassemble x86",
"Generate UUID", "Generate UUID",
"Generate TOTP",
"Generate HOTP",
"Render Image",
"Remove EXIF",
"Extract EXIF",
"Numberwang", "Numberwang",
] ]
}, },
@@ -292,6 +318,7 @@ const Categories = [
ops: [ ops: [
"Fork", "Fork",
"Merge", "Merge",
"Register",
"Jump", "Jump",
"Conditional Jump", "Conditional Jump",
"Return", "Return",

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
import CharEnc from "../../operations/CharEnc.js";
/**
* CharEnc module.
*
* Libraries:
* - cptable
* - CryptoJS
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.CharEnc = {
"Encode text": CharEnc.runEncode,
"Decode text": CharEnc.runDecode,
};
export default OpModules;

View File

@@ -0,0 +1,42 @@
import Cipher from "../../operations/Cipher.js";
/**
* Ciphers module.
*
* Libraries:
* - CryptoJS
* - Blowfish
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Ciphers = {
"AES Encrypt": Cipher.runAesEnc,
"AES Decrypt": Cipher.runAesDec,
"Blowfish Encrypt": Cipher.runBlowfishEnc,
"Blowfish Decrypt": Cipher.runBlowfishDec,
"DES Encrypt": Cipher.runDesEnc,
"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,
"Vigenère Encode": Cipher.runVigenereEnc,
"Vigenère Decode": Cipher.runVigenereDec,
"Bifid Cipher Encode": Cipher.runBifidEnc,
"Bifid Cipher Decode": Cipher.runBifidDec,
"Affine Cipher Encode": Cipher.runAffineEnc,
"Affine Cipher Decode": Cipher.runAffineDec,
"Atbash Cipher": Cipher.runAtbash,
"Substitute": Cipher.runSubstitute,
};
export default OpModules;

View File

@@ -0,0 +1,44 @@
import JS from "../../operations/JS.js";
import Code from "../../operations/Code.js";
/**
* Code module.
*
* Libraries:
* - lodash
* - vkbeautify
* - xmldom
* - xpath
* - jpath
* - googlecodeprettify
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Code = {
"JavaScript Parser": JS.runParse,
"JavaScript Beautify": JS.runBeautify,
"JavaScript Minify": JS.runMinify,
"Syntax highlighter": Code.runSyntaxHighlight,
"Generic Code Beautify": Code.runGenericBeautify,
"JSON Beautify": Code.runJsonBeautify,
"JSON Minify": Code.runJsonMinify,
"XML Beautify": Code.runXmlBeautify,
"XML Minify": Code.runXmlMinify,
"SQL Beautify": Code.runSqlBeautify,
"SQL Minify": Code.runSqlMinify,
"CSS Beautify": Code.runCssBeautify,
"CSS Minify": Code.runCssMinify,
"XPath expression": Code.runXpath,
"CSS selector": Code.runCSSQuery,
"To Snake case": Code.runToSnakeCase,
"To Camel case": Code.runToCamelCase,
"To Kebab case": Code.runToKebabCase,
"JPath expression": Code.runJpath,
};
export default OpModules;

View File

@@ -0,0 +1,32 @@
import Compress from "../../operations/Compress.js";
/**
* Compression module.
*
* Libraries:
* - zlib.js
* - bzip2.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Compression = {
"Raw Deflate": Compress.runRawDeflate,
"Raw Inflate": Compress.runRawInflate,
"Zlib Deflate": Compress.runZlibDeflate,
"Zlib Inflate": Compress.runZlibInflate,
"Gzip": Compress.runGzip,
"Gunzip": Compress.runGunzip,
"Zip": Compress.runPkzip,
"Unzip": Compress.runPkunzip,
"Bzip2 Decompress": Compress.runBzip2Decompress,
"Tar": Compress.runTar,
"Untar": Compress.runUntar,
};
export default OpModules;

View File

@@ -0,0 +1,192 @@
import FlowControl from "../../FlowControl.js";
import Base from "../../operations/Base.js";
import Base58 from "../../operations/Base58.js";
import Base64 from "../../operations/Base64.js";
import BCD from "../../operations/BCD.js";
import BitwiseOp from "../../operations/BitwiseOp.js";
import ByteRepr from "../../operations/ByteRepr.js";
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";
import MAC from "../../operations/MAC.js";
import MorseCode from "../../operations/MorseCode.js";
import MS from "../../operations/MS.js";
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 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.
*
* The Default module is for operations that are expected to be very commonly used or
* do not require any libraries. This module is loaded into the app at compile time.
*
* Libraries:
* - Utils.js
* - CryptoJS
* - otp
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Default = {
"To Hexdump": Hexdump.runTo,
"From Hexdump": Hexdump.runFrom,
"To Hex": ByteRepr.runToHex,
"From Hex": ByteRepr.runFromHex,
"To Octal": ByteRepr.runToOct,
"From Octal": ByteRepr.runFromOct,
"To Charcode": ByteRepr.runToCharcode,
"From Charcode": ByteRepr.runFromCharcode,
"To Decimal": ByteRepr.runToDecimal,
"From Decimal": ByteRepr.runFromDecimal,
"To Binary": ByteRepr.runToBinary,
"From Binary": ByteRepr.runFromBinary,
"To Hex Content": ByteRepr.runToHexContent,
"From Hex Content": ByteRepr.runFromHexContent,
"To Base64": Base64.runTo,
"From Base64": Base64.runFrom,
"Show Base64 offsets": Base64.runOffsets,
"To Base32": Base64.runTo32,
"From Base32": Base64.runFrom32,
"To Base58": Base58.runTo,
"From Base58": Base58.runFrom,
"To Base": Base.runTo,
"From Base": Base.runFrom,
"To BCD": BCD.runToBCD,
"From BCD": BCD.runFromBCD,
"To HTML Entity": HTML.runToEntity,
"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,
"Swap endianness": Endian.runSwapEndianness,
"ROT13": Rotate.runRot13,
"ROT47": Rotate.runRot47,
"Rotate left": Rotate.runRotl,
"Rotate right": Rotate.runRotr,
"Bit shift left": BitwiseOp.runBitShiftLeft,
"Bit shift right": BitwiseOp.runBitShiftRight,
"XOR": BitwiseOp.runXor,
"XOR Brute Force": BitwiseOp.runXorBrute,
"OR": BitwiseOp.runXor,
"NOT": BitwiseOp.runNot,
"AND": BitwiseOp.runAnd,
"ADD": BitwiseOp.runAdd,
"SUB": BitwiseOp.runSub,
"To Morse Code": MorseCode.runTo,
"From Morse Code": MorseCode.runFrom,
"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,
"Remove whitespace": Tidy.runRemoveWhitespace,
"Remove null bytes": Tidy.runRemoveNulls,
"Drop bytes": Tidy.runDropBytes,
"Take bytes": Tidy.runTakeBytes,
"Pad lines": Tidy.runPad,
"Reverse": SeqUtils.runReverse,
"Sort": SeqUtils.runSort,
"Unique": SeqUtils.runUnique,
"Count occurrences": SeqUtils.runCount,
"Add line numbers": SeqUtils.runAddLineNumbers,
"Remove line numbers": SeqUtils.runRemoveLineNumbers,
"Expand alphabet range": SeqUtils.runExpandAlphRange,
"Convert distance": Convert.runDistance,
"Convert area": Convert.runArea,
"Convert mass": Convert.runMass,
"Convert speed": Convert.runSpeed,
"Convert data units": Convert.runDataSize,
"Parse UNIX file permissions": OS.runParseUnixPerms,
"Parse DateTime": DateTime.runParse,
"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,
"Detect File Type": FileType.runDetect,
"Scan for Embedded Files": FileType.runScanForEmbeddedFiles,
"Generate UUID": UUID.runGenerateV4,
"Numberwang": Numberwang.run,
"Generate TOTP": OTP.runTOTP,
"Generate HOTP": OTP.runHOTP,
"Fork": FlowControl.runFork,
"Merge": FlowControl.runMerge,
"Register": FlowControl.runRegister,
"Jump": FlowControl.runJump,
"Conditional Jump": FlowControl.runCondJump,
"Return": FlowControl.runReturn,
"Comment": FlowControl.runComment,
/*
Highlighting functions.
This is a temporary solution as highlighting should be entirely
overhauled at some point.
*/
"From Base64-highlight": Base64.highlightFrom,
"From Base64-highlightReverse": Base64.highlightTo,
"To Base64-highlight": Base64.highlightTo,
"To Base64-highlightReverse": Base64.highlightFrom,
"From Hex-highlight": ByteRepr.highlightFrom,
"From Hex-highlightReverse": ByteRepr.highlightTo,
"To Hex-highlight": ByteRepr.highlightTo,
"To Hex-highlightReverse": ByteRepr.highlightFrom,
"From Charcode-highlight": ByteRepr.highlightFrom,
"From Charcode-highlightReverse": ByteRepr.highlightTo,
"To Charcode-highlight": ByteRepr.highlightTo,
"To Charcode-highlightReverse": ByteRepr.highlightFrom,
"From Binary-highlight": ByteRepr.highlightFromBinary,
"From Binary-highlightReverse": ByteRepr.highlightToBinary,
"To Binary-highlight": ByteRepr.highlightToBinary,
"To Binary-highlightReverse": ByteRepr.highlightFromBinary,
"From Hexdump-highlight": Hexdump.highlightFrom,
"From Hexdump-highlightReverse": Hexdump.highlightTo,
"To Hexdump-highlight": Hexdump.highlightTo,
"To Hexdump-highlightReverse": Hexdump.highlightFrom,
};
export default OpModules;

View File

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

View File

@@ -0,0 +1,21 @@
import Punycode from "../../operations/Punycode.js";
/**
* Encodings module.
*
* Libraries:
* - punycode
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Encodings = {
"To Punycode": Punycode.runToAscii,
"From Punycode": Punycode.runToUnicode,
};
export default OpModules;

View File

@@ -0,0 +1,22 @@
import HTTP from "../../operations/HTTP.js";
/**
* HTTP module.
*
* Libraries:
* - UAS_parser
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.HTTP = {
"HTTP request": HTTP.runHTTPRequest,
"Strip HTTP headers": HTTP.runStripHeaders,
"Parse User Agent": HTTP.runParseUserAgent,
};
export default OpModules;

View File

@@ -0,0 +1,48 @@
import Checksum from "../../operations/Checksum.js";
import Hash from "../../operations/Hash.js";
/**
* Hashing module.
*
* Libraries:
* - CryptoApi
* - node-md6
* - js-sha3
* - ./Checksum.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Hashing = {
"Analyse hash": Hash.runAnalyse,
"Generate all hashes": Hash.runAll,
"MD2": Hash.runMD2,
"MD4": Hash.runMD4,
"MD5": Hash.runMD5,
"MD6": Hash.runMD6,
"SHA0": Hash.runSHA0,
"SHA1": Hash.runSHA1,
"SHA2": Hash.runSHA2,
"SHA3": Hash.runSHA3,
"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,
"Fletcher-32 Checksum": Checksum.runFletcher32,
"Fletcher-64 Checksum": Checksum.runFletcher64,
"Adler-32 Checksum": Checksum.runAdler32,
"CRC-16 Checksum": Checksum.runCRC16,
"CRC-32 Checksum": Checksum.runCRC32,
"TCP/IP Checksum": Checksum.runTCPIP,
};
export default OpModules;

View File

@@ -0,0 +1,25 @@
import Image from "../../operations/Image.js";
/**
* Image module.
*
* Libraries:
* - exif-parser
* - remove-exif
* - ./FileType.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Image = {
"Extract EXIF": Image.runExtractEXIF,
"Remove EXIF": Image.runRemoveEXIF,
"Render Image": Image.runRenderImage,
};
export default OpModules;

View File

@@ -0,0 +1,28 @@
import IP from "../../operations/IP.js";
import Filetime from "../../operations/Filetime.js";
/**
* JSBN module.
*
* Libraries:
* - jsbn
* - ./Checksum.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.JSBN = {
"Parse IP range": IP.runParseIpRange,
"Parse IPv6 address": IP.runParseIPv6,
"Parse IPv4 header": IP.runParseIPv4Header,
"Change IP format": IP.runChangeIpFormat,
"Group IP addresses": IP.runGroupIps,
"Windows Filetime to UNIX Timestamp": Filetime.runFromFiletimeToUnix,
"UNIX Timestamp to Windows Filetime": Filetime.runToFiletimeFromUnix,
};
export default OpModules;

View File

@@ -0,0 +1,39 @@
/**
* Imports all modules for builds which do not load modules separately.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import OpModules from "./Default.js";
import CharEncModule from "./CharEnc.js";
import CipherModule from "./Ciphers.js";
import CodeModule from "./Code.js";
import CompressionModule from "./Compression.js";
import DiffModule from "./Diff.js";
import EncodingModule from "./Encodings.js";
import HashingModule from "./Hashing.js";
import HTTPModule from "./HTTP.js";
import ImageModule from "./Image.js";
import JSBNModule from "./JSBN.js";
import PublicKeyModule from "./PublicKey.js";
import ShellcodeModule from "./Shellcode.js";
Object.assign(
OpModules,
CharEncModule,
CipherModule,
CodeModule,
CompressionModule,
DiffModule,
EncodingModule,
HashingModule,
HTTPModule,
ImageModule,
JSBNModule,
PublicKeyModule,
ShellcodeModule
);
export default OpModules;

View File

@@ -0,0 +1,25 @@
import PublicKey from "../../operations/PublicKey.js";
/**
* PublicKey module.
*
* Libraries:
* - jsrsasign
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.PublicKey = {
"Parse X.509 certificate": PublicKey.runParseX509,
"Parse ASN.1 hex string": PublicKey.runParseAsn1HexString,
"PEM to Hex": PublicKey.runPemToHex,
"Hex to PEM": PublicKey.runHexToPem,
"Hex to Object Identifier": PublicKey.runHexToObjectIdentifier,
"Object Identifier to Hex": PublicKey.runObjectIdentifierToHex,
};
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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,515 @@
/* cputils.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ft=javascript: */
/*jshint newcap: false */
(function(root, factory) {
"use strict";
if(typeof cptable === "undefined") {
if(typeof require !== "undefined"){
var cpt = require('./cptable');
if (typeof module !== 'undefined' && module.exports) module.exports = factory(cpt);
else root.cptable = factory(cpt);
} else throw new Error("cptable not found");
} else cptable = factory(cptable);
}(this, function(cpt){
"use strict";
var magic = {
"1200":"utf16le",
"1201":"utf16be",
"12000":"utf32le",
"12001":"utf32be",
"16969":"utf64le",
"20127":"ascii",
"65000":"utf7",
"65001":"utf8"
};
var sbcs_cache = [874,1250,1251,1252,1253,1254,1255,1256,10000];
var dbcs_cache = [932,936,949,950];
var magic_cache = [65001];
var magic_decode = {};
var magic_encode = {};
var cpdcache = {};
var cpecache = {};
var sfcc = function sfcc(x) { return String.fromCharCode(x); };
var cca = function cca(x) { return x.charCodeAt(0); };
var has_buf = (typeof Buffer !== 'undefined');
if(has_buf) {
var mdl = 1024, mdb = new Buffer(mdl);
var make_EE = function make_EE(E){
var EE = new Buffer(65536);
for(var i = 0; i < 65536;++i) EE[i] = 0;
var keys = Object.keys(E), len = keys.length;
for(var ee = 0, e = keys[ee]; ee < len; ++ee) {
if(!(e = keys[ee])) continue;
EE[e.charCodeAt(0)] = E[e];
}
return EE;
};
var sbcs_encode = function make_sbcs_encode(cp) {
var EE = make_EE(cpt[cp].enc);
return function sbcs_e(data, ofmt) {
var len = data.length;
var out, i=0, j=0, D=0, w=0;
if(typeof data === 'string') {
out = new Buffer(len);
for(i = 0; i < len; ++i) out[i] = EE[data.charCodeAt(i)];
} else if(Buffer.isBuffer(data)) {
out = new Buffer(2*len);
j = 0;
for(i = 0; i < len; ++i) {
D = data[i];
if(D < 128) out[j++] = EE[D];
else if(D < 224) { out[j++] = EE[((D&31)<<6)+(data[i+1]&63)]; ++i; }
else if(D < 240) { out[j++] = EE[((D&15)<<12)+((data[i+1]&63)<<6)+(data[i+2]&63)]; i+=2; }
else {
w = ((D&7)<<18)+((data[i+1]&63)<<12)+((data[i+2]&63)<<6)+(data[i+3]&63); i+=3;
if(w < 65536) out[j++] = EE[w];
else { w -= 65536; out[j++] = EE[0xD800 + ((w>>10)&1023)]; out[j++] = EE[0xDC00 + (w&1023)]; }
}
}
out = out.slice(0,j);
} else {
out = new Buffer(len);
for(i = 0; i < len; ++i) out[i] = EE[data[i].charCodeAt(0)];
}
if(!ofmt || ofmt === 'buf') return out;
if(ofmt !== 'arr') return out.toString('binary');
return [].slice.call(out);
};
};
var sbcs_decode = function make_sbcs_decode(cp) {
var D = cpt[cp].dec;
var DD = new Buffer(131072), d=0, c="";
for(d=0;d<D.length;++d) {
if(!(c=D[d])) continue;
var w = c.charCodeAt(0);
DD[2*d] = w&255; DD[2*d+1] = w>>8;
}
return function sbcs_d(data) {
var len = data.length, i=0, j=0;
if(2 * len > mdl) { mdl = 2 * len; mdb = new Buffer(mdl); }
if(Buffer.isBuffer(data)) {
for(i = 0; i < len; i++) {
j = 2*data[i];
mdb[2*i] = DD[j]; mdb[2*i+1] = DD[j+1];
}
} else if(typeof data === "string") {
for(i = 0; i < len; i++) {
j = 2*data.charCodeAt(i);
mdb[2*i] = DD[j]; mdb[2*i+1] = DD[j+1];
}
} else {
for(i = 0; i < len; i++) {
j = 2*data[i];
mdb[2*i] = DD[j]; mdb[2*i+1] = DD[j+1];
}
}
return mdb.slice(0, 2 * len).toString('ucs2');
};
};
var dbcs_encode = function make_dbcs_encode(cp) {
var E = cpt[cp].enc;
var EE = new Buffer(131072);
for(var i = 0; i < 131072; ++i) EE[i] = 0;
var keys = Object.keys(E);
for(var ee = 0, e = keys[ee]; ee < keys.length; ++ee) {
if(!(e = keys[ee])) continue;
var f = e.charCodeAt(0);
EE[2*f] = E[e] & 255; EE[2*f+1] = E[e]>>8;
}
return function dbcs_e(data, ofmt) {
var len = data.length, out = new Buffer(2*len), i=0, j=0, jj=0, k=0, D=0;
if(typeof data === 'string') {
for(i = k = 0; i < len; ++i) {
j = data.charCodeAt(i)*2;
out[k++] = EE[j+1] || EE[j]; if(EE[j+1] > 0) out[k++] = EE[j];
}
out = out.slice(0,k);
} else if(Buffer.isBuffer(data)) {
for(i = k = 0; i < len; ++i) {
D = data[i];
if(D < 128) j = D;
else if(D < 224) { j = ((D&31)<<6)+(data[i+1]&63); ++i; }
else if(D < 240) { j = ((D&15)<<12)+((data[i+1]&63)<<6)+(data[i+2]&63); i+=2; }
else { j = ((D&7)<<18)+((data[i+1]&63)<<12)+((data[i+2]&63)<<6)+(data[i+3]&63); i+=3; }
if(j<65536) { j*=2; out[k++] = EE[j+1] || EE[j]; if(EE[j+1] > 0) out[k++] = EE[j]; }
else { jj = j-65536;
j=2*(0xD800 + ((jj>>10)&1023)); out[k++] = EE[j+1] || EE[j]; if(EE[j+1] > 0) out[k++] = EE[j];
j=2*(0xDC00 + (jj&1023)); out[k++] = EE[j+1] || EE[j]; if(EE[j+1] > 0) out[k++] = EE[j];
}
}
out = out.slice(0,k);
} else {
for(i = k = 0; i < len; i++) {
j = data[i].charCodeAt(0)*2;
out[k++] = EE[j+1] || EE[j]; if(EE[j+1] > 0) out[k++] = EE[j];
}
}
if(!ofmt || ofmt === 'buf') return out;
if(ofmt !== 'arr') return out.toString('binary');
return [].slice.call(out);
};
};
var dbcs_decode = function make_dbcs_decode(cp) {
var D = cpt[cp].dec;
var DD = new Buffer(131072), d=0, c, w=0, j=0, i=0;
for(i = 0; i < 65536; ++i) { DD[2*i] = 0xFF; DD[2*i+1] = 0xFD;}
for(d = 0; d < D.length; ++d) {
if(!(c=D[d])) continue;
w = c.charCodeAt(0);
j = 2*d;
DD[j] = w&255; DD[j+1] = w>>8;
}
return function dbcs_d(data) {
var len = data.length, out = new Buffer(2*len), i=0, j=0, k=0;
if(Buffer.isBuffer(data)) {
for(i = 0; i < len; i++) {
j = 2*data[i];
if(DD[j]===0xFF && DD[j+1]===0xFD) { j=2*((data[i]<<8)+data[i+1]); ++i; }
out[k++] = DD[j]; out[k++] = DD[j+1];
}
} else if(typeof data === "string") {
for(i = 0; i < len; i++) {
j = 2*data.charCodeAt(i);
if(DD[j]===0xFF && DD[j+1]===0xFD) { j=2*((data.charCodeAt(i)<<8)+data.charCodeAt(i+1)); ++i; }
out[k++] = DD[j]; out[k++] = DD[j+1];
}
} else {
for(i = 0; i < len; i++) {
j = 2*data[i];
if(DD[j]===0xFF && DD[j+1]===0xFD) { j=2*((data[i]<<8)+data[i+1]); ++i; }
out[k++] = DD[j]; out[k++] = DD[j+1];
}
}
return out.slice(0,k).toString('ucs2');
};
};
magic_decode[65001] = function utf8_d(data) {
if(typeof data === "string") return utf8_d(data.split("").map(cca));
var len = data.length, w = 0, ww = 0;
if(4 * len > mdl) { mdl = 4 * len; mdb = new Buffer(mdl); }
var i = 0;
if(len >= 3 && data[0] == 0xEF) if(data[1] == 0xBB && data[2] == 0xBF) i = 3;
for(var j = 1, k = 0, D = 0; i < len; i+=j) {
j = 1; D = data[i];
if(D < 128) w = D;
else if(D < 224) { w=(D&31)*64+(data[i+1]&63); j=2; }
else if(D < 240) { w=((D&15)<<12)+(data[i+1]&63)*64+(data[i+2]&63); j=3; }
else { w=(D&7)*262144+((data[i+1]&63)<<12)+(data[i+2]&63)*64+(data[i+3]&63); j=4; }
if(w < 65536) { mdb[k++] = w&255; mdb[k++] = w>>8; }
else {
w -= 65536; ww = 0xD800 + ((w>>10)&1023); w = 0xDC00 + (w&1023);
mdb[k++] = ww&255; mdb[k++] = ww>>>8; mdb[k++] = w&255; mdb[k++] = (w>>>8)&255;
}
}
return mdb.slice(0,k).toString('ucs2');
};
magic_encode[65001] = function utf8_e(data, ofmt) {
if(has_buf && Buffer.isBuffer(data)) {
if(!ofmt || ofmt === 'buf') return data;
if(ofmt !== 'arr') return data.toString('binary');
return [].slice.call(data);
}
var len = data.length, w = 0, ww = 0, j = 0;
var direct = typeof data === "string";
if(4 * len > mdl) { mdl = 4 * len; mdb = new Buffer(mdl); }
for(var i = 0; i < len; ++i) {
w = direct ? data.charCodeAt(i) : data[i].charCodeAt(0);
if(w <= 0x007F) mdb[j++] = w;
else if(w <= 0x07FF) {
mdb[j++] = 192 + (w >> 6);
mdb[j++] = 128 + (w&63);
} else if(w >= 0xD800 && w <= 0xDFFF) {
w -= 0xD800; ++i;
ww = (direct ? data.charCodeAt(i) : data[i].charCodeAt(0)) - 0xDC00 + (w << 10);
mdb[j++] = 240 + ((ww>>>18) & 0x07);
mdb[j++] = 144 + ((ww>>>12) & 0x3F);
mdb[j++] = 128 + ((ww>>>6) & 0x3F);
mdb[j++] = 128 + (ww & 0x3F);
} else {
mdb[j++] = 224 + (w >> 12);
mdb[j++] = 128 + ((w >> 6)&63);
mdb[j++] = 128 + (w&63);
}
}
if(!ofmt || ofmt === 'buf') return mdb.slice(0,j);
if(ofmt !== 'arr') return mdb.slice(0,j).toString('binary');
return [].slice.call(mdb, 0, j);
};
}
var encache = function encache() {
if(has_buf) {
if(cpdcache[sbcs_cache[0]]) return;
var i=0, s=0;
for(i = 0; i < sbcs_cache.length; ++i) {
s = sbcs_cache[i];
if(cpt[s]) {
cpdcache[s] = sbcs_decode(s);
cpecache[s] = sbcs_encode(s);
}
}
for(i = 0; i < dbcs_cache.length; ++i) {
s = dbcs_cache[i];
if(cpt[s]) {
cpdcache[s] = dbcs_decode(s);
cpecache[s] = dbcs_encode(s);
}
}
for(i = 0; i < magic_cache.length; ++i) {
s = magic_cache[i];
if(magic_decode[s]) cpdcache[s] = magic_decode[s];
if(magic_encode[s]) cpecache[s] = magic_encode[s];
}
}
};
var null_enc = function(data, ofmt) { return ""; };
var cp_decache = function cp_decache(cp) { delete cpdcache[cp]; delete cpecache[cp]; };
var decache = function decache() {
if(has_buf) {
if(!cpdcache[sbcs_cache[0]]) return;
sbcs_cache.forEach(cp_decache);
dbcs_cache.forEach(cp_decache);
magic_cache.forEach(cp_decache);
}
last_enc = null_enc; last_cp = 0;
};
var cache = {
encache: encache,
decache: decache,
sbcs: sbcs_cache,
dbcs: dbcs_cache
};
encache();
var BM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var SetD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'(),-./:?";
var last_enc = null_enc, last_cp = 0;
var encode = function encode(cp, data, ofmt) {
if(cp === last_cp && last_enc) { return last_enc(data, ofmt); }
if(cpecache[cp]) { last_enc = cpecache[last_cp=cp]; return last_enc(data, ofmt); }
if(has_buf && Buffer.isBuffer(data)) data = data.toString('utf8');
var len = data.length;
var out = has_buf ? new Buffer(4*len) : [], w=0, i=0, j = 0, ww=0;
var C = cpt[cp], E, M = "";
var isstr = typeof data === 'string';
if(C && (E=C.enc)) for(i = 0; i < len; ++i, ++j) {
w = E[isstr? data.charAt(i) : data[i]];
if(w > 255) {
out[j] = w>>8;
out[++j] = w&255;
} else out[j] = w&255;
}
else if((M=magic[cp])) switch(M) {
case "utf8":
if(has_buf && isstr) { out = new Buffer(data, M); j = out.length; break; }
for(i = 0; i < len; ++i, ++j) {
w = isstr ? data.charCodeAt(i) : data[i].charCodeAt(0);
if(w <= 0x007F) out[j] = w;
else if(w <= 0x07FF) {
out[j] = 192 + (w >> 6);
out[++j] = 128 + (w&63);
} else if(w >= 0xD800 && w <= 0xDFFF) {
w -= 0xD800;
ww = (isstr ? data.charCodeAt(++i) : data[++i].charCodeAt(0)) - 0xDC00 + (w << 10);
out[j] = 240 + ((ww>>>18) & 0x07);
out[++j] = 144 + ((ww>>>12) & 0x3F);
out[++j] = 128 + ((ww>>>6) & 0x3F);
out[++j] = 128 + (ww & 0x3F);
} else {
out[j] = 224 + (w >> 12);
out[++j] = 128 + ((w >> 6)&63);
out[++j] = 128 + (w&63);
}
}
break;
case "ascii":
if(has_buf && typeof data === "string") { out = new Buffer(data, M); j = out.length; break; }
for(i = 0; i < len; ++i, ++j) {
w = isstr ? data.charCodeAt(i) : data[i].charCodeAt(0);
if(w <= 0x007F) out[j] = w;
else throw new Error("bad ascii " + w);
}
break;
case "utf16le":
if(has_buf && typeof data === "string") { out = new Buffer(data, M); j = out.length; break; }
for(i = 0; i < len; ++i) {
w = isstr ? data.charCodeAt(i) : data[i].charCodeAt(0);
out[j++] = w&255;
out[j++] = w>>8;
}
break;
case "utf16be":
for(i = 0; i < len; ++i) {
w = isstr ? data.charCodeAt(i) : data[i].charCodeAt(0);
out[j++] = w>>8;
out[j++] = w&255;
}
break;
case "utf32le":
for(i = 0; i < len; ++i) {
w = isstr ? data.charCodeAt(i) : data[i].charCodeAt(0);
if(w >= 0xD800 && w <= 0xDFFF) w = 0x10000 + ((w - 0xD800) << 10) + (data[++i].charCodeAt(0) - 0xDC00);
out[j++] = w&255; w >>= 8;
out[j++] = w&255; w >>= 8;
out[j++] = w&255; w >>= 8;
out[j++] = w&255;
}
break;
case "utf32be":
for(i = 0; i < len; ++i) {
w = isstr ? data.charCodeAt(i) : data[i].charCodeAt(0);
if(w >= 0xD800 && w <= 0xDFFF) w = 0x10000 + ((w - 0xD800) << 10) + (data[++i].charCodeAt(0) - 0xDC00);
out[j+3] = w&255; w >>= 8;
out[j+2] = w&255; w >>= 8;
out[j+1] = w&255; w >>= 8;
out[j] = w&255;
j+=4;
}
break;
case "utf7":
for(i = 0; i < len; i++) {
var c = isstr ? data.charAt(i) : data[i].charAt(0);
if(c === "+") { out[j++] = 0x2b; out[j++] = 0x2d; continue; }
if(SetD.indexOf(c) > -1) { out[j++] = c.charCodeAt(0); continue; }
var tt = encode(1201, c);
out[j++] = 0x2b;
out[j++] = BM.charCodeAt(tt[0]>>2);
out[j++] = BM.charCodeAt(((tt[0]&0x03)<<4) + ((tt[1]||0)>>4));
out[j++] = BM.charCodeAt(((tt[1]&0x0F)<<2) + ((tt[2]||0)>>6));
out[j++] = 0x2d;
}
break;
default: throw new Error("Unsupported magic: " + cp + " " + magic[cp]);
}
else throw new Error("Unrecognized CP: " + cp);
out = out.slice(0,j);
if(!has_buf) return (ofmt == 'str') ? (out).map(sfcc).join("") : out;
if(!ofmt || ofmt === 'buf') return out;
if(ofmt !== 'arr') return out.toString('binary');
return [].slice.call(out);
};
var decode = function decode(cp, data) {
var F; if((F=cpdcache[cp])) return F(data);
if(typeof data === "string") return decode(cp, data.split("").map(cca));
var len = data.length, out = new Array(len), s="", w=0, i=0, j=1, k=0, ww=0;
var C = cpt[cp], D, M="";
if(C && (D=C.dec)) {
for(i = 0; i < len; i+=j) {
j = 2;
s = D[(data[i]<<8)+ data[i+1]];
if(!s) {
j = 1;
s = D[data[i]];
}
if(!s) throw new Error('Unrecognized code: ' + data[i] + ' ' + data[i+j-1] + ' ' + i + ' ' + j + ' ' + D[data[i]]);
out[k++] = s;
}
}
else if((M=magic[cp])) switch(M) {
case "utf8":
if(len >= 3 && data[0] == 0xEF) if(data[1] == 0xBB && data[2] == 0xBF) i = 3;
for(; i < len; i+=j) {
j = 1;
if(data[i] < 128) w = data[i];
else if(data[i] < 224) { w=(data[i]&31)*64+(data[i+1]&63); j=2; }
else if(data[i] < 240) { w=((data[i]&15)<<12)+(data[i+1]&63)*64+(data[i+2]&63); j=3; }
else { w=(data[i]&7)*262144+((data[i+1]&63)<<12)+(data[i+2]&63)*64+(data[i+3]&63); j=4; }
if(w < 65536) { out[k++] = String.fromCharCode(w); }
else {
w -= 65536; ww = 0xD800 + ((w>>10)&1023); w = 0xDC00 + (w&1023);
out[k++] = String.fromCharCode(ww); out[k++] = String.fromCharCode(w);
}
}
break;
case "ascii":
if(has_buf && Buffer.isBuffer(data)) return data.toString(M);
for(i = 0; i < len; i++) out[i] = String.fromCharCode(data[i]);
k = len; break;
case "utf16le":
if(len >= 2 && data[0] == 0xFF) if(data[1] == 0xFE) i = 2;
if(has_buf && Buffer.isBuffer(data)) return data.toString(M);
j = 2;
for(; i+1 < len; i+=j) {
out[k++] = String.fromCharCode((data[i+1]<<8) + data[i]);
}
break;
case "utf16be":
if(len >= 2 && data[0] == 0xFE) if(data[1] == 0xFF) i = 2;
j = 2;
for(; i+1 < len; i+=j) {
out[k++] = String.fromCharCode((data[i]<<8) + data[i+1]);
}
break;
case "utf32le":
if(len >= 4 && data[0] == 0xFF) if(data[1] == 0xFE && data[2] === 0 && data[3] === 0) i = 4;
j = 4;
for(; i < len; i+=j) {
w = (data[i+3]<<24) + (data[i+2]<<16) + (data[i+1]<<8) + (data[i]);
if(w > 0xFFFF) {
w -= 0x10000;
out[k++] = String.fromCharCode(0xD800 + ((w >> 10) & 0x3FF));
out[k++] = String.fromCharCode(0xDC00 + (w & 0x3FF));
}
else out[k++] = String.fromCharCode(w);
}
break;
case "utf32be":
if(len >= 4 && data[3] == 0xFF) if(data[2] == 0xFE && data[1] === 0 && data[0] === 0) i = 4;
j = 4;
for(; i < len; i+=j) {
w = (data[i]<<24) + (data[i+1]<<16) + (data[i+2]<<8) + (data[i+3]);
if(w > 0xFFFF) {
w -= 0x10000;
out[k++] = String.fromCharCode(0xD800 + ((w >> 10) & 0x3FF));
out[k++] = String.fromCharCode(0xDC00 + (w & 0x3FF));
}
else out[k++] = String.fromCharCode(w);
}
break;
case "utf7":
if(len >= 4 && data[0] == 0x2B && data[1] == 0x2F && data[2] == 0x76) {
if(len >= 5 && data[3] == 0x38 && data[4] == 0x2D) i = 5;
else if(data[3] == 0x38 || data[3] == 0x39 || data[3] == 0x2B || data[3] == 0x2F) i = 4;
}
for(; i < len; i+=j) {
if(data[i] !== 0x2b) { j=1; out[k++] = String.fromCharCode(data[i]); continue; }
j=1;
if(data[i+1] === 0x2d) { j = 2; out[k++] = "+"; continue; }
while(String.fromCharCode(data[i+j]).match(/[A-Za-z0-9+\/]/)) j++;
var dash = 0;
if(data[i+j] === 0x2d) { ++j; dash=1; }
var tt = [];
var o64 = "";
var c1=0, c2=0, c3=0;
var e1=0, e2=0, e3=0, e4=0;
for(var l = 1; l < j - dash;) {
e1 = BM.indexOf(String.fromCharCode(data[i+l++]));
e2 = BM.indexOf(String.fromCharCode(data[i+l++]));
c1 = e1 << 2 | e2 >> 4;
tt.push(c1);
e3 = BM.indexOf(String.fromCharCode(data[i+l++]));
if(e3 === -1) break;
c2 = (e2 & 15) << 4 | e3 >> 2;
tt.push(c2);
e4 = BM.indexOf(String.fromCharCode(data[i+l++]));
if(e4 === -1) break;
c3 = (e3 & 3) << 6 | e4;
if(e4 < 64) tt.push(c3);
}
o64 = decode(1201, tt);
for(l = 0; l < o64.length; ++l) out[k++] = o64.charAt(l);
}
break;
default: throw new Error("Unsupported magic: " + cp + " " + magic[cp]);
}
else throw new Error("Unrecognized CP: " + cp);
return out.slice(0,k).join("");
};
var hascp = function hascp(cp) { return !!(cpt[cp] || magic[cp]); };
cpt.utils = { decode: decode, encode: encode, hascp: hascp, magic: magic, cache:cache };
return cpt;
}));

153
src/core/lib/remove-exif.js Normal file
View File

@@ -0,0 +1,153 @@
/* piexifjs
The MIT License (MIT)
Copyright (c) 2014, 2015 hMatoba(https://github.com/hMatoba)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import Utils from "../Utils.js";
// Param jpeg should be a binaryArray
function removeEXIF(jpeg) {
// Convert binaryArray to char string
jpeg = Utils.byteArrayToChars(jpeg);
if (jpeg.slice(0, 2) != "\xff\xd8") {
throw ("Given data is not jpeg.");
}
var segments = splitIntoSegments(jpeg);
if (segments[1].slice(0, 2) == "\xff\xe1" &&
segments[1].slice(4, 10) == "Exif\x00\x00") {
segments = [segments[0]].concat(segments.slice(2));
} else if (segments[2].slice(0, 2) == "\xff\xe1" &&
segments[2].slice(4, 10) == "Exif\x00\x00") {
segments = segments.slice(0, 2).concat(segments.slice(3));
} else {
throw ("Exif not found.");
}
var new_data = segments.join("");
// Convert back to binaryArray
new_data = Utils.strToCharcode(new_data);
return new_data;
};
function splitIntoSegments(data) {
if (data.slice(0, 2) != "\xff\xd8") {
throw ("Given data isn't JPEG.");
}
var head = 2;
var segments = ["\xff\xd8"];
while (true) {
if (data.slice(head, head + 2) == "\xff\xda") {
segments.push(data.slice(head));
break;
} else {
var length = unpack(">H", data.slice(head + 2, head + 4))[0];
var endPoint = head + length + 2;
segments.push(data.slice(head, endPoint));
head = endPoint;
}
if (head >= data.length) {
throw ("Wrong JPEG data.");
}
}
return segments;
}
function unpack(mark, str) {
if (typeof(str) != "string") {
throw ("'unpack' error. Got invalid type argument.");
}
var l = 0;
for (var markPointer = 1; markPointer < mark.length; markPointer++) {
if (mark[markPointer].toLowerCase() == "b") {
l += 1;
} else if (mark[markPointer].toLowerCase() == "h") {
l += 2;
} else if (mark[markPointer].toLowerCase() == "l") {
l += 4;
} else {
throw ("'unpack' error. Got invalid mark.");
}
}
if (l != str.length) {
throw ("'unpack' error. Mismatch between symbol and string length. " + l + ":" + str.length);
}
var littleEndian;
if (mark[0] == "<") {
littleEndian = true;
} else if (mark[0] == ">") {
littleEndian = false;
} else {
throw ("'unpack' error.");
}
var unpacked = [];
var strPointer = 0;
var p = 1;
var val = null;
var c = null;
var length = null;
var sliced = "";
while (c = mark[p]) {
if (c.toLowerCase() == "b") {
length = 1;
sliced = str.slice(strPointer, strPointer + length);
val = sliced.charCodeAt(0);
if ((c == "b") && (val >= 0x80)) {
val -= 0x100;
}
} else if (c == "H") {
length = 2;
sliced = str.slice(strPointer, strPointer + length);
if (littleEndian) {
sliced = sliced.split("").reverse().join("");
}
val = sliced.charCodeAt(0) * 0x100 +
sliced.charCodeAt(1);
} else if (c.toLowerCase() == "l") {
length = 4;
sliced = str.slice(strPointer, strPointer + length);
if (littleEndian) {
sliced = sliced.split("").reverse().join("");
}
val = sliced.charCodeAt(0) * 0x1000000 +
sliced.charCodeAt(1) * 0x10000 +
sliced.charCodeAt(2) * 0x100 +
sliced.charCodeAt(3);
if ((c == "l") && (val >= 0x80000000)) {
val -= 0x100000000;
}
} else {
throw ("'unpack' error. " + c);
}
unpacked.push(val);
strPointer += length;
p += 1;
}
return unpacked;
}
export default removeEXIF;

214
src/core/operations/BCD.js Executable file
View File

@@ -0,0 +1,214 @@
import Utils from "../Utils.js";
/**
* Binary-Coded Decimal operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const BCD = {
/**
* @constant
* @default
*/
ENCODING_SCHEME: [
"8 4 2 1",
"7 4 2 1",
"4 2 2 1",
"2 4 2 1",
"8 4 -2 -1",
"Excess-3",
"IBM 8 4 2 1",
],
/**
* Lookup table for the binary value of each digit representation.
*
* I wrote a very nice algorithm to generate 8 4 2 1 encoding programatically,
* but unfortunately it's much easier (if less elegant) to use lookup tables
* when supporting multiple encoding schemes.
*
* "Practicality beats purity" - PEP 20
*
* In some schemes it is possible to represent the same value in multiple ways.
* For instance, in 4 2 2 1 encoding, 0100 and 0010 both represent 2. Support
* has not yet been added for this.
*
* @constant
*/
ENCODING_LOOKUP: {
"8 4 2 1": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"7 4 2 1": [0, 1, 2, 3, 4, 5, 6, 8, 9, 10],
"4 2 2 1": [0, 1, 4, 5, 8, 9, 12, 13, 14, 15],
"2 4 2 1": [0, 1, 2, 3, 4, 11, 12, 13, 14, 15],
"8 4 -2 -1": [0, 7, 6, 5, 4, 11, 10, 9, 8, 15],
"Excess-3": [3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
"IBM 8 4 2 1": [10, 1, 2, 3, 4, 5, 6, 7, 8, 9],
},
/**
* @default
* @constant
*/
FORMAT: ["Nibbles", "Bytes", "Raw"],
/**
* To BCD operation.
*
* @param {number} input
* @param {Object[]} args
* @returns {string}
*/
runToBCD: function(input, args) {
if (isNaN(input))
return "Invalid input";
if (Math.floor(input) !== input)
return "Fractional values are not supported by BCD";
const encoding = BCD.ENCODING_LOOKUP[args[0]],
packed = args[1],
signed = args[2],
outputFormat = args[3];
// Split input number up into separate digits
const digits = input.toString().split("");
if (digits[0] === "-" || digits[0] === "+") {
digits.shift();
}
let nibbles = [];
digits.forEach(d => {
const n = parseInt(d, 10);
nibbles.push(encoding[n]);
});
if (signed) {
if (packed && digits.length % 2 === 0) {
// If there are an even number of digits, we add a leading 0 so
// that the sign nibble doesn't sit in its own byte, leading to
// ambiguity around whether the number ends with a 0 or not.
nibbles.unshift(encoding[0]);
}
nibbles.push(input > 0 ? 12 : 13);
// 12 ("C") for + (credit)
// 13 ("D") for - (debit)
}
let bytes = [];
if (packed) {
let encoded = 0,
little = false;
nibbles.forEach(n => {
encoded ^= little ? n : (n << 4);
if (little) {
bytes.push(encoded);
encoded = 0;
}
little = !little;
});
if (little) bytes.push(encoded);
} else {
bytes = nibbles;
// Add null high nibbles
nibbles = nibbles.map(n => {
return [0, n];
}).reduce((a, b) => {
return a.concat(b);
});
}
// Output
switch (outputFormat) {
case "Nibbles":
return nibbles.map(n => {
return Utils.padLeft(n.toString(2), 4);
}).join(" ");
case "Bytes":
return bytes.map(b => {
return Utils.padLeft(b.toString(2), 8);
}).join(" ");
case "Raw":
default:
return Utils.byteArrayToChars(bytes);
}
},
/**
* From BCD operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
*/
runFromBCD: function(input, args) {
const encoding = BCD.ENCODING_LOOKUP[args[0]],
packed = args[1],
signed = args[2],
inputFormat = args[3];
let nibbles = [],
output = "",
byteArray;
// Normalise the input
switch (inputFormat) {
case "Nibbles":
case "Bytes":
input = input.replace(/\s/g, "");
for (let i = 0; i < input.length; i += 4) {
nibbles.push(parseInt(input.substr(i, 4), 2));
}
break;
case "Raw":
default:
byteArray = Utils.strToByteArray(input);
byteArray.forEach(b => {
nibbles.push(b >>> 4);
nibbles.push(b & 15);
});
break;
}
if (!packed) {
// Discard each high nibble
for (let i = 0; i < nibbles.length; i++) {
nibbles.splice(i, 1);
}
}
if (signed) {
const sign = nibbles.pop();
if (sign === 13 ||
sign === 11) {
// Negative
output += "-";
}
}
nibbles.forEach(n => {
if (isNaN(n)) throw "Invalid input";
let val = encoding.indexOf(n);
if (val < 0) throw `Value ${Utils.bin(n, 4)} not in encoding scheme`;
output += val.toString();
});
return parseInt(output, 10);
},
};
export default BCD;

View File

@@ -221,15 +221,15 @@ const Base64 = {
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" + offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -2)) + "'>" + Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -2)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset0.substr(offset0.length - 3, 1) + "</span>" + "<span class='hl5'>" + offset0.substr(offset0.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset0.substr(offset0.length - 2) + "</span>"; "<span class='hl3'>" + offset0.substr(offset0.length - 2) + "</span>";
} else if (len0 % 4 === 3) { } else if (len0 % 4 === 3) {
staticSection = offset0.slice(0, -2); staticSection = offset0.slice(0, -2);
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" + offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -1)) + "'>" + Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -1)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset0.substr(offset0.length - 2, 1) + "</span>" + "<span class='hl5'>" + offset0.substr(offset0.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset0.substr(offset0.length - 1) + "</span>"; "<span class='hl3'>" + offset0.substr(offset0.length - 1) + "</span>";
} else { } else {
staticSection = offset0; staticSection = offset0;
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" + offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
@@ -243,23 +243,23 @@ const Base64 = {
// Highlight offset 1 // Highlight offset 1
padding = "<span class='hlred'>" + offset1.substr(0, 1) + "</span>" + padding = "<span class='hl3'>" + offset1.substr(0, 1) + "</span>" +
"<span class='hlgreen'>" + offset1.substr(1, 1) + "</span>"; "<span class='hl5'>" + offset1.substr(1, 1) + "</span>";
offset1 = offset1.substr(2); offset1 = offset1.substr(2);
if (len1 % 4 === 2) { if (len1 % 4 === 2) {
staticSection = offset1.slice(0, -3); staticSection = offset1.slice(0, -3);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -2)) + "'>" + Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -2)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset1.substr(offset1.length - 3, 1) + "</span>" + "<span class='hl5'>" + offset1.substr(offset1.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset1.substr(offset1.length - 2) + "</span>"; "<span class='hl3'>" + offset1.substr(offset1.length - 2) + "</span>";
} else if (len1 % 4 === 3) { } else if (len1 % 4 === 3) {
staticSection = offset1.slice(0, -2); staticSection = offset1.slice(0, -2);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -1)) + "'>" + Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -1)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset1.substr(offset1.length - 2, 1) + "</span>" + "<span class='hl5'>" + offset1.substr(offset1.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset1.substr(offset1.length - 1) + "</span>"; "<span class='hl3'>" + offset1.substr(offset1.length - 1) + "</span>";
} else { } else {
staticSection = offset1; staticSection = offset1;
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
@@ -272,23 +272,23 @@ const Base64 = {
} }
// Highlight offset 2 // Highlight offset 2
padding = "<span class='hlred'>" + offset2.substr(0, 2) + "</span>" + padding = "<span class='hl3'>" + offset2.substr(0, 2) + "</span>" +
"<span class='hlgreen'>" + offset2.substr(2, 1) + "</span>"; "<span class='hl5'>" + offset2.substr(2, 1) + "</span>";
offset2 = offset2.substr(3); offset2 = offset2.substr(3);
if (len2 % 4 === 2) { if (len2 % 4 === 2) {
staticSection = offset2.slice(0, -3); staticSection = offset2.slice(0, -3);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" + Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset2.substr(offset2.length - 3, 1) + "</span>" + "<span class='hl5'>" + offset2.substr(offset2.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset2.substr(offset2.length - 2) + "</span>"; "<span class='hl3'>" + offset2.substr(offset2.length - 2) + "</span>";
} else if (len2 % 4 === 3) { } else if (len2 % 4 === 3) {
staticSection = offset2.slice(0, -2); staticSection = offset2.slice(0, -2);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" + Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset2.substr(offset2.length - 2, 1) + "</span>" + "<span class='hl5'>" + offset2.substr(offset2.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset2.substr(offset2.length - 1) + "</span>"; "<span class='hl3'>" + offset2.substr(offset2.length - 1) + "</span>";
} else { } else {
staticSection = offset2; staticSection = offset2;
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
@@ -300,8 +300,8 @@ const Base64 = {
offset2 = staticSection; offset2 = staticSection;
} }
return (showVariable ? "Characters highlighted in <span class='hlgreen'>green</span> could change if the input is surrounded by more data." + return (showVariable ? "Characters highlighted in <span class='hl5'>green</span> could change if the input is surrounded by more data." +
"\nCharacters highlighted in <span class='hlred'>red</span> are for padding purposes only." + "\nCharacters highlighted in <span class='hl3'>red</span> are for padding purposes only." +
"\nUnhighlighted characters are <span data-toggle='tooltip' data-placement='top' title='Tooltip on left'>static</span>." + "\nUnhighlighted characters are <span data-toggle='tooltip' data-placement='top' title='Tooltip on left'>static</span>." +
"\nHover over the static sections to see what they decode to on their own.\n" + "\nHover over the static sections to see what they decode to on their own.\n" +
"\nOffset 0: " + offset0 + "\nOffset 0: " + offset0 +

View File

@@ -1,5 +1,4 @@
import Utils from "../Utils.js"; import Utils from "../Utils.js";
import CryptoJS from "crypto-js";
/** /**
@@ -36,7 +35,9 @@ const BitwiseOp = {
o = input[i]; o = input[i];
x = nullPreserving && (o === 0 || o === k) ? o : func(o, k); x = nullPreserving && (o === 0 || o === k) ? o : func(o, k);
result.push(x); result.push(x);
if (scheme !== "Standard" && !(nullPreserving && (o === 0 || o === k))) { if (scheme &&
scheme !== "Standard" &&
!(nullPreserving && (o === 0 || o === k))) {
switch (scheme) { switch (scheme) {
case "Input differential": case "Input differential":
key[i % key.length] = x; key[i % key.length] = x;
@@ -90,7 +91,7 @@ const BitwiseOp = {
* @constant * @constant
* @default * @default
*/ */
XOR_BRUTE_KEY_LENGTH: ["1", "2"], XOR_BRUTE_KEY_LENGTH: 1,
/** /**
* @constant * @constant
* @default * @default
@@ -120,39 +121,62 @@ const BitwiseOp = {
* @returns {string} * @returns {string}
*/ */
runXorBrute: function (input, args) { runXorBrute: function (input, args) {
let keyLength = parseInt(args[0], 10), const keyLength = args[0],
sampleLength = args[1], sampleLength = args[1],
sampleOffset = args[2], sampleOffset = args[2],
nullPreserving = args[3], scheme = args[3],
differential = args[4], nullPreserving = args[4],
crib = args[5], printKey = args[5],
printKey = args[6], outputHex = args[6],
outputHex = args[7], crib = args[7].toLowerCase();
regex;
let output = "", let output = [],
result, result,
resultUtf8; resultUtf8,
record = "";
input = input.slice(sampleOffset, sampleOffset + sampleLength); input = input.slice(sampleOffset, sampleOffset + sampleLength);
if (crib !== "") { if (ENVIRONMENT_IS_WORKER())
regex = new RegExp(crib, "im"); self.sendStatusMessage("Calculating " + Math.pow(256, keyLength) + " values...");
}
/**
* Converts an integer to an array of bytes expressing that number.
*
* @param {number} int
* @param {number} len - Length of the resulting array
* @returns {array}
*/
const intToByteArray = (int, len) => {
let res = Array(len).fill(0);
for (let i = len - 1; i >= 0; i--) {
res[i] = int & 0xff;
int = int >>> 8;
}
return res;
};
for (let key = 1, l = Math.pow(256, keyLength); key < l; key++) { for (let key = 1, l = Math.pow(256, keyLength); key < l; key++) {
result = BitwiseOp._bitOp(input, Utils.hexToByteArray(key.toString(16)), BitwiseOp._xor, nullPreserving, differential); if (key % 10000 === 0 && ENVIRONMENT_IS_WORKER()) {
self.sendStatusMessage("Calculating " + l + " values... " + Math.floor(key / l * 100) + "%");
}
result = BitwiseOp._bitOp(input, intToByteArray(key, keyLength), BitwiseOp._xor, nullPreserving, scheme);
resultUtf8 = Utils.byteArrayToUtf8(result); resultUtf8 = Utils.byteArrayToUtf8(result);
if (crib !== "" && resultUtf8.search(regex) === -1) continue; record = "";
if (printKey) output += "Key = " + Utils.hex(key, (2*keyLength)) + ": ";
if (outputHex) if (crib && resultUtf8.toLowerCase().indexOf(crib) < 0) continue;
output += Utils.byteArrayToHex(result) + "\n"; if (printKey) record += "Key = " + Utils.hex(key, (2*keyLength)) + ": ";
else if (outputHex) {
output += Utils.printable(resultUtf8, false) + "\n"; record += Utils.toHex(result);
if (printKey) output += "\n"; } else {
record += Utils.printable(resultUtf8, false);
}
output.push(record);
} }
return output;
return output.join("\n");
}, },
@@ -228,6 +252,46 @@ const BitwiseOp = {
}, },
/**
* Bit shift left operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runBitShiftLeft: function(input, args) {
const amount = args[0];
return input.map(b => {
return (b << amount) & 0xff;
});
},
/**
* @constant
* @default
*/
BIT_SHIFT_TYPE: ["Logical shift", "Arithmetic shift"],
/**
* Bit shift right operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runBitShiftRight: function(input, args) {
const amount = args[0],
type = args[1],
mask = type === "Logical shift" ? 0 : 0x80;
return input.map(b => {
return (b >>> amount) ^ (b & mask);
});
},
/** /**
* XOR bitwise calculation. * XOR bitwise calculation.
* *

View File

@@ -1,4 +1,3 @@
/* globals app */
import Utils from "../Utils.js"; import Utils from "../Utils.js";
@@ -58,7 +57,7 @@ const ByteRepr = {
/** /**
* To Octal operation. * To Octal operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {byteArray} input * @param {byteArray} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
@@ -72,7 +71,7 @@ const ByteRepr = {
/** /**
* From Octal operation. * From Octal operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {byteArray} * @returns {byteArray}
@@ -108,8 +107,9 @@ const ByteRepr = {
throw "Error: Base argument must be between 2 and 36"; throw "Error: Base argument must be between 2 and 36";
} }
for (let i = 0; i < input.length; i++) { const charcode = Utils.strToCharcode(input);
ordinal = Utils.ord(input[i]); for (let i = 0; i < charcode.length; i++) {
ordinal = charcode[i];
if (base === 16) { if (base === 16) {
if (ordinal < 256) padding = 2; if (ordinal < 256) padding = 2;
@@ -118,11 +118,11 @@ const ByteRepr = {
else if (ordinal < 4294967296) padding = 8; else if (ordinal < 4294967296) padding = 8;
else padding = 2; else padding = 2;
if (padding > 2 && app) app.options.attemptHighlight = false; if (padding > 2 && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
output += Utils.hex(ordinal, padding) + delim; output += Utils.hex(ordinal, padding) + delim;
} else { } else {
if (app) app.options.attemptHighlight = false; if (ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
output += ordinal.toString(base) + delim; output += ordinal.toString(base) + delim;
} }
} }
@@ -148,9 +148,7 @@ const ByteRepr = {
throw "Error: Base argument must be between 2 and 36"; throw "Error: Base argument must be between 2 and 36";
} }
if (base !== 16 && app) { if (base !== 16 && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
app.options.attemptHighlight = false;
}
// Split into groups of 2 if the whole string is concatenated and // Split into groups of 2 if the whole string is concatenated and
// too long to be a single character // too long to be a single character
@@ -195,7 +193,7 @@ const ByteRepr = {
/** /**
* Highlight to hex * Highlight from hex
* *
* @param {Object[]} pos * @param {Object[]} pos
* @param {number} pos[].start * @param {number} pos[].start
@@ -287,10 +285,8 @@ const ByteRepr = {
* @returns {byteArray} * @returns {byteArray}
*/ */
runFromBinary: function(input, args) { runFromBinary: function(input, args) {
if (args[0] !== "None") { const delimRegex = Utils.regexRep[args[0] || "Space"];
const delimRegex = Utils.regexRep[args[0] || "Space"]; input = input.replace(delimRegex, "");
input = input.replace(delimRegex, "");
}
const output = []; const output = [];
const byteLen = 8; const byteLen = 8;

View File

@@ -1,5 +1,4 @@
import Utils from "../Utils.js"; import cptable from "../lib/js-codepage/cptable.js";
import CryptoJS from "crypto-js";
/** /**
@@ -17,34 +16,82 @@ const CharEnc = {
* @constant * @constant
* @default * @default
*/ */
IO_FORMAT: ["UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1", "Windows-1251", "Hex", "Base64"], IO_FORMAT: {
"UTF-8 (65001)": 65001,
"UTF-7 (65000)": 65000,
"UTF16LE (1200)": 1200,
"UTF16BE (1201)": 1201,
"UTF16 (1201)": 1201,
"IBM EBCDIC International (500)": 500,
"IBM EBCDIC US-Canada (37)": 37,
"Windows-874 Thai (874)": 874,
"Japanese Shift-JIS (932)": 932,
"Simplified Chinese GBK (936)": 936,
"Korean (949)": 949,
"Traditional Chinese Big5 (950)": 950,
"Windows-1250 Central European (1250)": 1250,
"Windows-1251 Cyrillic (1251)": 1251,
"Windows-1252 Latin (1252)": 1252,
"Windows-1253 Greek (1253)": 1253,
"Windows-1254 Turkish (1254)": 1254,
"Windows-1255 Hebrew (1255)": 1255,
"Windows-1256 Arabic (1256)": 1256,
"Windows-1257 Baltic (1257)": 1257,
"Windows-1258 Vietnam (1258)": 1258,
"US-ASCII (20127)": 20127,
"Simplified Chinese GB2312 (20936)": 20936,
"KOI8-R Russian Cyrillic (20866)": 20866,
"KOI8-U Ukrainian Cyrillic (21866)": 21866,
"ISO-8859-1 Latin 1 Western European (28591)": 28591,
"ISO-8859-2 Latin 2 Central European (28592)": 28592,
"ISO-8859-3 Latin 3 South European (28593)": 28593,
"ISO-8859-4 Latin 4 North European (28594)": 28594,
"ISO-8859-5 Latin/Cyrillic (28595)": 28595,
"ISO-8859-6 Latin/Arabic (28596)": 28596,
"ISO-8859-7 Latin/Greek (28597)": 28597,
"ISO-8859-8 Latin/Hebrew (28598)": 28598,
"ISO-8859-9 Latin 5 Turkish (28599)": 28599,
"ISO-8859-10 Latin 6 Nordic (28600)": 28600,
"ISO-8859-11 Latin/Thai (28601)": 28601,
"ISO-8859-13 Latin 7 Baltic Rim (28603)": 28603,
"ISO-8859-14 Latin 8 Celtic (28604)": 28604,
"ISO-8859-15 Latin 9 (28605)": 28605,
"ISO-8859-16 Latin 10 (28606)": 28606,
"ISO-2022 JIS Japanese (50222)": 50222,
"EUC Japanese (51932)": 51932,
"EUC Korean (51949)": 51949,
"Simplified Chinese GB18030 (54936)": 54936,
},
/** /**
* Text encoding operation. * Encode text operation.
* @author tlwr [toby@toby.codes]
* *
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {byteArray}
*/ */
run: function(input, args) { runEncode: function(input, args) {
let inputFormat = args[0], const format = CharEnc.IO_FORMAT[args[0]];
outputFormat = args[1]; let encoded = cptable.utils.encode(format, input);
encoded = Array.from(encoded);
if (inputFormat === "Windows-1251") { return encoded;
input = Utils.win1251ToUnicode(input);
input = CryptoJS.enc.Utf8.parse(input);
} else {
input = Utils.format[inputFormat].parse(input);
}
if (outputFormat === "Windows-1251") {
input = CryptoJS.enc.Utf8.stringify(input);
return Utils.unicodeToWin1251(input);
} else {
return Utils.format[outputFormat].stringify(input);
}
}, },
/**
* Decode text operation.
* @author tlwr [toby@toby.codes]
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runDecode: function(input, args) {
const format = CharEnc.IO_FORMAT[args[0]];
let decoded = cptable.utils.decode(format, input);
return decoded;
},
}; };
export default CharEnc; export default CharEnc;

View File

@@ -1,3 +1,4 @@
import * as CRC from "js-crc";
import Utils from "../Utils.js"; import Utils from "../Utils.js";
@@ -119,19 +120,24 @@ const Checksum = {
/** /**
* CRC-32 Checksum operation. * CRC-32 Checksum operation.
* *
* @param {byteArray} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
*/ */
runCRC32: function(input, args) { runCRC32: function(input, args) {
let crcTable = global.crcTable || (global.crcTable = Checksum._genCRCTable()), return CRC.crc32(input);
crc = 0 ^ (-1); },
for (let i = 0; i < input.length; i++) {
crc = (crc >>> 8) ^ crcTable[(crc ^ input[i]) & 0xff];
}
return Utils.hex((crc ^ (-1)) >>> 0); /**
* CRC-16 Checksum operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runCRC16: function(input, args) {
return CRC.crc16(input);
}, },
@@ -168,28 +174,6 @@ const Checksum = {
return Utils.hex(0xffff - csum); return Utils.hex(0xffff - csum);
}, },
/**
* Generates a CRC table for use with CRC checksums.
*
* @private
* @returns {array}
*/
_genCRCTable: function() {
let c,
crcTable = [];
for (let n = 0; n < 256; n++) {
c = n;
for (let k = 0; k < 8; k++) {
c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
}
crcTable[n] = c;
}
return crcTable;
},
}; };
export default Checksum; export default Checksum;

View File

@@ -407,7 +407,7 @@ const Cipher = {
/** /**
* Vigenère Encode operation. * Vigenère Encode operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
@@ -454,7 +454,7 @@ const Cipher = {
/** /**
* Vigenère Decode operation. * Vigenère Decode operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
@@ -508,7 +508,7 @@ const Cipher = {
/** /**
* Affine Cipher Encode operation. * Affine Cipher Encode operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
@@ -540,9 +540,9 @@ const Cipher = {
/** /**
* Affine Cipher Encode operation. * Affine Cipher Decode operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
@@ -584,7 +584,7 @@ const Cipher = {
/** /**
* Atbash Cipher Encode operation. * Atbash Cipher Encode operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
@@ -594,6 +594,159 @@ const Cipher = {
}, },
/**
* Generates a polybius square for the given keyword
*
* @private
* @author Matt C [matt@artemisbot.uk]
* @param {string} keyword - Must be upper case
* @returns {string}
*/
_genPolybiusSquare: function (keyword) {
const alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
const polArray = `${keyword}${alpha}`.split("").unique();
let polybius = [];
for (let i = 0; i < 5; i++) {
polybius[i] = polArray.slice(i*5, i*5 + 5);
}
return polybius;
},
/**
* Bifid Cipher Encode operation
*
* @author Matt C [matt@artemisbot.uk]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runBifidEnc: function (input, args) {
const keywordStr = args[0].toUpperCase().replace("J", "I"),
keyword = keywordStr.split("").unique(),
alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
let output = "",
xCo = [],
yCo = [],
structure = [],
count = 0;
if (keyword.length > 25)
return "The alphabet keyword must be less than 25 characters.";
if (!/^[a-zA-Z]+$/.test(keywordStr) && keyword.length > 0)
return "The key must consist only of letters";
const polybius = Cipher._genPolybiusSquare(keywordStr);
input.replace("J", "I").split("").forEach(letter => {
let alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0,
polInd;
if (alpInd) {
for (let i = 0; i < 5; i++) {
polInd = polybius[i].indexOf(letter.toLocaleUpperCase());
if (polInd >= 0) {
xCo.push(polInd);
yCo.push(i);
}
}
if (alpha.split("").indexOf(letter) >= 0) {
structure.push(true);
} else if (alpInd) {
structure.push(false);
}
} else {
structure.push(letter);
}
});
const trans = `${yCo.join("")}${xCo.join("")}`;
structure.forEach(pos => {
if (typeof pos === "boolean") {
let coords = trans.substr(2*count, 2).split("");
output += pos ?
polybius[coords[0]][coords[1]] :
polybius[coords[0]][coords[1]].toLocaleLowerCase();
count++;
} else {
output += pos;
}
});
return output;
},
/**
* Bifid Cipher Decode operation
*
* @author Matt C [matt@artemisbot.uk]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runBifidDec: function (input, args) {
const keywordStr = args[0].toUpperCase().replace("J", "I"),
keyword = keywordStr.split("").unique(),
alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
let output = "",
structure = [],
count = 0,
trans = "";
if (keyword.length > 25)
return "The alphabet keyword must be less than 25 characters.";
if (!/^[a-zA-Z]+$/.test(keywordStr) && keyword.length > 0)
return "The key must consist only of letters";
const polybius = Cipher._genPolybiusSquare(keywordStr);
input.replace("J", "I").split("").forEach((letter) => {
let alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0,
polInd;
if (alpInd) {
for (let i = 0; i < 5; i++) {
polInd = polybius[i].indexOf(letter.toLocaleUpperCase());
if (polInd >= 0) {
trans += `${i}${polInd}`;
}
}
if (alpha.split("").indexOf(letter) >= 0) {
structure.push(true);
} else if (alpInd) {
structure.push(false);
}
} else {
structure.push(letter);
}
});
structure.forEach(pos => {
if (typeof pos === "boolean") {
let coords = [trans[count], trans[count+trans.length/2]];
output += pos ?
polybius[coords[0]][coords[1]] :
polybius[coords[0]][coords[1]].toLocaleLowerCase();
count++;
} else {
output += pos;
}
});
return output;
},
/** /**
* @constant * @constant
* @default * @default
@@ -608,23 +761,23 @@ const Cipher = {
/** /**
* Substitute operation. * Substitute operation.
* *
* @param {byteArray} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {byteArray} * @returns {string}
*/ */
runSubstitute: function (input, args) { runSubstitute: function (input, args) {
let plaintext = Utils.strToByteArray(Utils.expandAlphRange(args[0]).join()), let plaintext = Utils.expandAlphRange(args[0]).join(""),
ciphertext = Utils.strToByteArray(Utils.expandAlphRange(args[1]).join()), ciphertext = Utils.expandAlphRange(args[1]).join(""),
output = [], output = "",
index = -1; index = -1;
if (plaintext.length !== ciphertext.length) { if (plaintext.length !== ciphertext.length) {
output = Utils.strToByteArray("Warning: Plaintext and Ciphertext lengths differ\n\n"); output = "Warning: Plaintext and Ciphertext lengths differ\n\n";
} }
for (let i = 0; i < input.length; i++) { for (let i = 0; i < input.length; i++) {
index = plaintext.indexOf(input[i]); index = plaintext.indexOf(input[i]);
output.push(index > -1 && index < ciphertext.length ? ciphertext[index] : input[i]); output += index > -1 && index < ciphertext.length ? ciphertext[index] : input[i];
} }
return output; return output;

View File

@@ -4,6 +4,7 @@ import Utils from "../Utils.js";
import vkbeautify from "vkbeautify"; import vkbeautify from "vkbeautify";
import {DOMParser as dom} from "xmldom"; import {DOMParser as dom} from "xmldom";
import xpath from "xpath"; import xpath from "xpath";
import jpath from "jsonpath";
import prettyPrintOne from "imports-loader?window=>global!exports-loader?prettyPrintOne!google-code-prettify/bin/prettify.min.js"; import prettyPrintOne from "imports-loader?window=>global!exports-loader?prettyPrintOne!google-code-prettify/bin/prettify.min.js";
@@ -228,19 +229,19 @@ const Code = {
} }
code = code code = code
// Create newlines after ; // Create newlines after ;
.replace(/;/g, ";\n") .replace(/;/g, ";\n")
// Create newlines after { and around } // Create newlines after { and around }
.replace(/{/g, "{\n") .replace(/{/g, "{\n")
.replace(/}/g, "\n}\n") .replace(/}/g, "\n}\n")
// Remove carriage returns // Remove carriage returns
.replace(/\r/g, "") .replace(/\r/g, "")
// Remove all indentation // Remove all indentation
.replace(/^\s+/g, "") .replace(/^\s+/g, "")
.replace(/\n\s+/g, "\n") .replace(/\n\s+/g, "\n")
// Remove trailing spaces // Remove trailing spaces
.replace(/\s*$/g, "") .replace(/\s*$/g, "")
.replace(/\n{/g, "{"); .replace(/\n{/g, "{");
// Indent // Indent
let i = 0, let i = 0,
@@ -265,27 +266,27 @@ const Code = {
} }
code = code code = code
// Add strategic spaces // Add strategic spaces
.replace(/\s*([!<>=+-/*]?)=\s*/g, " $1= ") .replace(/\s*([!<>=+-/*]?)=\s*/g, " $1= ")
.replace(/\s*<([=]?)\s*/g, " <$1 ") .replace(/\s*<([=]?)\s*/g, " <$1 ")
.replace(/\s*>([=]?)\s*/g, " >$1 ") .replace(/\s*>([=]?)\s*/g, " >$1 ")
.replace(/([^+])\+([^+=])/g, "$1 + $2") .replace(/([^+])\+([^+=])/g, "$1 + $2")
.replace(/([^-])-([^-=])/g, "$1 - $2") .replace(/([^-])-([^-=])/g, "$1 - $2")
.replace(/([^*])\*([^*=])/g, "$1 * $2") .replace(/([^*])\*([^*=])/g, "$1 * $2")
.replace(/([^/])\/([^/=])/g, "$1 / $2") .replace(/([^/])\/([^/=])/g, "$1 / $2")
.replace(/\s*,\s*/g, ", ") .replace(/\s*,\s*/g, ", ")
.replace(/\s*{/g, " {") .replace(/\s*{/g, " {")
.replace(/}\n/g, "}\n\n") .replace(/}\n/g, "}\n\n")
// Hacky horribleness // Hacky horribleness
.replace(/(if|for|while|with|elif|elseif)\s*\(([^\n]*)\)\s*\n([^{])/gim, "$1 ($2)\n $3") .replace(/(if|for|while|with|elif|elseif)\s*\(([^\n]*)\)\s*\n([^{])/gim, "$1 ($2)\n $3")
.replace(/(if|for|while|with|elif|elseif)\s*\(([^\n]*)\)([^{])/gim, "$1 ($2) $3") .replace(/(if|for|while|with|elif|elseif)\s*\(([^\n]*)\)([^{])/gim, "$1 ($2) $3")
.replace(/else\s*\n([^{])/gim, "else\n $1") .replace(/else\s*\n([^{])/gim, "else\n $1")
.replace(/else\s+([^{])/gim, "else $1") .replace(/else\s+([^{])/gim, "else $1")
// Remove strategic spaces // Remove strategic spaces
.replace(/\s+;/g, ";") .replace(/\s+;/g, ";")
.replace(/\{\s+\}/g, "{}") .replace(/\{\s+\}/g, "{}")
.replace(/\[\s+\]/g, "[]") .replace(/\[\s+\]/g, "[]")
.replace(/}\s*(else|catch|except|finally|elif|elseif|else if)/gi, "} $1"); .replace(/}\s*(else|catch|except|finally|elif|elseif|else if)/gi, "} $1");
// Replace preserved tokens // Replace preserved tokens
const ptokens = /###preservedToken(\d+)###/g; const ptokens = /###preservedToken(\d+)###/g;
@@ -329,7 +330,7 @@ const Code = {
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
*/ */
runXpath:function(input, args) { runXpath: function(input, args) {
let query = args[0], let query = args[0],
delimiter = args[1]; delimiter = args[1];
@@ -355,6 +356,48 @@ const Code = {
}, },
/**
* @constant
* @default
*/
JPATH_INITIAL: "",
/**
* @constant
* @default
*/
JPATH_DELIMITER: "\\n",
/**
* JPath expression operation.
*
* @author Matt C (matt@artemisbot.uk)
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runJpath: function(input, args) {
let query = args[0],
delimiter = args[1],
results,
obj;
try {
obj = JSON.parse(input);
} catch (err) {
return "Invalid input JSON: " + err.message;
}
try {
results = jpath.query(obj, query);
} catch (err) {
return "Invalid JPath expression: " + err.message;
}
return results.map(result => JSON.stringify(result)).join(delimiter);
},
/** /**
* @constant * @constant
* @default * @default
@@ -426,7 +469,7 @@ const Code = {
* @returns {string} * @returns {string}
*/ */
_replaceVariableNames(input, replacer) { _replaceVariableNames(input, replacer) {
let tokenRegex = /\\"|"(?:\\"|[^"])*"|(\b[a-z0-9\-_]+\b)/ig; const tokenRegex = /\\"|"(?:\\"|[^"])*"|(\b[a-z0-9\-_]+\b)/ig;
return input.replace(tokenRegex, (...args) => { return input.replace(tokenRegex, (...args) => {
let match = args[0], let match = args[0],
@@ -450,7 +493,7 @@ const Code = {
* *
*/ */
runToSnakeCase(input, args) { runToSnakeCase(input, args) {
let smart = args[0]; const smart = args[0];
if (smart) { if (smart) {
return Code._replaceVariableNames(input, snakeCase); return Code._replaceVariableNames(input, snakeCase);
@@ -469,7 +512,7 @@ const Code = {
* *
*/ */
runToCamelCase(input, args) { runToCamelCase(input, args) {
let smart = args[0]; const smart = args[0];
if (smart) { if (smart) {
return Code._replaceVariableNames(input, camelCase); return Code._replaceVariableNames(input, camelCase);
@@ -488,7 +531,7 @@ const Code = {
* *
*/ */
runToKebabCase(input, args) { runToKebabCase(input, args) {
let smart = args[0]; const smart = args[0];
if (smart) { if (smart) {
return Code._replaceVariableNames(input, kebabCase); return Code._replaceVariableNames(input, kebabCase);

View File

@@ -9,12 +9,12 @@ import bzip2 from "exports-loader?bzip2!../lib/bzip2.js";
const Zlib = { const Zlib = {
RawDeflate: rawdeflate.Zlib.RawDeflate, RawDeflate: rawdeflate.Zlib.RawDeflate,
RawInflate: rawinflate.Zlib.RawInflate, RawInflate: rawinflate.Zlib.RawInflate,
Deflate: zlibAndGzip.Zlib.Deflate, Deflate: zlibAndGzip.Zlib.Deflate,
Inflate: zlibAndGzip.Zlib.Inflate, Inflate: zlibAndGzip.Zlib.Inflate,
Gzip: zlibAndGzip.Zlib.Gzip, Gzip: zlibAndGzip.Zlib.Gzip,
Gunzip: zlibAndGzip.Zlib.Gunzip, Gunzip: zlibAndGzip.Zlib.Gunzip,
Zip: zip.Zlib.Zip, Zip: zip.Zlib.Zip,
Unzip: unzip.Zlib.Unzip, Unzip: unzip.Zlib.Unzip,
}; };
@@ -54,9 +54,9 @@ const Compress = {
* @default * @default
*/ */
RAW_COMPRESSION_TYPE_LOOKUP: { RAW_COMPRESSION_TYPE_LOOKUP: {
"Fixed Huffman Coding" : Zlib.RawDeflate.CompressionType.FIXED, "Fixed Huffman Coding": Zlib.RawDeflate.CompressionType.FIXED,
"Dynamic Huffman Coding" : Zlib.RawDeflate.CompressionType.DYNAMIC, "Dynamic Huffman Coding": Zlib.RawDeflate.CompressionType.DYNAMIC,
"None (Store)" : Zlib.RawDeflate.CompressionType.NONE, "None (Store)": Zlib.RawDeflate.CompressionType.NONE,
}, },
/** /**
@@ -99,8 +99,8 @@ const Compress = {
* @default * @default
*/ */
RAW_BUFFER_TYPE_LOOKUP: { RAW_BUFFER_TYPE_LOOKUP: {
"Adaptive" : Zlib.RawInflate.BufferType.ADAPTIVE, "Adaptive": Zlib.RawInflate.BufferType.ADAPTIVE,
"Block" : Zlib.RawInflate.BufferType.BLOCK, "Block": Zlib.RawInflate.BufferType.BLOCK,
}, },
/** /**
@@ -150,9 +150,9 @@ const Compress = {
* @default * @default
*/ */
ZLIB_COMPRESSION_TYPE_LOOKUP: { ZLIB_COMPRESSION_TYPE_LOOKUP: {
"Fixed Huffman Coding" : Zlib.Deflate.CompressionType.FIXED, "Fixed Huffman Coding": Zlib.Deflate.CompressionType.FIXED,
"Dynamic Huffman Coding" : Zlib.Deflate.CompressionType.DYNAMIC, "Dynamic Huffman Coding": Zlib.Deflate.CompressionType.DYNAMIC,
"None (Store)" : Zlib.Deflate.CompressionType.NONE, "None (Store)": Zlib.Deflate.CompressionType.NONE,
}, },
/** /**
@@ -175,8 +175,8 @@ const Compress = {
* @default * @default
*/ */
ZLIB_BUFFER_TYPE_LOOKUP: { ZLIB_BUFFER_TYPE_LOOKUP: {
"Adaptive" : Zlib.Inflate.BufferType.ADAPTIVE, "Adaptive": Zlib.Inflate.BufferType.ADAPTIVE,
"Block" : Zlib.Inflate.BufferType.BLOCK, "Block": Zlib.Inflate.BufferType.BLOCK,
}, },
/** /**
@@ -264,17 +264,17 @@ const Compress = {
* @default * @default
*/ */
ZIP_COMPRESSION_METHOD_LOOKUP: { ZIP_COMPRESSION_METHOD_LOOKUP: {
"Deflate" : Zlib.Zip.CompressionMethod.DEFLATE, "Deflate": Zlib.Zip.CompressionMethod.DEFLATE,
"None (Store)" : Zlib.Zip.CompressionMethod.STORE "None (Store)": Zlib.Zip.CompressionMethod.STORE
}, },
/** /**
* @constant * @constant
* @default * @default
*/ */
ZIP_OS_LOOKUP: { ZIP_OS_LOOKUP: {
"MSDOS" : Zlib.Zip.OperatingSystem.MSDOS, "MSDOS": Zlib.Zip.OperatingSystem.MSDOS,
"Unix" : Zlib.Zip.OperatingSystem.UNIX, "Unix": Zlib.Zip.OperatingSystem.UNIX,
"Macintosh" : Zlib.Zip.OperatingSystem.MACINTOSH "Macintosh": Zlib.Zip.OperatingSystem.MACINTOSH
}, },
/** /**

View File

@@ -25,36 +25,36 @@ const Convert = {
* @default * @default
*/ */
DISTANCE_FACTOR: { // Multiples of a metre DISTANCE_FACTOR: { // Multiples of a metre
"Nanometres (nm)" : 1e-9, "Nanometres (nm)": 1e-9,
"Micrometres (µm)" : 1e-6, "Micrometres (µm)": 1e-6,
"Millimetres (mm)" : 1e-3, "Millimetres (mm)": 1e-3,
"Centimetres (cm)" : 1e-2, "Centimetres (cm)": 1e-2,
"Metres (m)" : 1, "Metres (m)": 1,
"Kilometers (km)" : 1e3, "Kilometers (km)": 1e3,
"Thou (th)" : 0.0000254, "Thou (th)": 0.0000254,
"Inches (in)" : 0.0254, "Inches (in)": 0.0254,
"Feet (ft)" : 0.3048, "Feet (ft)": 0.3048,
"Yards (yd)" : 0.9144, "Yards (yd)": 0.9144,
"Chains (ch)" : 20.1168, "Chains (ch)": 20.1168,
"Furlongs (fur)" : 201.168, "Furlongs (fur)": 201.168,
"Miles (mi)" : 1609.344, "Miles (mi)": 1609.344,
"Leagues (lea)" : 4828.032, "Leagues (lea)": 4828.032,
"Fathoms (ftm)" : 1.853184, "Fathoms (ftm)": 1.853184,
"Cables" : 185.3184, "Cables": 185.3184,
"Nautical miles" : 1853.184, "Nautical miles": 1853.184,
"Cars (4m)" : 4, "Cars (4m)": 4,
"Buses (8.4m)" : 8.4, "Buses (8.4m)": 8.4,
"American football fields (91m)": 91, "American football fields (91m)": 91,
"Football pitches (105m)": 105, "Football pitches (105m)": 105,
"Earth-to-Moons" : 380000000, "Earth-to-Moons": 380000000,
"Earth's equators" : 40075016.686, "Earth's equators": 40075016.686,
"Astronomical units (au)": 149597870700, "Astronomical units (au)": 149597870700,
"Light-years (ly)" : 9460730472580800, "Light-years (ly)": 9460730472580800,
"Parsecs (pc)" : 3.0856776e16 "Parsecs (pc)": 3.0856776e16
}, },
/** /**
@@ -90,52 +90,52 @@ const Convert = {
* @default * @default
*/ */
DATA_FACTOR: { // Multiples of a bit DATA_FACTOR: { // Multiples of a bit
"Bits (b)" : 1, "Bits (b)": 1,
"Nibbles" : 4, "Nibbles": 4,
"Octets" : 8, "Octets": 8,
"Bytes (B)" : 8, "Bytes (B)": 8,
// Binary bits (2^n) // Binary bits (2^n)
"Kibibits (Kib)" : 1024, "Kibibits (Kib)": 1024,
"Mebibits (Mib)" : 1048576, "Mebibits (Mib)": 1048576,
"Gibibits (Gib)" : 1073741824, "Gibibits (Gib)": 1073741824,
"Tebibits (Tib)" : 1099511627776, "Tebibits (Tib)": 1099511627776,
"Pebibits (Pib)" : 1125899906842624, "Pebibits (Pib)": 1125899906842624,
"Exbibits (Eib)" : 1152921504606846976, "Exbibits (Eib)": 1152921504606846976,
"Zebibits (Zib)" : 1180591620717411303424, "Zebibits (Zib)": 1180591620717411303424,
"Yobibits (Yib)" : 1208925819614629174706176, "Yobibits (Yib)": 1208925819614629174706176,
// Decimal bits (10^n) // Decimal bits (10^n)
"Decabits" : 10, "Decabits": 10,
"Hectobits" : 100, "Hectobits": 100,
"Kilobits (Kb)" : 1e3, "Kilobits (Kb)": 1e3,
"Megabits (Mb)" : 1e6, "Megabits (Mb)": 1e6,
"Gigabits (Gb)" : 1e9, "Gigabits (Gb)": 1e9,
"Terabits (Tb)" : 1e12, "Terabits (Tb)": 1e12,
"Petabits (Pb)" : 1e15, "Petabits (Pb)": 1e15,
"Exabits (Eb)" : 1e18, "Exabits (Eb)": 1e18,
"Zettabits (Zb)" : 1e21, "Zettabits (Zb)": 1e21,
"Yottabits (Yb)" : 1e24, "Yottabits (Yb)": 1e24,
// Binary bytes (8 x 2^n) // Binary bytes (8 x 2^n)
"Kibibytes (KiB)" : 8192, "Kibibytes (KiB)": 8192,
"Mebibytes (MiB)" : 8388608, "Mebibytes (MiB)": 8388608,
"Gibibytes (GiB)" : 8589934592, "Gibibytes (GiB)": 8589934592,
"Tebibytes (TiB)" : 8796093022208, "Tebibytes (TiB)": 8796093022208,
"Pebibytes (PiB)" : 9007199254740992, "Pebibytes (PiB)": 9007199254740992,
"Exbibytes (EiB)" : 9223372036854775808, "Exbibytes (EiB)": 9223372036854775808,
"Zebibytes (ZiB)" : 9444732965739290427392, "Zebibytes (ZiB)": 9444732965739290427392,
"Yobibytes (YiB)" : 9671406556917033397649408, "Yobibytes (YiB)": 9671406556917033397649408,
// Decimal bytes (8 x 10^n) // Decimal bytes (8 x 10^n)
"Kilobytes (KB)" : 8e3, "Kilobytes (KB)": 8e3,
"Megabytes (MB)" : 8e6, "Megabytes (MB)": 8e6,
"Gigabytes (GB)" : 8e9, "Gigabytes (GB)": 8e9,
"Terabytes (TB)" : 8e12, "Terabytes (TB)": 8e12,
"Petabytes (PB)" : 8e15, "Petabytes (PB)": 8e15,
"Exabytes (EB)" : 8e18, "Exabytes (EB)": 8e18,
"Zettabytes (ZB)" : 8e21, "Zettabytes (ZB)": 8e21,
"Yottabytes (YB)" : 8e24, "Yottabytes (YB)": 8e24,
}, },
/** /**
@@ -171,51 +171,51 @@ const Convert = {
*/ */
AREA_FACTOR: { // Multiples of a square metre AREA_FACTOR: { // Multiples of a square metre
// Metric // Metric
"Square metre (sq m)" : 1, "Square metre (sq m)": 1,
"Square kilometre (sq km)" : 1e6, "Square kilometre (sq km)": 1e6,
"Centiare (ca)" : 1, "Centiare (ca)": 1,
"Deciare (da)" : 10, "Deciare (da)": 10,
"Are (a)" : 100, "Are (a)": 100,
"Decare (daa)" : 1e3, "Decare (daa)": 1e3,
"Hectare (ha)" : 1e4, "Hectare (ha)": 1e4,
// Imperial // Imperial
"Square inch (sq in)" : 0.00064516, "Square inch (sq in)": 0.00064516,
"Square foot (sq ft)" : 0.09290304, "Square foot (sq ft)": 0.09290304,
"Square yard (sq yd)" : 0.83612736, "Square yard (sq yd)": 0.83612736,
"Square mile (sq mi)" : 2589988.110336, "Square mile (sq mi)": 2589988.110336,
"Perch (sq per)" : 42.21, "Perch (sq per)": 42.21,
"Rood (ro)" : 1011, "Rood (ro)": 1011,
"International acre (ac)" : 4046.8564224, "International acre (ac)": 4046.8564224,
// US customary units // US customary units
"US survey acre (ac)" : 4046.87261, "US survey acre (ac)": 4046.87261,
"US survey square mile (sq mi)" : 2589998.470305239, "US survey square mile (sq mi)": 2589998.470305239,
"US survey township" : 93239944.9309886, "US survey township": 93239944.9309886,
// Nuclear physics // Nuclear physics
"Yoctobarn (yb)" : 1e-52, "Yoctobarn (yb)": 1e-52,
"Zeptobarn (zb)" : 1e-49, "Zeptobarn (zb)": 1e-49,
"Attobarn (ab)" : 1e-46, "Attobarn (ab)": 1e-46,
"Femtobarn (fb)" : 1e-43, "Femtobarn (fb)": 1e-43,
"Picobarn (pb)" : 1e-40, "Picobarn (pb)": 1e-40,
"Nanobarn (nb)" : 1e-37, "Nanobarn (nb)": 1e-37,
"Microbarn (μb)" : 1e-34, "Microbarn (μb)": 1e-34,
"Millibarn (mb)" : 1e-31, "Millibarn (mb)": 1e-31,
"Barn (b)" : 1e-28, "Barn (b)": 1e-28,
"Kilobarn (kb)" : 1e-25, "Kilobarn (kb)": 1e-25,
"Megabarn (Mb)" : 1e-22, "Megabarn (Mb)": 1e-22,
"Planck area" : 2.6e-70, "Planck area": 2.6e-70,
"Shed" : 1e-52, "Shed": 1e-52,
"Outhouse" : 1e-34, "Outhouse": 1e-34,
// Comparisons // Comparisons
"Washington D.C." : 176119191.502848, "Washington D.C.": 176119191.502848,
"Isle of Wight" : 380000000, "Isle of Wight": 380000000,
"Wales" : 20779000000, "Wales": 20779000000,
"Texas" : 696241000000, "Texas": 696241000000,
}, },
/** /**
@@ -252,81 +252,81 @@ const Convert = {
*/ */
MASS_FACTOR: { // Multiples of a gram MASS_FACTOR: { // Multiples of a gram
// Metric // Metric
"Yoctogram (yg)" : 1e-24, "Yoctogram (yg)": 1e-24,
"Zeptogram (zg)" : 1e-21, "Zeptogram (zg)": 1e-21,
"Attogram (ag)" : 1e-18, "Attogram (ag)": 1e-18,
"Femtogram (fg)" : 1e-15, "Femtogram (fg)": 1e-15,
"Picogram (pg)" : 1e-12, "Picogram (pg)": 1e-12,
"Nanogram (ng)" : 1e-9, "Nanogram (ng)": 1e-9,
"Microgram (μg)" : 1e-6, "Microgram (μg)": 1e-6,
"Milligram (mg)" : 1e-3, "Milligram (mg)": 1e-3,
"Centigram (cg)" : 1e-2, "Centigram (cg)": 1e-2,
"Decigram (dg)" : 1e-1, "Decigram (dg)": 1e-1,
"Gram (g)" : 1, "Gram (g)": 1,
"Decagram (dag)" : 10, "Decagram (dag)": 10,
"Hectogram (hg)" : 100, "Hectogram (hg)": 100,
"Kilogram (kg)" : 1000, "Kilogram (kg)": 1000,
"Megagram (Mg)" : 1e6, "Megagram (Mg)": 1e6,
"Tonne (t)" : 1e6, "Tonne (t)": 1e6,
"Gigagram (Gg)" : 1e9, "Gigagram (Gg)": 1e9,
"Teragram (Tg)" : 1e12, "Teragram (Tg)": 1e12,
"Petagram (Pg)" : 1e15, "Petagram (Pg)": 1e15,
"Exagram (Eg)" : 1e18, "Exagram (Eg)": 1e18,
"Zettagram (Zg)" : 1e21, "Zettagram (Zg)": 1e21,
"Yottagram (Yg)" : 1e24, "Yottagram (Yg)": 1e24,
// Imperial Avoirdupois // Imperial Avoirdupois
"Grain (gr)" : 64.79891e-3, "Grain (gr)": 64.79891e-3,
"Dram (dr)" : 1.7718451953125, "Dram (dr)": 1.7718451953125,
"Ounce (oz)" : 28.349523125, "Ounce (oz)": 28.349523125,
"Pound (lb)" : 453.59237, "Pound (lb)": 453.59237,
"Nail" : 3175.14659, "Nail": 3175.14659,
"Stone (st)" : 6.35029318e3, "Stone (st)": 6.35029318e3,
"Quarter (gr)" : 12700.58636, "Quarter (gr)": 12700.58636,
"Tod" : 12700.58636, "Tod": 12700.58636,
"US hundredweight (cwt)" : 45.359237e3, "US hundredweight (cwt)": 45.359237e3,
"Imperial hundredweight (cwt)" : 50.80234544e3, "Imperial hundredweight (cwt)": 50.80234544e3,
"US ton (t)" : 907.18474e3, "US ton (t)": 907.18474e3,
"Imperial ton (t)" : 1016.0469088e3, "Imperial ton (t)": 1016.0469088e3,
// Imperial Troy // Imperial Troy
"Pennyweight (dwt)" : 1.55517384, "Pennyweight (dwt)": 1.55517384,
"Troy dram (dr t)" : 3.8879346, "Troy dram (dr t)": 3.8879346,
"Troy ounce (oz t)" : 31.1034768, "Troy ounce (oz t)": 31.1034768,
"Troy pound (lb t)" : 373.2417216, "Troy pound (lb t)": 373.2417216,
"Mark" : 248.8278144, "Mark": 248.8278144,
// Archaic // Archaic
"Wey" : 76.5e3, "Wey": 76.5e3,
"Wool wey" : 101.7e3, "Wool wey": 101.7e3,
"Suffolk wey" : 161.5e3, "Suffolk wey": 161.5e3,
"Wool sack" : 153000, "Wool sack": 153000,
"Coal sack" : 50.80234544e3, "Coal sack": 50.80234544e3,
"Load" : 918000, "Load": 918000,
"Last" : 1836000, "Last": 1836000,
"Flax or feather last" : 770e3, "Flax or feather last": 770e3,
"Gunpowder last" : 1090e3, "Gunpowder last": 1090e3,
"Picul" : 60.478982e3, "Picul": 60.478982e3,
"Rice last" : 1200e3, "Rice last": 1200e3,
// Comparisons // Comparisons
"Big Ben (14 tonnes)" : 14e6, "Big Ben (14 tonnes)": 14e6,
"Blue whale (180 tonnes)" : 180e6, "Blue whale (180 tonnes)": 180e6,
"International Space Station (417 tonnes)" : 417e6, "International Space Station (417 tonnes)": 417e6,
"Space Shuttle (2,041 tonnes)" : 2041e6, "Space Shuttle (2,041 tonnes)": 2041e6,
"RMS Titanic (52,000 tonnes)" : 52000e6, "RMS Titanic (52,000 tonnes)": 52000e6,
"Great Pyramid of Giza (6,000,000 tonnes)" : 6e12, "Great Pyramid of Giza (6,000,000 tonnes)": 6e12,
"Earth's oceans (1.4 yottagrams)" : 1.4e24, "Earth's oceans (1.4 yottagrams)": 1.4e24,
// Astronomical // Astronomical
"A teaspoon of neutron star (5,500 million tonnes)" : 5.5e15, "A teaspoon of neutron star (5,500 million tonnes)": 5.5e15,
"Lunar mass (ML)" : 7.342e25, "Lunar mass (ML)": 7.342e25,
"Earth mass (M⊕)" : 5.97219e27, "Earth mass (M⊕)": 5.97219e27,
"Jupiter mass (MJ)" : 1.8981411476999997e30, "Jupiter mass (MJ)": 1.8981411476999997e30,
"Solar mass (M☉)" : 1.98855e33, "Solar mass (M☉)": 1.98855e33,
"Sagittarius A* (7.5 x 10^36 kgs-ish)" : 7.5e39, "Sagittarius A* (7.5 x 10^36 kgs-ish)": 7.5e39,
"Milky Way galaxy (1.2 x 10^42 kgs)" : 1.2e45, "Milky Way galaxy (1.2 x 10^42 kgs)": 1.2e45,
"The observable universe (1.45 x 10^53 kgs)" : 1.45e56, "The observable universe (1.45 x 10^53 kgs)": 1.45e56,
}, },
/** /**
@@ -361,37 +361,37 @@ const Convert = {
*/ */
SPEED_FACTOR: { // Multiples of m/s SPEED_FACTOR: { // Multiples of m/s
// Metric // Metric
"Metres per second (m/s)" : 1, "Metres per second (m/s)": 1,
"Kilometres per hour (km/h)" : 0.2778, "Kilometres per hour (km/h)": 0.2778,
// Imperial // Imperial
"Miles per hour (mph)" : 0.44704, "Miles per hour (mph)": 0.44704,
"Knots (kn)" : 0.5144, "Knots (kn)": 0.5144,
// Comparisons // Comparisons
"Human hair growth rate" : 4.8e-9, "Human hair growth rate": 4.8e-9,
"Bamboo growth rate" : 1.4e-5, "Bamboo growth rate": 1.4e-5,
"World's fastest snail" : 0.00275, "World's fastest snail": 0.00275,
"Usain Bolt's top speed" : 12.42, "Usain Bolt's top speed": 12.42,
"Jet airliner cruising speed" : 250, "Jet airliner cruising speed": 250,
"Concorde" : 603, "Concorde": 603,
"SR-71 Blackbird" : 981, "SR-71 Blackbird": 981,
"Space Shuttle" : 1400, "Space Shuttle": 1400,
"International Space Station" : 7700, "International Space Station": 7700,
// Scientific // Scientific
"Sound in standard atmosphere" : 340.3, "Sound in standard atmosphere": 340.3,
"Sound in water" : 1500, "Sound in water": 1500,
"Lunar escape velocity" : 2375, "Lunar escape velocity": 2375,
"Earth escape velocity" : 11200, "Earth escape velocity": 11200,
"Earth's solar orbit" : 29800, "Earth's solar orbit": 29800,
"Solar system's Milky Way orbit" : 200000, "Solar system's Milky Way orbit": 200000,
"Milky Way relative to the cosmic microwave background" : 552000, "Milky Way relative to the cosmic microwave background": 552000,
"Solar escape velocity" : 617700, "Solar escape velocity": 617700,
"Neutron star escape velocity (0.3c)" : 100000000, "Neutron star escape velocity (0.3c)": 100000000,
"Light in a diamond (0.4136c)" : 124000000, "Light in a diamond (0.4136c)": 124000000,
"Signal in an optical fibre (0.667c)" : 200000000, "Signal in an optical fibre (0.667c)": 200000000,
"Light (c)" : 299792458, "Light (c)": 299792458,
}, },
/** /**

View File

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

View File

@@ -0,0 +1,94 @@
import Utils from "../Utils.js";
import * as JsDiff from "diff";
/**
* Diff operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Diff = {
/**
* @constant
* @default
*/
DIFF_SAMPLE_DELIMITER: "\\n\\n",
/**
* @constant
* @default
*/
DIFF_BY: ["Character", "Word", "Line", "Sentence", "CSS", "JSON"],
/**
* Diff operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runDiff: function(input, args) {
let sampleDelim = args[0],
diffBy = args[1],
showAdded = args[2],
showRemoved = args[3],
ignoreWhitespace = args[4],
samples = input.split(sampleDelim),
output = "",
diff;
if (!samples || samples.length !== 2) {
return "Incorrect number of samples, perhaps you need to modify the sample delimiter or add more samples?";
}
switch (diffBy) {
case "Character":
diff = JsDiff.diffChars(samples[0], samples[1]);
break;
case "Word":
if (ignoreWhitespace) {
diff = JsDiff.diffWords(samples[0], samples[1]);
} else {
diff = JsDiff.diffWordsWithSpace(samples[0], samples[1]);
}
break;
case "Line":
if (ignoreWhitespace) {
diff = JsDiff.diffTrimmedLines(samples[0], samples[1]);
} else {
diff = JsDiff.diffLines(samples[0], samples[1]);
}
break;
case "Sentence":
diff = JsDiff.diffSentences(samples[0], samples[1]);
break;
case "CSS":
diff = JsDiff.diffCss(samples[0], samples[1]);
break;
case "JSON":
diff = JsDiff.diffJson(samples[0], samples[1]);
break;
default:
return "Invalid 'Diff by' option.";
}
for (let i = 0; i < diff.length; i++) {
if (diff[i].added) {
if (showAdded) output += "<span class='hl5'>" + Utils.escapeHtml(diff[i].value) + "</span>";
} else if (diff[i].removed) {
if (showRemoved) output += "<span class='hl3'>" + Utils.escapeHtml(diff[i].value) + "</span>";
} else {
output += Utils.escapeHtml(diff[i].value);
}
}
return output;
},
};
export default Diff;

View File

@@ -170,9 +170,9 @@ const Extract = {
protocol = "[A-Z]+://", protocol = "[A-Z]+://",
hostname = "[-\\w]+(?:\\.\\w[-\\w]*)+", hostname = "[-\\w]+(?:\\.\\w[-\\w]*)+",
port = ":\\d+", port = ":\\d+",
path = "/[^.!,?;\"'<>()\\[\\]{}\\s\\x7F-\\xFF]*"; path = "/[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]*";
path += "(?:[.!,?]+[^.!,?;\"'<>()\\[\\]{}\\s\\x7F-\\xFF]+)*"; path += "(?:[.!,?]+[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]+)*";
const regex = new RegExp(protocol + hostname + "(?:" + port + const regex = new RegExp(protocol + hostname + "(?:" + port +
")?(?:" + path + ")?", "ig"); ")?(?:" + path + ")?", "ig");
return Extract._search(input, regex, null, displayTotal); return Extract._search(input, regex, null, displayTotal);
@@ -187,11 +187,8 @@ const Extract = {
* @returns {string} * @returns {string}
*/ */
runDomains: function(input, args) { runDomains: function(input, args) {
let displayTotal = args[0], const displayTotal = args[0],
protocol = "https?://", regex = /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b/ig;
hostname = "[-\\w\\.]+",
tld = "\\.(?:com|net|org|biz|info|co|uk|onion|int|mobi|name|edu|gov|mil|eu|ac|ae|af|de|ca|ch|cn|cy|es|gb|hk|il|in|io|tv|me|nl|no|nz|ro|ru|tr|us|az|ir|kz|uz|pk)+",
regex = new RegExp("(?:" + protocol + ")?" + hostname + tld, "ig");
return Extract._search(input, regex, null, displayTotal); return Extract._search(input, regex, null, displayTotal);
}, },
@@ -261,39 +258,6 @@ const Extract = {
return Extract._search(input, regex, null, displayTotal); return Extract._search(input, regex, null, displayTotal);
}, },
/**
* Extract all identifiers operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runAllIdents: function(input, args) {
let output = "";
output += "IP addresses\n";
output += Extract.runIp(input, [true, true, false]);
output += "\nEmail addresses\n";
output += Extract.runEmail(input, []);
output += "\nMAC addresses\n";
output += Extract.runMac(input, []);
output += "\nURLs\n";
output += Extract.runUrls(input, []);
output += "\nDomain names\n";
output += Extract.runDomains(input, []);
output += "\nFile paths\n";
output += Extract.runFilePaths(input, [true, true]);
output += "\nDates\n";
output += Extract.runDates(input, []);
return output;
},
}; };
export default Extract; export default Extract;

View File

@@ -20,7 +20,7 @@ const FileType = {
* @returns {string} * @returns {string}
*/ */
runDetect: function(input, args) { runDetect: function(input, args) {
const type = FileType._magicType(input); const type = FileType.magicType(input);
if (!type) { if (!type) {
return "Unknown file type. Have you tried checking the entropy of this data to determine whether it might be encrypted or compressed?"; return "Unknown file type. Have you tried checking the entropy of this data to determine whether it might be encrypted or compressed?";
@@ -59,7 +59,7 @@ const FileType = {
numCommonFound = 0; numCommonFound = 0;
for (let i = 0; i < input.length; i++) { for (let i = 0; i < input.length; i++) {
type = FileType._magicType(input.slice(i)); type = FileType.magicType(input.slice(i));
if (type) { if (type) {
if (ignoreCommon && commonExts.indexOf(type.ext) > -1) { if (ignoreCommon && commonExts.indexOf(type.ext) > -1) {
numCommonFound++; numCommonFound++;
@@ -96,14 +96,13 @@ const FileType = {
* Given a buffer, detects magic byte sequences at specific positions and returns the * Given a buffer, detects magic byte sequences at specific positions and returns the
* extension and mime type. * extension and mime type.
* *
* @private
* @param {byteArray} buf * @param {byteArray} buf
* @returns {Object} type * @returns {Object} type
* @returns {string} type.ext - File extension * @returns {string} type.ext - File extension
* @returns {string} type.mime - Mime type * @returns {string} type.mime - Mime type
* @returns {string} [type.desc] - Description * @returns {string} [type.desc] - Description
*/ */
_magicType: function (buf) { magicType: function (buf) {
if (!(buf && buf.length > 1)) { if (!(buf && buf.length > 1)) {
return null; return null;
} }

View File

@@ -0,0 +1,99 @@
import {BigInteger} from "jsbn";
/**
* Windows Filetime operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const Filetime = {
/**
* @constant
* @default
*/
UNITS: ["Seconds (s)", "Milliseconds (ms)", "Microseconds (μs)", "Nanoseconds (ns)"],
/**
* @constant
* @default
*/
FILETIME_FORMATS: ["Decimal", "Hex"],
/**
* Windows Filetime to Unix Timestamp operation.
*
* @author bwhitn [brian.m.whitney@outlook.com]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runFromFiletimeToUnix: function(input, args) {
let units = args[0],
format = args[1];
if (format === "Hex") {
input = new BigInteger(input, 16);
} else {
input = new BigInteger(input);
}
input = input.subtract(new BigInteger("116444736000000000"));
if (units === "Seconds (s)"){
input = input.divide(new BigInteger("10000000"));
} else if (units === "Milliseconds (ms)") {
input = input.divide(new BigInteger("10000"));
} else if (units === "Microseconds (μs)") {
input = input.divide(new BigInteger("10"));
} else if (units === "Nanoseconds (ns)") {
input = input.multiply(new BigInteger("100"));
} else {
throw "Unrecognised unit";
}
return input.toString();
},
/**
* Unix Timestamp to Windows Filetime operation.
*
* @author bwhitn [brian.m.whitney@outlook.com]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runToFiletimeFromUnix: function(input, args) {
let units = args[0],
format = args[1];
input = new BigInteger(input);
if (units === "Seconds (s)"){
input = input.multiply(new BigInteger("10000000"));
} else if (units === "Milliseconds (ms)") {
input = input.multiply(new BigInteger("10000"));
} else if (units === "Microseconds (μs)") {
input = input.multiply(new BigInteger("10"));
} else if (units === "Nanoseconds (ns)") {
input = input.divide(new BigInteger("100"));
} else {
throw "Unrecognised unit";
}
input = input.add(new BigInteger("116444736000000000"));
if (format === "Hex"){
return input.toString(16);
} else {
return input.toString();
}
},
};
export default Filetime;

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,17 @@ import {UAS_parser as UAParser} from "../lib/uas_parser.js";
*/ */
const HTTP = { const HTTP = {
/**
* @constant
* @default
*/
METHODS: [
"GET", "POST", "HEAD",
"PUT", "PATCH", "DELETE",
"CONNECT", "TRACE", "OPTIONS"
],
/** /**
* Strip HTTP headers operation. * Strip HTTP headers operation.
* *
@@ -51,6 +62,95 @@ const HTTP = {
"Device Type: " + ua.deviceType + "\n"; "Device Type: " + ua.deviceType + "\n";
}, },
/**
* @constant
* @default
*/
MODE: [
"Cross-Origin Resource Sharing",
"No CORS (limited to HEAD, GET or POST)",
],
/**
* Lookup table for HTTP modes
*
* @private
* @constant
*/
_modeLookup: {
"Cross-Origin Resource Sharing": "cors",
"No CORS (limited to HEAD, GET or POST)": "no-cors",
},
/**
* HTTP request operation.
*
* @author tlwr [toby@toby.codes]
* @author n1474335 [n1474335@gmail.com]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runHTTPRequest(input, args) {
const method = args[0],
url = args[1],
headersText = args[2],
mode = args[3],
showResponseMetadata = args[4];
if (url.length === 0) return "";
let headers = new Headers();
headersText.split(/\r?\n/).forEach(line => {
line = line.trim();
if (line.length === 0) return;
let split = line.split(":");
if (split.length !== 2) throw `Could not parse header in line: ${line}`;
headers.set(split[0].trim(), split[1].trim());
});
let config = {
method: method,
headers: headers,
mode: HTTP._modeLookup[mode],
cache: "no-cache",
};
if (method !== "GET" && method !== "HEAD") {
config.body = input;
}
return fetch(url, config)
.then(r => {
if (r.status === 0 && r.type === "opaque") {
return "Error: Null response. Try setting the connection mode to CORS.";
}
if (showResponseMetadata) {
let headers = "";
for (let pair of r.headers.entries()) {
headers += " " + pair[0] + ": " + pair[1] + "\n";
}
return r.text().then(b => {
return "####\n Status: " + r.status + " " + r.statusText +
"\n Exposed headers:\n" + headers + "####\n\n" + b;
});
}
return r.text();
})
.catch(e => {
return e.toString() +
"\n\nThis error could be caused by one of the following:\n" +
" - An invalid URL\n" +
" - Making a request to an insecure resource (HTTP) from a secure source (HTTPS)\n" +
" - Making a cross-origin request to a server which does not support CORS\n";
});
},
}; };
export default HTTP; export default HTTP;

View File

@@ -1,6 +1,7 @@
import Utils from "../Utils.js"; import Utils from "../Utils.js";
import CryptoJS from "crypto-js";
import CryptoApi from "crypto-api"; import CryptoApi from "crypto-api";
import MD6 from "node-md6";
import * as SHA3 from "js-sha3";
import Checksum from "./Checksum.js"; import Checksum from "./Checksum.js";
@@ -23,7 +24,7 @@ const Hash = {
* @returns {string} * @returns {string}
*/ */
runMD2: function (input, args) { runMD2: function (input, args) {
return Utils.toHexFast(CryptoApi.hash("md2", input, {})); return CryptoApi.hash("md2", input, {}).stringify("hex");
}, },
@@ -35,7 +36,7 @@ const Hash = {
* @returns {string} * @returns {string}
*/ */
runMD4: function (input, args) { runMD4: function (input, args) {
return Utils.toHexFast(CryptoApi.hash("md4", input, {})); return CryptoApi.hash("md4", input, {}).stringify("hex");
}, },
@@ -47,8 +48,39 @@ const Hash = {
* @returns {string} * @returns {string}
*/ */
runMD5: function (input, args) { runMD5: function (input, args) {
input = CryptoJS.enc.Latin1.parse(input); // Cast to WordArray return CryptoApi.hash("md5", input, {}).stringify("hex");
return CryptoJS.MD5(input).toString(CryptoJS.enc.Hex); },
/**
* @constant
* @default
*/
MD6_SIZE: 256,
/**
* @constant
* @default
*/
MD6_LEVELS: 64,
/**
* MD6 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runMD6: function (input, args) {
const size = args[0],
levels = args[1],
key = args[2];
if (size < 0 || size > 512)
return "Size must be between 0 and 512";
if (levels < 0)
return "Levels must be greater than 0";
return MD6.getHashOfText(input, size, key, levels);
}, },
@@ -60,7 +92,7 @@ const Hash = {
* @returns {string} * @returns {string}
*/ */
runSHA0: function (input, args) { runSHA0: function (input, args) {
return Utils.toHexFast(CryptoApi.hash("sha0", input, {})); return CryptoApi.hash("sha0", input, {}).stringify("hex");
}, },
@@ -72,60 +104,7 @@ const Hash = {
* @returns {string} * @returns {string}
*/ */
runSHA1: function (input, args) { runSHA1: function (input, args) {
input = CryptoJS.enc.Latin1.parse(input); return CryptoApi.hash("sha1", input, {}).stringify("hex");
return CryptoJS.SHA1(input).toString(CryptoJS.enc.Hex);
},
/**
* SHA224 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSHA224: function (input, args) {
input = CryptoJS.enc.Latin1.parse(input);
return CryptoJS.SHA224(input).toString(CryptoJS.enc.Hex);
},
/**
* SHA256 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSHA256: function (input, args) {
input = CryptoJS.enc.Latin1.parse(input);
return CryptoJS.SHA256(input).toString(CryptoJS.enc.Hex);
},
/**
* SHA384 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSHA384: function (input, args) {
input = CryptoJS.enc.Latin1.parse(input);
return CryptoJS.SHA384(input).toString(CryptoJS.enc.Hex);
},
/**
* SHA512 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSHA512: function (input, args) {
input = CryptoJS.enc.Latin1.parse(input);
return CryptoJS.SHA512(input).toString(CryptoJS.enc.Hex);
}, },
@@ -133,7 +112,26 @@ const Hash = {
* @constant * @constant
* @default * @default
*/ */
SHA3_LENGTH: ["512", "384", "256", "224"], SHA2_SIZE: ["512", "256", "384", "224", "512/256", "512/224"],
/**
* SHA2 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSHA2: function (input, args) {
const size = args[0];
return CryptoApi.hash("sha" + size, input, {}).stringify("hex");
},
/**
* @constant
* @default
*/
SHA3_SIZE: ["512", "384", "256", "224"],
/** /**
* SHA3 operation. * SHA3 operation.
@@ -143,25 +141,27 @@ const Hash = {
* @returns {string} * @returns {string}
*/ */
runSHA3: function (input, args) { runSHA3: function (input, args) {
input = CryptoJS.enc.Latin1.parse(input); const size = parseInt(args[0], 10);
let sha3Length = args[0], let algo;
options = {
outputLength: parseInt(sha3Length, 10)
};
return CryptoJS.SHA3(input, options).toString(CryptoJS.enc.Hex);
},
switch (size) {
case 224:
algo = SHA3.sha3_224;
break;
case 384:
algo = SHA3.sha3_384;
break;
case 256:
algo = SHA3.sha3_256;
break;
case 512:
algo = SHA3.sha3_512;
break;
default:
return "Invalid size";
}
/** return algo(input);
* RIPEMD-160 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runRIPEMD160: function (input, args) {
input = CryptoJS.enc.Latin1.parse(input);
return CryptoJS.RIPEMD160(input).toString(CryptoJS.enc.Hex);
}, },
@@ -169,7 +169,181 @@ const Hash = {
* @constant * @constant
* @default * @default
*/ */
HMAC_FUNCTIONS: ["MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "RIPEMD-160"], KECCAK_SIZE: ["512", "384", "256", "224"],
/**
* Keccak operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runKeccak: function (input, args) {
const size = parseInt(args[0], 10);
let algo;
switch (size) {
case 224:
algo = SHA3.keccak224;
break;
case 384:
algo = SHA3.keccak384;
break;
case 256:
algo = SHA3.keccak256;
break;
case 512:
algo = SHA3.keccak512;
break;
default:
return "Invalid size";
}
return algo(input);
},
/**
* @constant
* @default
*/
SHAKE_CAPACITY: ["256", "128"],
/**
* @constant
* @default
*/
SHAKE_SIZE: 512,
/**
* Shake operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runShake: function (input, args) {
const capacity = parseInt(args[0], 10),
size = args[1];
let algo;
if (size < 0)
return "Size must be greater than 0";
switch (capacity) {
case 128:
algo = SHA3.shake128;
break;
case 256:
algo = SHA3.shake256;
break;
default:
return "Invalid size";
}
return algo(input, size);
},
/**
* @constant
* @default
*/
RIPEMD_SIZE: ["320", "256", "160", "128"],
/**
* RIPEMD operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runRIPEMD: function (input, args) {
const size = args[0];
return CryptoApi.hash("ripemd" + size, input, {}).stringify("hex");
},
/**
* HAS-160 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runHAS: function (input, args) {
return CryptoApi.hash("has160", input, {}).stringify("hex");
},
/**
* @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 CryptoApi.hash(variant, input, {}).stringify("hex");
},
/**
* @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 CryptoApi.hash(`snefru-${rounds}-${size}`, input, {}).stringify("hex");
},
/**
* @constant
* @default
*/
HMAC_FUNCTIONS: [
"MD2",
"MD4",
"MD5",
"SHA0",
"SHA1",
"SHA224",
"SHA256",
"SHA384",
"SHA512",
"SHA512/224",
"SHA512/256",
"RIPEMD128",
"RIPEMD160",
"RIPEMD256",
"RIPEMD320",
"HAS160",
"Whirlpool",
"Whirlpool-0",
"Whirlpool-T"
],
/** /**
* HMAC operation. * HMAC operation.
@@ -179,19 +353,12 @@ const Hash = {
* @returns {string} * @returns {string}
*/ */
runHMAC: function (input, args) { runHMAC: function (input, args) {
const hashFunc = args[1]; const password = args[0],
input = CryptoJS.enc.Latin1.parse(input); hashFunc = args[1].toLowerCase(),
const execute = { hmac = CryptoApi.mac("hmac", password, hashFunc, {});
"MD5": CryptoJS.HmacMD5(input, args[0]),
"SHA1": CryptoJS.HmacSHA1(input, args[0]), hmac.update(input);
"SHA224": CryptoJS.HmacSHA224(input, args[0]), return hmac.finalize().stringify("hex");
"SHA256": CryptoJS.HmacSHA256(input, args[0]),
"SHA384": CryptoJS.HmacSHA384(input, args[0]),
"SHA512": CryptoJS.HmacSHA512(input, args[0]),
"SHA3": CryptoJS.HmacSHA3(input, args[0]),
"RIPEMD-160": CryptoJS.HmacRIPEMD160(input, args[0]),
};
return execute[hashFunc].toString(CryptoJS.enc.Hex);
}, },
@@ -207,24 +374,39 @@ const Hash = {
output = "MD2: " + Hash.runMD2(input, []) + output = "MD2: " + Hash.runMD2(input, []) +
"\nMD4: " + Hash.runMD4(input, []) + "\nMD4: " + Hash.runMD4(input, []) +
"\nMD5: " + Hash.runMD5(input, []) + "\nMD5: " + Hash.runMD5(input, []) +
"\nMD6: " + Hash.runMD6(input, []) +
"\nSHA0: " + Hash.runSHA0(input, []) + "\nSHA0: " + Hash.runSHA0(input, []) +
"\nSHA1: " + Hash.runSHA1(input, []) + "\nSHA1: " + Hash.runSHA1(input, []) +
"\nSHA2 224: " + Hash.runSHA224(input, []) + "\nSHA2 224: " + Hash.runSHA2(input, ["224"]) +
"\nSHA2 256: " + Hash.runSHA256(input, []) + "\nSHA2 256: " + Hash.runSHA2(input, ["256"]) +
"\nSHA2 384: " + Hash.runSHA384(input, []) + "\nSHA2 384: " + Hash.runSHA2(input, ["384"]) +
"\nSHA2 512: " + Hash.runSHA512(input, []) + "\nSHA2 512: " + Hash.runSHA2(input, ["512"]) +
"\nSHA3 224: " + Hash.runSHA3(input, ["224"]) + "\nSHA3 224: " + Hash.runSHA3(input, ["224"]) +
"\nSHA3 256: " + Hash.runSHA3(input, ["256"]) + "\nSHA3 256: " + Hash.runSHA3(input, ["256"]) +
"\nSHA3 384: " + Hash.runSHA3(input, ["384"]) + "\nSHA3 384: " + Hash.runSHA3(input, ["384"]) +
"\nSHA3 512: " + Hash.runSHA3(input, ["512"]) + "\nSHA3 512: " + Hash.runSHA3(input, ["512"]) +
"\nRIPEMD-160: " + Hash.runRIPEMD160(input, []) + "\nKeccak 224: " + Hash.runKeccak(input, ["224"]) +
"\nKeccak 256: " + Hash.runKeccak(input, ["256"]) +
"\nKeccak 384: " + Hash.runKeccak(input, ["384"]) +
"\nKeccak 512: " + Hash.runKeccak(input, ["512"]) +
"\nShake 128: " + Hash.runShake(input, ["128", 256]) +
"\nShake 256: " + Hash.runShake(input, ["256", 512]) +
"\nRIPEMD-128: " + Hash.runRIPEMD(input, ["128"]) +
"\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:" + "\n\nChecksums:" +
"\nFletcher-8: " + Checksum.runFletcher8(byteArray, []) + "\nFletcher-8: " + Checksum.runFletcher8(byteArray, []) +
"\nFletcher-16: " + Checksum.runFletcher16(byteArray, []) + "\nFletcher-16: " + Checksum.runFletcher16(byteArray, []) +
"\nFletcher-32: " + Checksum.runFletcher32(byteArray, []) + "\nFletcher-32: " + Checksum.runFletcher32(byteArray, []) +
"\nFletcher-64: " + Checksum.runFletcher64(byteArray, []) + "\nFletcher-64: " + Checksum.runFletcher64(byteArray, []) +
"\nAdler-32: " + Checksum.runAdler32(byteArray, []) + "\nAdler-32: " + Checksum.runAdler32(byteArray, []) +
"\nCRC-32: " + Checksum.runCRC32(byteArray, []); "\nCRC-16: " + Checksum.runCRC16(input, []) +
"\nCRC-32: " + Checksum.runCRC32(input, []);
return output; return output;
}, },

View File

@@ -1,4 +1,3 @@
/* globals app */
import Utils from "../Utils.js"; import Utils from "../Utils.js";
@@ -92,7 +91,7 @@ const Hexdump = {
const w = (width - 13) / 4; const w = (width - 13) / 4;
// w should be the specified width of the hexdump and therefore a round number // w should be the specified width of the hexdump and therefore a round number
if (Math.floor(w) !== w || input.indexOf("\r") !== -1 || output.indexOf(13) !== -1) { if (Math.floor(w) !== w || input.indexOf("\r") !== -1 || output.indexOf(13) !== -1) {
if (app) app.options.attemptHighlight = false; if (ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
} }
return output; return output;
}, },

View File

@@ -283,7 +283,7 @@ const IP = {
baIp.push(decimal & 255); baIp.push(decimal & 255);
break; break;
case "Hex": case "Hex":
baIp = Utils.hexToByteArray(lines[i]); baIp = Utils.fromHex(lines[i]);
break; break;
default: default:
throw "Unsupported input IP format"; throw "Unsupported input IP format";
@@ -516,7 +516,7 @@ const IP = {
"<tr><td>Destination IP address</td><td>" + IP._ipv4ToStr(dstIP) + "</td></tr>"; "<tr><td>Destination IP address</td><td>" + IP._ipv4ToStr(dstIP) + "</td></tr>";
if (ihl > 5) { if (ihl > 5) {
output += "<tr><td>Options</td><td>" + Utils.byteArrayToHex(options) + "</td></tr>"; output += "<tr><td>Options</td><td>" + Utils.toHex(options) + "</td></tr>";
} }
return output + "</table>"; return output + "</table>";

View File

@@ -0,0 +1,124 @@
import * as ExifParser from "exif-parser";
import removeEXIF from "../lib/remove-exif.js";
import Utils from "../Utils.js";
import FileType from "./FileType.js";
/**
* Image operations.
*
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const Image = {
/**
* Extract EXIF operation.
*
* Extracts EXIF data from a byteArray, representing a JPG or a TIFF image.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runExtractEXIF(input, args) {
try {
const bytes = Uint8Array.from(input);
const parser = ExifParser.create(bytes.buffer);
const result = parser.parse();
let lines = [];
for (let tagName in result.tags) {
let value = result.tags[tagName];
lines.push(`${tagName}: ${value}`);
}
const numTags = lines.length;
lines.unshift(`Found ${numTags} tags.\n`);
return lines.join("\n");
} catch (err) {
throw "Could not extract EXIF data from image: " + err;
}
},
/**
* Remove EXIF operation.
*
* Removes EXIF data from a byteArray, representing a JPG.
*
* @author David Moodie [davidmoodie12@gmail.com]
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runRemoveEXIF(input, args) {
// Do nothing if input is empty
if (input.length === 0) return input;
try {
return removeEXIF(input);
} catch (err) {
// Simply return input if no EXIF data is found
if (err === "Exif not found.") return input;
throw "Could not remove EXIF data from image: " + err;
}
},
/**
* @constant
* @default
*/
INPUT_FORMAT: ["Raw", "Base64", "Hex"],
/**
* Render Image operation.
*
* @author n1474335 [n1474335@gmail.com]
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runRenderImage(input, args) {
const inputFormat = args[0];
let dataURI = "data:";
if (!input.length) return "";
// Convert input to raw bytes
switch (inputFormat) {
case "Hex":
input = Utils.fromHex(input);
break;
case "Base64":
// Don't trust the Base64 entered by the user.
// Unwrap it first, then re-encode later.
input = Utils.fromBase64(input, null, "byteArray");
break;
case "Raw":
default:
input = Utils.strToByteArray(input);
break;
}
// Determine file type
const type = FileType.magicType(input);
if (type && type.mime.indexOf("image") === 0) {
dataURI += type.mime + ";";
} else {
throw "Invalid file type";
}
// Add image data to URI
dataURI += "base64," + Utils.toBase64(input);
return "<img src='" + dataURI + "'>";
},
};
export default Image;

View File

@@ -1,4 +1,4 @@
import esprima from "esprima"; import * as esprima from "esprima";
import escodegen from "escodegen"; import escodegen from "escodegen";
import esmangle from "esmangle"; import esmangle from "esmangle";
@@ -62,7 +62,7 @@ const JS = {
tolerant: parseTolerant tolerant: parseTolerant
}; };
result = esprima.parse(input, options); result = esprima.parseScript(input, options);
return JSON.stringify(result, null, 2); return JSON.stringify(result, null, 2);
}, },
@@ -104,7 +104,7 @@ const JS = {
AST; AST;
try { try {
AST = esprima.parse(input, { AST = esprima.parseScript(input, {
range: true, range: true,
tokens: true, tokens: true,
comment: true comment: true
@@ -142,7 +142,7 @@ const JS = {
*/ */
runMinify: function(input, args) { runMinify: function(input, args) {
let result = "", let result = "",
AST = esprima.parse(input), AST = esprima.parseScript(input),
optimisedAST = esmangle.optimize(AST, null), optimisedAST = esmangle.optimize(AST, null),
mangledAST = esmangle.mangle(optimisedAST); mangledAST = esmangle.mangle(optimisedAST);

213
src/core/operations/MS.js Normal file
View File

@@ -0,0 +1,213 @@
/**
* Microsoft operations.
*
* @author bmwhitn [brian.m.whitney@outlook.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const MS = {
/**
* @constant
* @default
*/
D_DECODE: [
"",
"",
"",
"",
"",
"",
"",
"",
"",
"\x57\x6E\x7B",
"\x4A\x4C\x41",
"\x0B\x0B\x0B",
"\x0C\x0C\x0C",
"\x4A\x4C\x41",
"\x0E\x0E\x0E",
"\x0F\x0F\x0F",
"\x10\x10\x10",
"\x11\x11\x11",
"\x12\x12\x12",
"\x13\x13\x13",
"\x14\x14\x14",
"\x15\x15\x15",
"\x16\x16\x16",
"\x17\x17\x17",
"\x18\x18\x18",
"\x19\x19\x19",
"\x1A\x1A\x1A",
"\x1B\x1B\x1B",
"\x1C\x1C\x1C",
"\x1D\x1D\x1D",
"\x1E\x1E\x1E",
"\x1F\x1F\x1F",
"\x2E\x2D\x32",
"\x47\x75\x30",
"\x7A\x52\x21",
"\x56\x60\x29",
"\x42\x71\x5B",
"\x6A\x5E\x38",
"\x2F\x49\x33",
"\x26\x5C\x3D",
"\x49\x62\x58",
"\x41\x7D\x3A",
"\x34\x29\x35",
"\x32\x36\x65",
"\x5B\x20\x39",
"\x76\x7C\x5C",
"\x72\x7A\x56",
"\x43\x7F\x73",
"\x38\x6B\x66",
"\x39\x63\x4E",
"\x70\x33\x45",
"\x45\x2B\x6B",
"\x68\x68\x62",
"\x71\x51\x59",
"\x4F\x66\x78",
"\x09\x76\x5E",
"\x62\x31\x7D",
"\x44\x64\x4A",
"\x23\x54\x6D",
"\x75\x43\x71",
"\x4A\x4C\x41",
"\x7E\x3A\x60",
"\x4A\x4C\x41",
"\x5E\x7E\x53",
"\x40\x4C\x40",
"\x77\x45\x42",
"\x4A\x2C\x27",
"\x61\x2A\x48",
"\x5D\x74\x72",
"\x22\x27\x75",
"\x4B\x37\x31",
"\x6F\x44\x37",
"\x4E\x79\x4D",
"\x3B\x59\x52",
"\x4C\x2F\x22",
"\x50\x6F\x54",
"\x67\x26\x6A",
"\x2A\x72\x47",
"\x7D\x6A\x64",
"\x74\x39\x2D",
"\x54\x7B\x20",
"\x2B\x3F\x7F",
"\x2D\x38\x2E",
"\x2C\x77\x4C",
"\x30\x67\x5D",
"\x6E\x53\x7E",
"\x6B\x47\x6C",
"\x66\x34\x6F",
"\x35\x78\x79",
"\x25\x5D\x74",
"\x21\x30\x43",
"\x64\x23\x26",
"\x4D\x5A\x76",
"\x52\x5B\x25",
"\x63\x6C\x24",
"\x3F\x48\x2B",
"\x7B\x55\x28",
"\x78\x70\x23",
"\x29\x69\x41",
"\x28\x2E\x34",
"\x73\x4C\x09",
"\x59\x21\x2A",
"\x33\x24\x44",
"\x7F\x4E\x3F",
"\x6D\x50\x77",
"\x55\x09\x3B",
"\x53\x56\x55",
"\x7C\x73\x69",
"\x3A\x35\x61",
"\x5F\x61\x63",
"\x65\x4B\x50",
"\x46\x58\x67",
"\x58\x3B\x51",
"\x31\x57\x49",
"\x69\x22\x4F",
"\x6C\x6D\x46",
"\x5A\x4D\x68",
"\x48\x25\x7C",
"\x27\x28\x36",
"\x5C\x46\x70",
"\x3D\x4A\x6E",
"\x24\x32\x7A",
"\x79\x41\x2F",
"\x37\x3D\x5F",
"\x60\x5F\x4B",
"\x51\x4F\x5A",
"\x20\x42\x2C",
"\x36\x65\x57"
],
/**
* @constant
* @default
*/
D_COMBINATION: [
0, 1, 2, 0, 1, 2, 1, 2, 2, 1, 2, 1, 0, 2, 1, 2, 0, 2, 1, 2, 0, 0, 1, 2, 2, 1, 0, 2, 1, 2, 2, 1,
0, 0, 2, 1, 2, 1, 2, 0, 2, 0, 0, 1, 2, 0, 2, 1, 0, 2, 1, 2, 0, 0, 1, 2, 2, 0, 0, 1, 2, 0, 2, 1
],
/**
* Decodes Microsoft Encoded Script files that can be read and executed by cscript.exe/wscript.exe.
* This is a conversion of a Python script that was originally created by Didier Stevens
* (https://DidierStevens.com).
*
* @private
* @param {string} data
* @returns {string}
*/
_decode: function (data) {
let result = [];
let index = -1;
data = data.replace(/@&/g, String.fromCharCode(10))
.replace(/@#/g, String.fromCharCode(13))
.replace(/@\*/g, ">")
.replace(/@!/g, "<")
.replace(/@\$/g, "@");
for (let i = 0; i < data.length; i++) {
let byte = data.charCodeAt(i);
let char = data.charAt(i);
if (byte < 128) {
index++;
}
if ((byte === 9 || byte > 31 && byte < 128) &&
byte !== 60 &&
byte !== 62 &&
byte !== 64) {
char = MS.D_DECODE[byte].charAt(MS.D_COMBINATION[index % 64]);
}
result.push(char);
}
return result.join("");
},
/**
* Microsoft Script Decoder operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runDecodeScript: function (input, args) {
let matcher = /#@~\^.{6}==(.+).{6}==\^#~@/;
let encodedData = matcher.exec(input);
if (encodedData){
return MS._decode(encodedData[1]);
} else {
return "";
}
}
};
export default MS;

View File

@@ -18,25 +18,25 @@ const OS = {
*/ */
runParseUnixPerms: function(input, args) { runParseUnixPerms: function(input, args) {
let perms = { let perms = {
d : false, // directory d: false, // directory
sl : false, // symbolic link sl: false, // symbolic link
np : false, // named pipe np: false, // named pipe
s : false, // socket s: false, // socket
cd : false, // character device cd: false, // character device
bd : false, // block device bd: false, // block device
dr : false, // door dr: false, // door
sb : false, // sticky bit sb: false, // sticky bit
su : false, // setuid su: false, // setuid
sg : false, // setgid sg: false, // setgid
ru : false, // read user ru: false, // read user
wu : false, // write user wu: false, // write user
eu : false, // execute user eu: false, // execute user
rg : false, // read group rg: false, // read group
wg : false, // write group wg: false, // write group
eg : false, // execute group eg: false, // execute group
ro : false, // read other ro: false, // read other
wo : false, // write other wo: false, // write other
eo : false // execute other eo: false // execute other
}, },
d = 0, d = 0,
u = 0, u = 0,

55
src/core/operations/OTP.js Executable file
View File

@@ -0,0 +1,55 @@
import otp from "otp";
import Base64 from "./Base64.js";
/**
* One-Time Password operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const OTP = {
/**
* Generate TOTP operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runTOTP: function(input, args) {
const otpObj = otp({
name: args[0],
keySize: args[1],
codeLength: args[2],
secret: Base64.runTo32(input, []),
epoch: args[3],
timeSlice: args[4]
});
return `URI: ${otpObj.totpURL}\n\nPassword: ${otpObj.totp()}`;
},
/**
* Generate HOTP operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runHOTP: function(input, args) {
const otpObj = otp({
name: args[0],
keySize: args[1],
codeLength: args[2],
secret: Base64.runTo32(input, []),
});
const counter = args[3];
return `URI: ${otpObj.hotpURL}\n\nPassword: ${otpObj.hotp(counter)}`;
},
};
export default OTP;

View File

@@ -27,52 +27,47 @@ const PublicKey = {
* @returns {string} * @returns {string}
*/ */
runParseX509: function (input, args) { runParseX509: function (input, args) {
let cert = new r.X509(),
inputFormat = args[0];
if (!input.length) { if (!input.length) {
return "No input"; return "No input";
} }
let cert = new r.X509(),
inputFormat = args[0];
switch (inputFormat) { switch (inputFormat) {
case "DER Hex": case "DER Hex":
input = input.replace(/\s/g, ""); input = input.replace(/\s/g, "");
cert.hex = input; cert.readCertHex(input);
cert.pem = r.KJUR.asn1.ASN1Util.getPEMStringFromHex(input, "CERTIFICATE");
break; break;
case "PEM": case "PEM":
cert.hex = r.X509.pemToHex(input); cert.readCertPEM(input);
cert.pem = input;
break; break;
case "Base64": case "Base64":
cert.hex = Utils.toHex(Utils.fromBase64(input, null, "byteArray"), ""); cert.readCertHex(Utils.toHex(Utils.fromBase64(input, null, "byteArray"), ""));
cert.pem = r.KJUR.asn1.ASN1Util.getPEMStringFromHex(cert.hex, "CERTIFICATE");
break; break;
case "Raw": case "Raw":
cert.hex = Utils.toHex(Utils.strToByteArray(input), ""); cert.readCertHex(Utils.toHex(Utils.strToByteArray(input), ""));
cert.pem = r.KJUR.asn1.ASN1Util.getPEMStringFromHex(cert.hex, "CERTIFICATE");
break; break;
default: default:
throw "Undefined input format"; throw "Undefined input format";
} }
let version = r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [0, 0, 0]), let sn = cert.getSerialNumberHex(),
sn = cert.getSerialNumberHex(),
algorithm = r.KJUR.asn1.x509.OID.oid2name(r.KJUR.asn1.ASN1Util.oidHexToInt(r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [0, 2, 0]))),
issuer = cert.getIssuerString(), issuer = cert.getIssuerString(),
notBefore = cert.getNotBefore(),
notAfter = cert.getNotAfter(),
subject = cert.getSubjectString(), subject = cert.getSubjectString(),
pkAlgorithm = r.KJUR.asn1.x509.OID.oid2name(r.KJUR.asn1.ASN1Util.oidHexToInt(r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [0, 6, 0, 0]))), pk = cert.getPublicKey(),
pk = r.X509.getPublicKeyFromCertPEM(cert.pem),
pkFields = [], pkFields = [],
pkStr = "", pkStr = "",
certSigAlg = r.KJUR.asn1.x509.OID.oid2name(r.KJUR.asn1.ASN1Util.oidHexToInt(r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [1, 0]))), sig = cert.getSignatureValueHex(),
certSig = r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [2]).substr(2),
sigStr = "", sigStr = "",
extensions = r.ASN1HEX.dump(r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [0, 7])); extensions = cert.getInfo().split("X509v3 Extensions:\n")[1].split("signature")[0];
// Public Key fields // Public Key fields
pkFields.push({
key: "Algorithm",
value: pk.type
});
if (pk.type === "EC") { // ECDSA if (pk.type === "EC") { // ECDSA
pkFields.push({ pkFields.push({
key: "Curve Name", key: "Curve Name",
@@ -123,21 +118,6 @@ const PublicKey = {
}); });
} }
// Signature fields
let breakoutSig = false;
try {
breakoutSig = r.ASN1HEX.dump(certSig).indexOf("SEQUENCE") === 0;
} catch (err) {
// Error processing signature, output without further breakout
}
if (breakoutSig) { // DSA or ECDSA
sigStr = " r: " + PublicKey._formatByteStr(r.ASN1HEX.getDecendantHexVByNthList(certSig, 0, [0]), 16, 18) + "\n" +
" s: " + PublicKey._formatByteStr(r.ASN1HEX.getDecendantHexVByNthList(certSig, 0, [1]), 16, 18) + "\n";
} else { // RSA or unknown
sigStr = " Signature: " + PublicKey._formatByteStr(certSig, 16, 18) + "\n";
}
// Format Public Key fields // Format Public Key fields
for (let i = 0; i < pkFields.length; i++) { for (let i = 0; i < pkFields.length; i++) {
pkStr += " " + pkFields[i].key + ":" + pkStr += " " + pkFields[i].key + ":" +
@@ -148,31 +128,45 @@ const PublicKey = {
); );
} }
// Signature fields
let breakoutSig = false;
try {
breakoutSig = r.ASN1HEX.dump(sig).indexOf("SEQUENCE") === 0;
} catch (err) {
// Error processing signature, output without further breakout
}
if (breakoutSig) { // DSA or ECDSA
sigStr = " r: " + PublicKey._formatByteStr(r.ASN1HEX.getV(sig, 4), 16, 18) + "\n" +
" s: " + PublicKey._formatByteStr(r.ASN1HEX.getV(sig, 48), 16, 18);
} else { // RSA or unknown
sigStr = " Signature: " + PublicKey._formatByteStr(sig, 16, 18);
}
let issuerStr = PublicKey._formatDnStr(issuer, 2), let issuerStr = PublicKey._formatDnStr(issuer, 2),
nbDate = PublicKey._formatDate(notBefore), nbDate = PublicKey._formatDate(cert.getNotBefore()),
naDate = PublicKey._formatDate(notAfter), naDate = PublicKey._formatDate(cert.getNotAfter()),
subjectStr = PublicKey._formatDnStr(subject, 2); subjectStr = PublicKey._formatDnStr(subject, 2);
const output = "Version: " + (parseInt(version, 16) + 1) + " (0x" + version + ")\n" + return `Version: ${cert.version} (0x${Utils.hex(cert.version - 1)})
"Serial number: " + new r.BigInteger(sn, 16).toString() + " (0x" + sn + ")\n" + Serial number: ${new r.BigInteger(sn, 16).toString()} (0x${sn})
"Algorithm ID: " + algorithm + "\n" + Algorithm ID: ${cert.getSignatureAlgorithmField()}
"Validity\n" + Validity
" Not Before: " + nbDate + " (dd-mm-yy hh:mm:ss) (" + notBefore + ")\n" + Not Before: ${nbDate} (dd-mm-yy hh:mm:ss) (${cert.getNotBefore()})
" Not After: " + naDate + " (dd-mm-yy hh:mm:ss) (" + notAfter + ")\n" + Not After: ${naDate} (dd-mm-yy hh:mm:ss) (${cert.getNotAfter()})
"Issuer\n" + Issuer
issuerStr + ${issuerStr}
"Subject\n" + Subject
subjectStr + ${subjectStr}
"Public Key\n" + Public Key
" Algorithm: " + pkAlgorithm + "\n" + ${pkStr.slice(0, -1)}
pkStr + Certificate Signature
"Certificate Signature\n" + Algorithm: ${cert.getSignatureAlgorithmName()}
" Algorithm: " + certSigAlg + "\n" + ${sigStr}
sigStr +
"\nExtensions (parsed ASN.1)\n" +
extensions;
return output; Extensions
${extensions}`;
}, },
@@ -192,7 +186,9 @@ const PublicKey = {
// Add footer so that the KEYUTIL function works // Add footer so that the KEYUTIL function works
input = input + "-----END CERTIFICATE-----"; input = input + "-----END CERTIFICATE-----";
} }
return r.KEYUTIL.getHexFromPEM(input); let cert = new r.X509();
cert.readCertPEM(input);
return cert.hex;
}, },
@@ -270,7 +266,7 @@ const PublicKey = {
*/ */
_formatDnStr: function(dnStr, indent) { _formatDnStr: function(dnStr, indent) {
let output = "", let output = "",
fields = dnStr.split(",/|"), fields = dnStr.substr(1).replace(/([^\\])\//g, "$1$1/").split(/[^\\]\//),
maxKeyLen = 0, maxKeyLen = 0,
key, key,
value, value,
@@ -295,7 +291,7 @@ const PublicKey = {
output += Utils.padLeft(str, indent + str.length, " "); output += Utils.padLeft(str, indent + str.length, " ");
} }
return output; return output.slice(0, -1);
}, },
@@ -345,720 +341,3 @@ const PublicKey = {
}; };
export default PublicKey; export default PublicKey;
/**
* Overwrite X509.hex2dn function so as to join RDNs with a string which can be split on without
* causing problems later (I hope).
*
* @param {string} hDN - Hex DN string
* @returns {string}
*/
r.X509.hex2dn = function(hDN) {
let s = "";
const a = r.ASN1HEX.getPosArrayOfChildren_AtObj(hDN, 0);
for (let i = 0; i < a.length; i++) {
const hRDN = r.ASN1HEX.getHexOfTLV_AtObj(hDN, a[i]);
s = s + ",/|" + r.X509.hex2rdn(hRDN);
}
return s;
};
/**
* Overwrite DN attribute lookup in jsrasign library with a much more complete version from
* https://github.com/nfephp-org/nfephp/blob/master/libs/Common/Certificate/Oids.php
*
* Various duplicates commented out.
*
* @constant
*/
r.X509.DN_ATTRHEX = {
"0603550403" : "commonName",
"0603550404" : "surname",
"0603550406" : "countryName",
"0603550407" : "localityName",
"0603550408" : "stateOrProvinceName",
"0603550409" : "streetAddress",
"060355040a" : "organizationName",
"060355040b" : "organizationalUnitName",
"060355040c" : "title",
"0603550414" : "telephoneNumber",
"060355042a" : "givenName",
// "0603551d0e" : "id-ce-subjectKeyIdentifier",
// "0603551d0f" : "id-ce-keyUsage",
// "0603551d11" : "id-ce-subjectAltName",
// "0603551d13" : "id-ce-basicConstraints",
// "0603551d14" : "id-ce-cRLNumber",
// "0603551d1f" : "id-ce-CRLDistributionPoints",
// "0603551d20" : "id-ce-certificatePolicies",
// "0603551d23" : "id-ce-authorityKeyIdentifier",
// "0603551d25" : "id-ce-extKeyUsage",
// "06032a864886f70d010901" : "Email",
// "06032a864886f70d010101" : "RSAEncryption",
// "06032a864886f70d010102" : "md2WithRSAEncryption",
// "06032a864886f70d010104" : "md5withRSAEncryption",
// "06032a864886f70d010105" : "SHA-1WithRSAEncryption",
// "06032a8648ce380403" : "id-dsa-with-sha-1",
// "06032b06010505070302" : "idKpClientAuth",
// "06032b06010505070304" : "idKpSecurityemail",
"06032b06010505070201" : "idCertificatePolicies",
"06036086480186f8420101" : "netscape-cert-type",
"06036086480186f8420102" : "netscape-base-url",
"06036086480186f8420103" : "netscape-revocation-url",
"06036086480186f8420104" : "netscape-ca-revocation-url",
"06036086480186f8420107" : "netscape-cert-renewal-url",
"06036086480186f8420108" : "netscape-ca-policy-url",
"06036086480186f842010c" : "netscape-ssl-server-name",
"06036086480186f842010d" : "netscape-comment",
"0603604c010201" : "A1",
"0603604c010203" : "A3",
"0603604c01020110" : "Certification Practice Statement pointer",
"0603604c010301" : "Dados do cert parte 1",
"0603604c010305" : "Dados do cert parte 2",
"0603604c010306" : "Dados do cert parte 3",
"06030992268993f22c640119" : "domainComponent",
"06032a24a0f2a07d01010a" : "Signet pilot",
"06032a24a0f2a07d01010b" : "Signet intraNet",
"06032a24a0f2a07d010102" : "Signet personal",
"06032a24a0f2a07d010114" : "Signet securityPolicy",
"06032a24a0f2a07d010103" : "Signet business",
"06032a24a0f2a07d010104" : "Signet legal",
"06032a24a497a35301640101" : "Certificates Australia policyIdentifier",
"06032a85702201" : "seis-cp",
"06032a8570220101" : "SEIS certificatePolicy-s10",
"06032a85702202" : "SEIS pe",
"06032a85702203" : "SEIS at",
"06032a8570220301" : "SEIS at-personalIdentifier",
"06032a8648ce380201" : "holdinstruction-none",
"06032a8648ce380202" : "holdinstruction-callissuer",
"06032a8648ce380203" : "holdinstruction-reject",
"06032a8648ce380401" : "dsa",
"06032a8648ce380403" : "dsaWithSha1",
"06032a8648ce3d01" : "fieldType",
"06032a8648ce3d0101" : "prime-field",
"06032a8648ce3d0102" : "characteristic-two-field",
"06032a8648ce3d010201" : "ecPublicKey",
"06032a8648ce3d010203" : "characteristic-two-basis",
"06032a8648ce3d01020301" : "onBasis",
"06032a8648ce3d01020302" : "tpBasis",
"06032a8648ce3d01020303" : "ppBasis",
"06032a8648ce3d02" : "publicKeyType",
"06032a8648ce3d0201" : "ecPublicKey",
"06032a8648ce3e0201" : "dhPublicNumber",
"06032a864886f67d07" : "nsn",
"06032a864886f67d0741" : "nsn-ce",
"06032a864886f67d074100" : "entrustVersInfo",
"06032a864886f67d0742" : "nsn-alg",
"06032a864886f67d07420a" : "cast5CBC",
"06032a864886f67d07420b" : "cast5MAC",
"06032a864886f67d07420c" : "pbeWithMD5AndCAST5-CBC",
"06032a864886f67d07420d" : "passwordBasedMac",
"06032a864886f67d074203" : "cast3CBC",
"06032a864886f67d0743" : "nsn-oc",
"06032a864886f67d074300" : "entrustUser",
"06032a864886f67d0744" : "nsn-at",
"06032a864886f67d074400" : "entrustCAInfo",
"06032a864886f67d07440a" : "attributeCertificate",
"06032a864886f70d0101" : "pkcs-1",
"06032a864886f70d010101" : "rsaEncryption",
"06032a864886f70d010102" : "md2withRSAEncryption",
"06032a864886f70d010103" : "md4withRSAEncryption",
"06032a864886f70d010104" : "md5withRSAEncryption",
"06032a864886f70d010105" : "sha1withRSAEncryption",
"06032a864886f70d010106" : "rsaOAEPEncryptionSET",
"06032a864886f70d010910020b" : "SMIMEEncryptionKeyPreference",
"06032a864886f70d010c" : "pkcs-12",
"06032a864886f70d010c01" : "pkcs-12-PbeIds",
"06032a864886f70d010c0101" : "pbeWithSHAAnd128BitRC4",
"06032a864886f70d010c0102" : "pbeWithSHAAnd40BitRC4",
"06032a864886f70d010c0103" : "pbeWithSHAAnd3-KeyTripleDES-CBC",
"06032a864886f70d010c0104" : "pbeWithSHAAnd2-KeyTripleDES-CBC",
"06032a864886f70d010c0105" : "pbeWithSHAAnd128BitRC2-CBC",
"06032a864886f70d010c0106" : "pbeWithSHAAnd40BitRC2-CBC",
"06032a864886f70d010c0a" : "pkcs-12Version1",
"06032a864886f70d010c0a01" : "pkcs-12BadIds",
"06032a864886f70d010c0a0101" : "pkcs-12-keyBag",
"06032a864886f70d010c0a0102" : "pkcs-12-pkcs-8ShroudedKeyBag",
"06032a864886f70d010c0a0103" : "pkcs-12-certBag",
"06032a864886f70d010c0a0104" : "pkcs-12-crlBag",
"06032a864886f70d010c0a0105" : "pkcs-12-secretBag",
"06032a864886f70d010c0a0106" : "pkcs-12-safeContentsBag",
"06032a864886f70d010c02" : "pkcs-12-ESPVKID",
"06032a864886f70d010c0201" : "pkcs-12-PKCS8KeyShrouding",
"06032a864886f70d010c03" : "pkcs-12-BagIds",
"06032a864886f70d010c0301" : "pkcs-12-keyBagId",
"06032a864886f70d010c0302" : "pkcs-12-certAndCRLBagId",
"06032a864886f70d010c0303" : "pkcs-12-secretBagId",
"06032a864886f70d010c0304" : "pkcs-12-safeContentsId",
"06032a864886f70d010c0305" : "pkcs-12-pkcs-8ShroudedKeyBagId",
"06032a864886f70d010c04" : "pkcs-12-CertBagID",
"06032a864886f70d010c0401" : "pkcs-12-X509CertCRLBagID",
"06032a864886f70d010c0402" : "pkcs-12-SDSICertBagID",
"06032a864886f70d010c05" : "pkcs-12-OID",
"06032a864886f70d010c0501" : "pkcs-12-PBEID",
"06032a864886f70d010c050101" : "pkcs-12-PBEWithSha1And128BitRC4",
"06032a864886f70d010c050102" : "pkcs-12-PBEWithSha1And40BitRC4",
"06032a864886f70d010c050103" : "pkcs-12-PBEWithSha1AndTripleDESCBC",
"06032a864886f70d010c050104" : "pkcs-12-PBEWithSha1And128BitRC2CBC",
"06032a864886f70d010c050105" : "pkcs-12-PBEWithSha1And40BitRC2CBC",
"06032a864886f70d010c050106" : "pkcs-12-PBEWithSha1AndRC4",
"06032a864886f70d010c050107" : "pkcs-12-PBEWithSha1AndRC2CBC",
"06032a864886f70d010c0502" : "pkcs-12-EnvelopingID",
"06032a864886f70d010c050201" : "pkcs-12-RSAEncryptionWith128BitRC4",
"06032a864886f70d010c050202" : "pkcs-12-RSAEncryptionWith40BitRC4",
"06032a864886f70d010c050203" : "pkcs-12-RSAEncryptionWithTripleDES",
"06032a864886f70d010c0503" : "pkcs-12-SignatureID",
"06032a864886f70d010c050301" : "pkcs-12-RSASignatureWithSHA1Digest",
"06032a864886f70d0103" : "pkcs-3",
"06032a864886f70d010301" : "dhKeyAgreement",
"06032a864886f70d0105" : "pkcs-5",
"06032a864886f70d010501" : "pbeWithMD2AndDES-CBC",
"06032a864886f70d01050a" : "pbeWithSHAAndDES-CBC",
"06032a864886f70d010503" : "pbeWithMD5AndDES-CBC",
"06032a864886f70d010504" : "pbeWithMD2AndRC2-CBC",
"06032a864886f70d010506" : "pbeWithMD5AndRC2-CBC",
"06032a864886f70d010509" : "pbeWithMD5AndXOR",
"06032a864886f70d0107" : "pkcs-7",
"06032a864886f70d010701" : "data",
"06032a864886f70d010702" : "signedData",
"06032a864886f70d010703" : "envelopedData",
"06032a864886f70d010704" : "signedAndEnvelopedData",
"06032a864886f70d010705" : "digestData",
"06032a864886f70d010706" : "encryptedData",
"06032a864886f70d010707" : "dataWithAttributes",
"06032a864886f70d010708" : "encryptedPrivateKeyInfo",
"06032a864886f70d0109" : "pkcs-9",
"06032a864886f70d010901" : "emailAddress",
"06032a864886f70d01090a" : "issuerAndSerialNumber",
"06032a864886f70d01090b" : "passwordCheck",
"06032a864886f70d01090c" : "publicKey",
"06032a864886f70d01090d" : "signingDescription",
"06032a864886f70d01090e" : "extensionReq",
"06032a864886f70d01090f" : "sMIMECapabilities",
"06032a864886f70d01090f01" : "preferSignedData",
"06032a864886f70d01090f02" : "canNotDecryptAny",
"06032a864886f70d01090f03" : "receiptRequest",
"06032a864886f70d01090f04" : "receipt",
"06032a864886f70d01090f05" : "contentHints",
"06032a864886f70d01090f06" : "mlExpansionHistory",
"06032a864886f70d010910" : "id-sMIME",
"06032a864886f70d01091000" : "id-mod",
"06032a864886f70d0109100001" : "id-mod-cms",
"06032a864886f70d0109100002" : "id-mod-ess",
"06032a864886f70d01091001" : "id-ct",
"06032a864886f70d0109100101" : "id-ct-receipt",
"06032a864886f70d01091002" : "id-aa",
"06032a864886f70d0109100201" : "id-aa-receiptRequest",
"06032a864886f70d0109100202" : "id-aa-securityLabel",
"06032a864886f70d0109100203" : "id-aa-mlExpandHistory",
"06032a864886f70d0109100204" : "id-aa-contentHint",
"06032a864886f70d010902" : "unstructuredName",
"06032a864886f70d010914" : "friendlyName",
"06032a864886f70d010915" : "localKeyID",
"06032a864886f70d010916" : "certTypes",
"06032a864886f70d01091601" : "x509Certificate",
"06032a864886f70d01091602" : "sdsiCertificate",
"06032a864886f70d010917" : "crlTypes",
"06032a864886f70d01091701" : "x509Crl",
"06032a864886f70d010903" : "contentType",
"06032a864886f70d010904" : "messageDigest",
"06032a864886f70d010905" : "signingTime",
"06032a864886f70d010906" : "countersignature",
"06032a864886f70d010907" : "challengePassword",
"06032a864886f70d010908" : "unstructuredAddress",
"06032a864886f70d010909" : "extendedCertificateAttributes",
"06032a864886f70d02" : "digestAlgorithm",
"06032a864886f70d0202" : "md2",
"06032a864886f70d0204" : "md4",
"06032a864886f70d0205" : "md5",
"06032a864886f70d03" : "encryptionAlgorithm",
"06032a864886f70d030a" : "desCDMF",
"06032a864886f70d0302" : "rc2CBC",
"06032a864886f70d0303" : "rc2ECB",
"06032a864886f70d0304" : "rc4",
"06032a864886f70d0305" : "rc4WithMAC",
"06032a864886f70d0306" : "DESX-CBC",
"06032a864886f70d0307" : "DES-EDE3-CBC",
"06032a864886f70d0308" : "RC5CBC",
"06032a864886f70d0309" : "RC5-CBCPad",
"06032a864886f7140403" : "microsoftExcel",
"06032a864886f7140404" : "titledWithOID",
"06032a864886f7140405" : "microsoftPowerPoint",
"06032b81051086480954" : "x9-84",
"06032b8105108648095400" : "x9-84-Module",
"06032b810510864809540001" : "x9-84-Biometrics",
"06032b810510864809540002" : "x9-84-CMS",
"06032b810510864809540003" : "x9-84-Identifiers",
"06032b8105108648095401" : "biometric",
"06032b810510864809540100" : "id-unknown-Type",
"06032b810510864809540101" : "id-body-Odor",
"06032b81051086480954010a" : "id-palm",
"06032b81051086480954010b" : "id-retina",
"06032b81051086480954010c" : "id-signature",
"06032b81051086480954010d" : "id-speech-Pattern",
"06032b81051086480954010e" : "id-thermal-Image",
"06032b81051086480954010f" : "id-vein-Pattern",
"06032b810510864809540110" : "id-thermal-Face-Image",
"06032b810510864809540111" : "id-thermal-Hand-Image",
"06032b810510864809540112" : "id-lip-Movement",
"06032b810510864809540113" : "id-gait",
"06032b810510864809540102" : "id-dna",
"06032b810510864809540103" : "id-ear-Shape",
"06032b810510864809540104" : "id-facial-Features",
"06032b810510864809540105" : "id-finger-Image",
"06032b810510864809540106" : "id-finger-Geometry",
"06032b810510864809540107" : "id-hand-Geometry",
"06032b810510864809540108" : "id-iris-Features",
"06032b810510864809540109" : "id-keystroke-Dynamics",
"06032b8105108648095402" : "processing-algorithm",
"06032b8105108648095403" : "matching-method",
"06032b8105108648095404" : "format-Owner",
"06032b810510864809540400" : "cbeff-Owner",
"06032b810510864809540401" : "ibia-Owner",
"06032b81051086480954040101" : "id-ibia-SAFLINK",
"06032b8105108648095404010a" : "id-ibia-SecuGen",
"06032b8105108648095404010b" : "id-ibia-PreciseBiometric",
"06032b8105108648095404010c" : "id-ibia-Identix",
"06032b8105108648095404010d" : "id-ibia-DERMALOG",
"06032b8105108648095404010e" : "id-ibia-LOGICO",
"06032b8105108648095404010f" : "id-ibia-NIST",
"06032b81051086480954040110" : "id-ibia-A3Vision",
"06032b81051086480954040111" : "id-ibia-NEC",
"06032b81051086480954040112" : "id-ibia-STMicroelectronics",
"06032b81051086480954040102" : "id-ibia-Bioscrypt",
"06032b81051086480954040103" : "id-ibia-Visionics",
"06032b81051086480954040104" : "id-ibia-InfineonTechnologiesAG",
"06032b81051086480954040105" : "id-ibia-IridianTechnologies",
"06032b81051086480954040106" : "id-ibia-Veridicom",
"06032b81051086480954040107" : "id-ibia-CyberSIGN",
"06032b81051086480954040108" : "id-ibia-eCryp.",
"06032b81051086480954040109" : "id-ibia-FingerprintCardsAB",
"06032b810510864809540402" : "x9-Owner",
"06032b0e021a05" : "sha",
"06032b0e03020101" : "rsa",
"06032b0e03020a" : "desMAC",
"06032b0e03020b" : "rsaSignature",
"06032b0e03020c" : "dsa",
"06032b0e03020d" : "dsaWithSHA",
"06032b0e03020e" : "mdc2WithRSASignature",
"06032b0e03020f" : "shaWithRSASignature",
"06032b0e030210" : "dhWithCommonModulus",
"06032b0e030211" : "desEDE",
"06032b0e030212" : "sha",
"06032b0e030213" : "mdc-2",
"06032b0e030202" : "md4WitRSA",
"06032b0e03020201" : "sqmod-N",
"06032b0e030214" : "dsaCommon",
"06032b0e030215" : "dsaCommonWithSHA",
"06032b0e030216" : "rsaKeyTransport",
"06032b0e030217" : "keyed-hash-seal",
"06032b0e030218" : "md2WithRSASignature",
"06032b0e030219" : "md5WithRSASignature",
"06032b0e03021a" : "sha1",
"06032b0e03021b" : "dsaWithSHA1",
"06032b0e03021c" : "dsaWithCommonSHA1",
"06032b0e03021d" : "sha-1WithRSAEncryption",
"06032b0e030203" : "md5WithRSA",
"06032b0e03020301" : "sqmod-NwithRSA",
"06032b0e030204" : "md4WithRSAEncryption",
"06032b0e030206" : "desECB",
"06032b0e030207" : "desCBC",
"06032b0e030208" : "desOFB",
"06032b0e030209" : "desCFB",
"06032b0e030301" : "simple-strong-auth-mechanism",
"06032b0e07020101" : "ElGamal",
"06032b0e07020301" : "md2WithRSA",
"06032b0e07020302" : "md2WithElGamal",
"06032b2403" : "algorithm",
"06032b240301" : "encryptionAlgorithm",
"06032b24030101" : "des",
"06032b240301010101" : "desECBPad",
"06032b24030101010101" : "desECBPadISO",
"06032b240301010201" : "desCBCPad",
"06032b24030101020101" : "desCBCPadISO",
"06032b24030102" : "idea",
"06032b2403010201" : "ideaECB",
"06032b240301020101" : "ideaECBPad",
"06032b24030102010101" : "ideaECBPadISO",
"06032b2403010202" : "ideaCBC",
"06032b240301020201" : "ideaCBCPad",
"06032b24030102020101" : "ideaCBCPadISO",
"06032b2403010203" : "ideaOFB",
"06032b2403010204" : "ideaCFB",
"06032b24030103" : "des-3",
"06032b240301030101" : "des-3ECBPad",
"06032b24030103010101" : "des-3ECBPadISO",
"06032b240301030201" : "des-3CBCPad",
"06032b24030103020101" : "des-3CBCPadISO",
"06032b240302" : "hashAlgorithm",
"06032b24030201" : "ripemd160",
"06032b24030202" : "ripemd128",
"06032b24030203" : "ripemd256",
"06032b24030204" : "mdc2singleLength",
"06032b24030205" : "mdc2doubleLength",
"06032b240303" : "signatureAlgorithm",
"06032b24030301" : "rsa",
"06032b2403030101" : "rsaMitSHA-1",
"06032b2403030102" : "rsaMitRIPEMD160",
"06032b24030302" : "ellipticCurve",
"06032b240304" : "signatureScheme",
"06032b24030401" : "iso9796-1",
"06032b2403040201" : "iso9796-2",
"06032b2403040202" : "iso9796-2rsa",
"06032b2404" : "attribute",
"06032b2405" : "policy",
"06032b2406" : "api",
"06032b240601" : "manufacturerSpecific",
"06032b240602" : "functionalitySpecific",
"06032b2407" : "api",
"06032b240701" : "keyAgreement",
"06032b240702" : "keyTransport",
"06032b06010401927c0a0101" : "UNINETT policyIdentifier",
"06032b0601040195180a" : "ICE-TEL policyIdentifier",
"06032b0601040197552001" : "cryptlibEnvelope",
"06032b0601040197552002" : "cryptlibPrivateKey",
"060a2b060104018237" : "Microsoft OID",
"060a2b0601040182370a" : "Crypto 2.0",
"060a2b0601040182370a01" : "certTrustList",
"060a2b0601040182370a0101" : "szOID_SORTED_CTL",
"060a2b0601040182370a0a" : "Microsoft CMC OIDs",
"060a2b0601040182370a0a01" : "szOID_CMC_ADD_ATTRIBUTES",
"060a2b0601040182370a0b" : "Microsoft certificate property OIDs",
"060a2b0601040182370a0b01" : "szOID_CERT_PROP_ID_PREFIX",
"060a2b0601040182370a0c" : "CryptUI",
"060a2b0601040182370a0c01" : "szOID_ANY_APPLICATION_POLICY",
"060a2b0601040182370a02" : "nextUpdateLocation",
"060a2b0601040182370a0301" : "certTrustListSigning",
"060a2b0601040182370a030a" : "szOID_KP_QUALIFIED_SUBORDINATION",
"060a2b0601040182370a030b" : "szOID_KP_KEY_RECOVERY",
"060a2b0601040182370a030c" : "szOID_KP_DOCUMENT_SIGNING",
"060a2b0601040182370a0302" : "timeStampSigning",
"060a2b0601040182370a0303" : "serverGatedCrypto",
"060a2b0601040182370a030301" : "szOID_SERIALIZED",
"060a2b0601040182370a0304" : "encryptedFileSystem",
"060a2b0601040182370a030401" : "szOID_EFS_RECOVERY",
"060a2b0601040182370a0305" : "szOID_WHQL_CRYPTO",
"060a2b0601040182370a0306" : "szOID_NT5_CRYPTO",
"060a2b0601040182370a0307" : "szOID_OEM_WHQL_CRYPTO",
"060a2b0601040182370a0308" : "szOID_EMBEDDED_NT_CRYPTO",
"060a2b0601040182370a0309" : "szOID_ROOT_LIST_SIGNER",
"060a2b0601040182370a0401" : "yesnoTrustAttr",
"060a2b0601040182370a0501" : "szOID_DRM",
"060a2b0601040182370a0502" : "szOID_DRM_INDIVIDUALIZATION",
"060a2b0601040182370a0601" : "szOID_LICENSES",
"060a2b0601040182370a0602" : "szOID_LICENSE_SERVER",
"060a2b0601040182370a07" : "szOID_MICROSOFT_RDN_PREFIX",
"060a2b0601040182370a0701" : "szOID_KEYID_RDN",
"060a2b0601040182370a0801" : "szOID_REMOVE_CERTIFICATE",
"060a2b0601040182370a0901" : "szOID_CROSS_CERT_DIST_POINTS",
"060a2b0601040182370c" : "Catalog",
"060a2b0601040182370c0101" : "szOID_CATALOG_LIST",
"060a2b0601040182370c0102" : "szOID_CATALOG_LIST_MEMBER",
"060a2b0601040182370c0201" : "CAT_NAMEVALUE_OBJID",
"060a2b0601040182370c0202" : "CAT_MEMBERINFO_OBJID",
"060a2b0601040182370d" : "Microsoft PKCS10 OIDs",
"060a2b0601040182370d01" : "szOID_RENEWAL_CERTIFICATE",
"060a2b0601040182370d0201" : "szOID_ENROLLMENT_NAME_VALUE_PAIR",
"060a2b0601040182370d0202" : "szOID_ENROLLMENT_CSP_PROVIDER",
"060a2b0601040182370d0203" : "OS Version",
"060a2b0601040182370f" : "Microsoft Java",
"060a2b06010401823710" : "Microsoft Outlook/Exchange",
"060a2b0601040182371004" : "Outlook Express",
"060a2b06010401823711" : "Microsoft PKCS12 attributes",
"060a2b0601040182371101" : "szOID_LOCAL_MACHINE_KEYSET",
"060a2b06010401823712" : "Microsoft Hydra",
"060a2b06010401823713" : "Microsoft ISPU Test",
"060a2b06010401823702" : "Authenticode",
"060a2b06010401823702010a" : "spcAgencyInfo",
"060a2b06010401823702010b" : "spcStatementType",
"060a2b06010401823702010c" : "spcSpOpusInfo",
"060a2b06010401823702010e" : "certExtensions",
"060a2b06010401823702010f" : "spcPelmageData",
"060a2b060104018237020112" : "SPC_RAW_FILE_DATA_OBJID",
"060a2b060104018237020113" : "SPC_STRUCTURED_STORAGE_DATA_OBJID",
"060a2b060104018237020114" : "spcLink",
"060a2b060104018237020115" : "individualCodeSigning",
"060a2b060104018237020116" : "commercialCodeSigning",
"060a2b060104018237020119" : "spcLink",
"060a2b06010401823702011a" : "spcMinimalCriteriaInfo",
"060a2b06010401823702011b" : "spcFinancialCriteriaInfo",
"060a2b06010401823702011c" : "spcLink",
"060a2b06010401823702011d" : "SPC_HASH_INFO_OBJID",
"060a2b06010401823702011e" : "SPC_SIPINFO_OBJID",
"060a2b060104018237020104" : "spcIndirectDataContext",
"060a2b0601040182370202" : "CTL for Software Publishers Trusted CAs",
"060a2b060104018237020201" : "szOID_TRUSTED_CODESIGNING_CA_LIST",
"060a2b060104018237020202" : "szOID_TRUSTED_CLIENT_AUTH_CA_LIST",
"060a2b060104018237020203" : "szOID_TRUSTED_SERVER_AUTH_CA_LIST",
"060a2b06010401823714" : "Microsoft Enrollment Infrastructure",
"060a2b0601040182371401" : "szOID_AUTO_ENROLL_CTL_USAGE",
"060a2b0601040182371402" : "szOID_ENROLL_CERTTYPE_EXTENSION",
"060a2b060104018237140201" : "szOID_ENROLLMENT_AGENT",
"060a2b060104018237140202" : "szOID_KP_SMARTCARD_LOGON",
"060a2b060104018237140203" : "szOID_NT_PRINCIPAL_NAME",
"060a2b0601040182371403" : "szOID_CERT_MANIFOLD",
"06092b06010401823715" : "Microsoft CertSrv Infrastructure",
"06092b0601040182371501" : "szOID_CERTSRV_CA_VERSION",
"06092b0601040182371514" : "Client Information",
"060a2b06010401823719" : "Microsoft Directory Service",
"060a2b0601040182371901" : "szOID_NTDS_REPLICATION",
"060a2b06010401823703" : "Time Stamping",
"060a2b060104018237030201" : "SPC_TIME_STAMP_REQUEST_OBJID",
"060a2b0601040182371e" : "IIS",
"060a2b0601040182371f" : "Windows updates and service packs",
"060a2b0601040182371f01" : "szOID_PRODUCT_UPDATE",
"060a2b06010401823704" : "Permissions",
"060a2b06010401823728" : "Fonts",
"060a2b06010401823729" : "Microsoft Licensing and Registration",
"060a2b0601040182372a" : "Microsoft Corporate PKI (ITG)",
"060a2b06010401823758" : "CAPICOM",
"060a2b0601040182375801" : "szOID_CAPICOM_VERSION",
"060a2b0601040182375802" : "szOID_CAPICOM_ATTRIBUTE",
"060a2b060104018237580201" : "szOID_CAPICOM_DOCUMENT_NAME",
"060a2b060104018237580202" : "szOID_CAPICOM_DOCUMENT_DESCRIPTION",
"060a2b0601040182375803" : "szOID_CAPICOM_ENCRYPTED_DATA",
"060a2b060104018237580301" : "szOID_CAPICOM_ENCRYPTED_CONTENT",
"06032b0601050507" : "pkix",
"06032b060105050701" : "privateExtension",
"06032b06010505070101" : "authorityInfoAccess",
"06032b06010505070c02" : "CMC Data",
"06032b060105050702" : "policyQualifierIds",
// "06032b06010505070201" : "cps",
"06032b06010505070202" : "unotice",
"06032b060105050703" : "keyPurpose",
"06032b06010505070301" : "serverAuth",
"06032b06010505070302" : "clientAuth",
"06032b06010505070303" : "codeSigning",
"06032b06010505070304" : "emailProtection",
"06032b06010505070305" : "ipsecEndSystem",
"06032b06010505070306" : "ipsecTunnel",
"06032b06010505070307" : "ipsecUser",
"06032b06010505070308" : "timeStamping",
"06032b060105050704" : "cmpInformationTypes",
"06032b06010505070401" : "caProtEncCert",
"06032b06010505070402" : "signKeyPairTypes",
"06032b06010505070403" : "encKeyPairTypes",
"06032b06010505070404" : "preferredSymmAlg",
"06032b06010505070405" : "caKeyUpdateInfo",
"06032b06010505070406" : "currentCRL",
"06032b06010505073001" : "ocsp",
"06032b06010505073002" : "caIssuers",
"06032b06010505080101" : "HMAC-MD5",
"06032b06010505080102" : "HMAC-SHA",
"060360864801650201010a" : "mosaicKeyManagementAlgorithm",
"060360864801650201010b" : "sdnsKMandSigAlgorithm",
"060360864801650201010c" : "mosaicKMandSigAlgorithm",
"060360864801650201010d" : "SuiteASignatureAlgorithm",
"060360864801650201010e" : "SuiteAConfidentialityAlgorithm",
"060360864801650201010f" : "SuiteAIntegrityAlgorithm",
"06036086480186f84201" : "cert-extension",
// "06036086480186f8420101" : "netscape-cert-type",
"06036086480186f842010a" : "EntityLogo",
"06036086480186f842010b" : "UserPicture",
// "06036086480186f842010c" : "netscape-ssl-server-name",
// "06036086480186f842010d" : "netscape-comment",
// "06036086480186f8420102" : "netscape-base-url",
// "06036086480186f8420103" : "netscape-revocation-url",
// "06036086480186f8420104" : "netscape-ca-revocation-url",
// "06036086480186f8420107" : "netscape-cert-renewal-url",
// "06036086480186f8420108" : "netscape-ca-policy-url",
"06036086480186f8420109" : "HomePage-url",
"06036086480186f84202" : "data-type",
"06036086480186f8420201" : "GIF",
"06036086480186f8420202" : "JPEG",
"06036086480186f8420203" : "URL",
"06036086480186f8420204" : "HTML",
"06036086480186f8420205" : "netscape-cert-sequence",
"06036086480186f8420206" : "netscape-cert-url",
"06036086480186f84203" : "directory",
"06036086480186f8420401" : "serverGatedCrypto",
"06036086480186f845010603" : "Unknown Verisign extension",
"06036086480186f845010606" : "Unknown Verisign extension",
"06036086480186f84501070101" : "Verisign certificatePolicy",
"06036086480186f8450107010101" : "Unknown Verisign policy qualifier",
"06036086480186f8450107010102" : "Unknown Verisign policy qualifier",
"0603678105" : "TCPA",
"060367810501" : "tcpaSpecVersion",
"060367810502" : "tcpaAttribute",
"06036781050201" : "tcpaAtTpmManufacturer",
"0603678105020a" : "tcpaAtSecurityQualities",
"0603678105020b" : "tcpaAtTpmProtectionProfile",
"0603678105020c" : "tcpaAtTpmSecurityTarget",
"0603678105020d" : "tcpaAtFoundationProtectionProfile",
"0603678105020e" : "tcpaAtFoundationSecurityTarget",
"0603678105020f" : "tcpaAtTpmIdLabel",
"06036781050202" : "tcpaAtTpmModel",
"06036781050203" : "tcpaAtTpmVersion",
"06036781050204" : "tcpaAtPlatformManufacturer",
"06036781050205" : "tcpaAtPlatformModel",
"06036781050206" : "tcpaAtPlatformVersion",
"06036781050207" : "tcpaAtComponentManufacturer",
"06036781050208" : "tcpaAtComponentModel",
"06036781050209" : "tcpaAtComponentVersion",
"060367810503" : "tcpaProtocol",
"06036781050301" : "tcpaPrttTpmIdProtocol",
"0603672a00" : "contentType",
"0603672a0000" : "PANData",
"0603672a0001" : "PANToken",
"0603672a0002" : "PANOnly",
"0603672a01" : "msgExt",
"0603672a0a" : "national",
"0603672a0a8140" : "Japan",
"0603672a02" : "field",
"0603672a0200" : "fullName",
"0603672a0201" : "givenName",
"0603672a020a" : "amount",
"0603672a0202" : "familyName",
"0603672a0203" : "birthFamilyName",
"0603672a0204" : "placeName",
"0603672a0205" : "identificationNumber",
"0603672a0206" : "month",
"0603672a0207" : "date",
"0603672a02070b" : "accountNumber",
"0603672a02070c" : "passPhrase",
"0603672a0208" : "address",
"0603672a0209" : "telephone",
"0603672a03" : "attribute",
"0603672a0300" : "cert",
"0603672a030000" : "rootKeyThumb",
"0603672a030001" : "additionalPolicy",
"0603672a04" : "algorithm",
"0603672a05" : "policy",
"0603672a0500" : "root",
"0603672a06" : "module",
"0603672a07" : "certExt",
"0603672a0700" : "hashedRootKey",
"0603672a0701" : "certificateType",
"0603672a0702" : "merchantData",
"0603672a0703" : "cardCertRequired",
"0603672a0704" : "tunneling",
"0603672a0705" : "setExtensions",
"0603672a0706" : "setQualifier",
"0603672a08" : "brand",
"0603672a0801" : "IATA-ATA",
"0603672a081e" : "Diners",
"0603672a0822" : "AmericanExpress",
"0603672a0804" : "VISA",
"0603672a0805" : "MasterCard",
"0603672a08ae7b" : "Novus",
"0603672a09" : "vendor",
"0603672a0900" : "GlobeSet",
"0603672a0901" : "IBM",
"0603672a090a" : "Griffin",
"0603672a090b" : "Certicom",
"0603672a090c" : "OSS",
"0603672a090d" : "TenthMountain",
"0603672a090e" : "Antares",
"0603672a090f" : "ECC",
"0603672a0910" : "Maithean",
"0603672a0911" : "Netscape",
"0603672a0912" : "Verisign",
"0603672a0913" : "BlueMoney",
"0603672a0902" : "CyberCash",
"0603672a0914" : "Lacerte",
"0603672a0915" : "Fujitsu",
"0603672a0916" : "eLab",
"0603672a0917" : "Entrust",
"0603672a0918" : "VIAnet",
"0603672a0919" : "III",
"0603672a091a" : "OpenMarket",
"0603672a091b" : "Lexem",
"0603672a091c" : "Intertrader",
"0603672a091d" : "Persimmon",
"0603672a0903" : "Terisa",
"0603672a091e" : "NABLE",
"0603672a091f" : "espace-net",
"0603672a0920" : "Hitachi",
"0603672a0921" : "Microsoft",
"0603672a0922" : "NEC",
"0603672a0923" : "Mitsubishi",
"0603672a0924" : "NCR",
"0603672a0925" : "e-COMM",
"0603672a0926" : "Gemplus",
"0603672a0904" : "RSADSI",
"0603672a0905" : "VeriFone",
"0603672a0906" : "TrinTech",
"0603672a0907" : "BankGate",
"0603672a0908" : "GTE",
"0603672a0909" : "CompuSource",
"0603551d01" : "authorityKeyIdentifier",
"0603551d0a" : "basicConstraints",
"0603551d0b" : "nameConstraints",
"0603551d0c" : "policyConstraints",
"0603551d0d" : "basicConstraints",
"0603551d0e" : "subjectKeyIdentifier",
"0603551d0f" : "keyUsage",
"0603551d10" : "privateKeyUsagePeriod",
"0603551d11" : "subjectAltName",
"0603551d12" : "issuerAltName",
"0603551d13" : "basicConstraints",
"0603551d02" : "keyAttributes",
"0603551d14" : "cRLNumber",
"0603551d15" : "cRLReason",
"0603551d16" : "expirationDate",
"0603551d17" : "instructionCode",
"0603551d18" : "invalidityDate",
"0603551d1a" : "issuingDistributionPoint",
"0603551d1b" : "deltaCRLIndicator",
"0603551d1c" : "issuingDistributionPoint",
"0603551d1d" : "certificateIssuer",
"0603551d03" : "certificatePolicies",
"0603551d1e" : "nameConstraints",
"0603551d1f" : "cRLDistributionPoints",
"0603551d20" : "certificatePolicies",
"0603551d21" : "policyMappings",
"0603551d22" : "policyConstraints",
"0603551d23" : "authorityKeyIdentifier",
"0603551d24" : "policyConstraints",
"0603551d25" : "extKeyUsage",
"0603551d04" : "keyUsageRestriction",
"0603551d05" : "policyMapping",
"0603551d06" : "subtreesConstraint",
"0603551d07" : "subjectAltName",
"0603551d08" : "issuerAltName",
"0603551d09" : "subjectDirectoryAttributes",
"0603550400" : "objectClass",
"0603550401" : "aliasObjectName",
// "060355040c" : "title",
"060355040d" : "description",
"060355040e" : "searchGuide",
"060355040f" : "businessCategory",
"0603550410" : "postalAddress",
"0603550411" : "postalCode",
"0603550412" : "postOfficeBox",
"0603550413" : "physicalDeliveryOfficeName",
"0603550402" : "knowledgeInformation",
// "0603550414" : "telephoneNumber",
"0603550415" : "telexNumber",
"0603550416" : "teletexTerminalIdentifier",
"0603550417" : "facsimileTelephoneNumber",
"0603550418" : "x121Address",
"0603550419" : "internationalISDNNumber",
"060355041a" : "registeredAddress",
"060355041b" : "destinationIndicator",
"060355041c" : "preferredDeliveryMehtod",
"060355041d" : "presentationAddress",
"060355041e" : "supportedApplicationContext",
"060355041f" : "member",
"0603550420" : "owner",
"0603550421" : "roleOccupant",
"0603550422" : "seeAlso",
"0603550423" : "userPassword",
"0603550424" : "userCertificate",
"0603550425" : "caCertificate",
"0603550426" : "authorityRevocationList",
"0603550427" : "certificateRevocationList",
"0603550428" : "crossCertificatePair",
"0603550429" : "givenName",
// "060355042a" : "givenName",
"0603550405" : "serialNumber",
"0603550434" : "supportedAlgorithms",
"0603550435" : "deltaRevocationList",
"060355043a" : "crossCertificatePair",
// "0603550409" : "streetAddress",
"06035508" : "X.500-Algorithms",
"0603550801" : "X.500-Alg-Encryption",
"060355080101" : "rsa",
"0603604c0101" : "DPC"
};

View File

@@ -61,7 +61,7 @@ const QuotedPrintable = {
* @returns {byteArray} * @returns {byteArray}
*/ */
runFrom: function (input, args) { runFrom: function (input, args) {
const str = input.replace(/\=(?:\r?\n|$)/g, ""); const str = input.replace(/=(?:\r?\n|$)/g, "");
return QuotedPrintable.mimeDecode(str); return QuotedPrintable.mimeDecode(str);
}, },
@@ -73,7 +73,7 @@ const QuotedPrintable = {
* @returns {byteArray} * @returns {byteArray}
*/ */
mimeDecode: function(str) { mimeDecode: function(str) {
let encodedBytesCount = (str.match(/\=[\da-fA-F]{2}/g) || []).length, let encodedBytesCount = (str.match(/=[\da-fA-F]{2}/g) || []).length,
bufferLength = str.length - encodedBytesCount * 2, bufferLength = str.length - encodedBytesCount * 2,
chr, hex, chr, hex,
buffer = new Array(bufferLength), buffer = new Array(bufferLength),
@@ -219,21 +219,21 @@ const QuotedPrintable = {
result += line; result += line;
pos += line.length; pos += line.length;
continue; continue;
} else if (line.length > lineLengthMax - lineMargin && (match = line.substr(-lineMargin).match(/[ \t\.,!\?][^ \t\.,!\?]*$/))) { } else if (line.length > lineLengthMax - lineMargin && (match = line.substr(-lineMargin).match(/[ \t.,!?][^ \t.,!?]*$/))) {
// truncate to nearest space // truncate to nearest space
line = line.substr(0, line.length - (match[0].length - 1)); line = line.substr(0, line.length - (match[0].length - 1));
} else if (line.substr(-1) === "\r") { } else if (line.substr(-1) === "\r") {
line = line.substr(0, line.length - 1); line = line.substr(0, line.length - 1);
} else { } else {
if (line.match(/\=[\da-f]{0,2}$/i)) { if (line.match(/=[\da-f]{0,2}$/i)) {
// push incomplete encoding sequences to the next line // push incomplete encoding sequences to the next line
if ((match = line.match(/\=[\da-f]{0,1}$/i))) { if ((match = line.match(/=[\da-f]{0,1}$/i))) {
line = line.substr(0, line.length - match[0].length); line = line.substr(0, line.length - match[0].length);
} }
// ensure that utf-8 sequences are not split // ensure that utf-8 sequences are not split
while (line.length > 3 && line.length < len - pos && !line.match(/^(?:=[\da-f]{2}){1,4}$/i) && (match = line.match(/\=[\da-f]{2}$/ig))) { while (line.length > 3 && line.length < len - pos && !line.match(/^(?:=[\da-f]{2}){1,4}$/i) && (match = line.match(/=[\da-f]{2}$/ig))) {
code = parseInt(match[0].substr(1, 2), 16); code = parseInt(match[0].substr(1, 2), 16);
if (code < 128) { if (code < 128) {
break; break;
@@ -250,7 +250,7 @@ const QuotedPrintable = {
} }
if (pos + line.length < len && line.substr(-1) !== "\n") { if (pos + line.length < len && line.substr(-1) !== "\n") {
if (line.length === 76 && line.match(/\=[\da-f]{2}$/i)) { if (line.length === 76 && line.match(/=[\da-f]{2}$/i)) {
line = line.substr(0, line.length - 3); line = line.substr(0, line.length - 3);
} else if (line.length === 76) { } else if (line.length === 76) {
line = line.substr(0, line.length - 1); line = line.substr(0, line.length - 1);

View File

@@ -20,7 +20,7 @@ const Rotate = {
* @constant * @constant
* @default * @default
*/ */
ROTATE_WHOLE: false, ROTATE_CARRY: false,
/** /**
* Runs rotation operations across the input data. * Runs rotation operations across the input data.
@@ -53,7 +53,7 @@ const Rotate = {
*/ */
runRotr: function(input, args) { runRotr: function(input, args) {
if (args[1]) { if (args[1]) {
return Rotate._rotrWhole(input, args[0]); return Rotate._rotrCarry(input, args[0]);
} else { } else {
return Rotate._rot(input, args[0], Rotate._rotr); return Rotate._rot(input, args[0], Rotate._rotr);
} }
@@ -69,7 +69,7 @@ const Rotate = {
*/ */
runRotl: function(input, args) { runRotl: function(input, args) {
if (args[1]) { if (args[1]) {
return Rotate._rotlWhole(input, args[0]); return Rotate._rotlCarry(input, args[0]);
} else { } else {
return Rotate._rot(input, args[0], Rotate._rotl); return Rotate._rot(input, args[0], Rotate._rotl);
} }
@@ -135,7 +135,7 @@ const Rotate = {
/** /**
* ROT47 operation. * ROT47 operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {byteArray} input * @param {byteArray} input
* @param {Object[]} args * @param {Object[]} args
* @returns {byteArray} * @returns {byteArray}
@@ -197,7 +197,7 @@ const Rotate = {
* @param {number} amount * @param {number} amount
* @returns {byteArray} * @returns {byteArray}
*/ */
_rotrWhole: function(data, amount) { _rotrCarry: function(data, amount) {
let carryBits = 0, let carryBits = 0,
newByte, newByte,
result = []; result = [];
@@ -223,7 +223,7 @@ const Rotate = {
* @param {number} amount * @param {number} amount
* @returns {byteArray} * @returns {byteArray}
*/ */
_rotlWhole: function(data, amount) { _rotlCarry: function(data, amount) {
let carryBits = 0, let carryBits = 0,
newByte, newByte,
result = []; result = [];

View File

@@ -26,7 +26,7 @@ const SeqUtils = {
* @constant * @constant
* @default * @default
*/ */
SORT_ORDER: ["Alphabetical (case sensitive)", "Alphabetical (case insensitive)", "IP address"], SORT_ORDER: ["Alphabetical (case sensitive)", "Alphabetical (case insensitive)", "IP address", "Numeric"],
/** /**
* Sort operation. * Sort operation.
@@ -47,6 +47,8 @@ const SeqUtils = {
sorted = sorted.sort(SeqUtils._caseInsensitiveSort); sorted = sorted.sort(SeqUtils._caseInsensitiveSort);
} else if (order === "IP address") { } else if (order === "IP address") {
sorted = sorted.sort(SeqUtils._ipSort); sorted = sorted.sort(SeqUtils._ipSort);
} else if (order === "Numeric") {
sorted = sorted.sort(SeqUtils._numericSort);
} }
if (sortReverse) sorted.reverse(); if (sortReverse) sorted.reverse();
@@ -221,6 +223,35 @@ const SeqUtils = {
return a_ - b_; return a_ - b_;
}, },
/**
* Comparison operation for sorting of numeric values.
*
* @author Chris van Marle
* @private
* @param {string} a
* @param {string} b
* @returns {number}
*/
_numericSort: function _numericSort(a, b) {
let a_ = a.split(/([^\d]+)/),
b_ = b.split(/([^\d]+)/);
for (let i = 0; i < a_.length && i < b.length; ++i) {
if (isNaN(a_[i]) && !isNaN(b_[i])) return 1; // Numbers after non-numbers
if (!isNaN(a_[i]) && isNaN(b_[i])) return -1;
if (isNaN(a_[i]) && isNaN(b_[i])) {
let ret = a_[i].localeCompare(b_[i]); // Compare strings
if (ret !== 0) return ret;
}
if (!isNaN(a_[i]) && !isNaN(a_[i])) { // Compare numbers
if (a_[i] - b_[i] !== 0) return a_[i] - b_[i];
}
}
return 0;
},
}; };
export default SeqUtils; export default SeqUtils;

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

@@ -1,5 +1,4 @@
import Utils from "../Utils.js"; import Utils from "../Utils.js";
import * as JsDiff from "diff";
/** /**
@@ -36,11 +35,11 @@ const StrUtils = {
}, },
{ {
name: "URL", name: "URL",
value: "([A-Za-z]+://)([-\\w]+(?:\\.\\w[-\\w]*)+)(:\\d+)?(/[^.!,?;\"\\x27<>()\\[\\]{}\\s\\x7F-\\xFF]*(?:[.!,?]+[^.!,?;\"\\x27<>()\\[\\]{}\\s\\x7F-\\xFF]+)*)?" value: "([A-Za-z]+://)([-\\w]+(?:\\.\\w[-\\w]*)+)(:\\d+)?(/[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]*(?:[.!,?]+[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]+)*)?"
}, },
{ {
name: "Domain", name: "Domain",
value: "(?:(https?):\\/\\/)?([-\\w.]+)\\.(com|net|org|biz|info|co|uk|onion|int|mobi|name|edu|gov|mil|eu|ac|ae|af|de|ca|ch|cn|cy|es|gb|hk|il|in|io|tv|me|nl|no|nz|ro|ru|tr|us|az|ir|kz|uz|pk)+" value: "\\b((?=[a-z0-9-]{1,63}\\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,63}\\b"
}, },
{ {
name: "Windows file path", name: "Windows file path",
@@ -193,17 +192,17 @@ const StrUtils = {
* @constant * @constant
* @default * @default
*/ */
FIND_REPLACE_GLOBAL : true, FIND_REPLACE_GLOBAL: true,
/** /**
* @constant * @constant
* @default * @default
*/ */
FIND_REPLACE_CASE : false, FIND_REPLACE_CASE: false,
/** /**
* @constant * @constant
* @default * @default
*/ */
FIND_REPLACE_MULTILINE : true, FIND_REPLACE_MULTILINE: true,
/** /**
* Find / Replace operation. * Find / Replace operation.
@@ -227,14 +226,16 @@ const StrUtils = {
if (type === "Regex") { if (type === "Regex") {
find = new RegExp(find, modifiers); find = new RegExp(find, modifiers);
} else if (type.indexOf("Extended") === 0) { return input.replace(find, replace);
}
if (type.indexOf("Extended") === 0) {
find = Utils.parseEscapedChars(find); find = Utils.parseEscapedChars(find);
} }
return input.replace(find, replace, modifiers); find = new RegExp(Utils.escapeRegex(find), modifiers);
// Non-standard addition of flags in the third argument. This will work in Firefox but
// probably nowhere else. The purpose is to allow global matching when the `find` parameter return input.replace(find, replace);
// is just a string.
}, },
@@ -292,83 +293,6 @@ const StrUtils = {
}, },
/**
* @constant
* @default
*/
DIFF_SAMPLE_DELIMITER: "\\n\\n",
/**
* @constant
* @default
*/
DIFF_BY: ["Character", "Word", "Line", "Sentence", "CSS", "JSON"],
/**
* Diff operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runDiff: function(input, args) {
let sampleDelim = args[0],
diffBy = args[1],
showAdded = args[2],
showRemoved = args[3],
ignoreWhitespace = args[4],
samples = input.split(sampleDelim),
output = "",
diff;
if (!samples || samples.length !== 2) {
return "Incorrect number of samples, perhaps you need to modify the sample delimiter or add more samples?";
}
switch (diffBy) {
case "Character":
diff = JsDiff.diffChars(samples[0], samples[1]);
break;
case "Word":
if (ignoreWhitespace) {
diff = JsDiff.diffWords(samples[0], samples[1]);
} else {
diff = JsDiff.diffWordsWithSpace(samples[0], samples[1]);
}
break;
case "Line":
if (ignoreWhitespace) {
diff = JsDiff.diffTrimmedLines(samples[0], samples[1]);
} else {
diff = JsDiff.diffLines(samples[0], samples[1]);
}
break;
case "Sentence":
diff = JsDiff.diffSentences(samples[0], samples[1]);
break;
case "CSS":
diff = JsDiff.diffCss(samples[0], samples[1]);
break;
case "JSON":
diff = JsDiff.diffJson(samples[0], samples[1]);
break;
default:
return "Invalid 'Diff by' option.";
}
for (let i = 0; i < diff.length; i++) {
if (diff[i].added) {
if (showAdded) output += "<span class='hlgreen'>" + Utils.escapeHtml(diff[i].value) + "</span>";
} else if (diff[i].removed) {
if (showRemoved) output += "<span class='hlred'>" + Utils.escapeHtml(diff[i].value) + "</span>";
} else {
output += Utils.escapeHtml(diff[i].value);
}
}
return output;
},
/** /**
* @constant * @constant
* @default * @default
@@ -422,7 +346,7 @@ const StrUtils = {
} }
if (match && !inMatch) { if (match && !inMatch) {
outputs[s] += "<span class='hlgreen'>" + Utils.escapeHtml(samples[s][i]); outputs[s] += "<span class='hl5'>" + Utils.escapeHtml(samples[s][i]);
if (samples[s].length === i + 1) outputs[s] += "</span>"; if (samples[s].length === i + 1) outputs[s] += "</span>";
if (s === samples.length - 1) inMatch = true; if (s === samples.length - 1) inMatch = true;
} else if (!match && inMatch) { } else if (!match && inMatch) {
@@ -448,14 +372,84 @@ const StrUtils = {
/** /**
* Parse escaped string operation. * @constant
* @default
*/
ESCAPE_REPLACEMENTS: [
{"escaped": "\\\\", "unescaped": "\\"}, // Must be first
{"escaped": "\\'", "unescaped": "'"},
{"escaped": "\\\"", "unescaped": "\""},
{"escaped": "\\n", "unescaped": "\n"},
{"escaped": "\\r", "unescaped": "\r"},
{"escaped": "\\t", "unescaped": "\t"},
{"escaped": "\\b", "unescaped": "\b"},
{"escaped": "\\f", "unescaped": "\f"},
],
/**
* Escape string operation.
*
* @author Vel0x [dalemy@microsoft.com]
* *
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
*
* @example
* StrUtils.runUnescape("Don't do that", [])
* > "Don\'t do that"
* StrUtils.runUnescape(`Hello
* World`, [])
* > "Hello\nWorld"
*/ */
runParseEscapedString: function(input, args) { runEscape: function(input, args) {
return Utils.parseEscapedChars(input); return StrUtils._replaceByKeys(input, "unescaped", "escaped");
},
/**
* Unescape string operation.
*
* @author Vel0x [dalemy@microsoft.com]
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @example
* StrUtils.runUnescape("Don\'t do that", [])
* > "Don't do that"
* StrUtils.runUnescape("Hello\nWorld", [])
* > `Hello
* World`
*/
runUnescape: function(input, args) {
return StrUtils._replaceByKeys(input, "escaped", "unescaped");
},
/**
* Replaces all matching tokens in ESCAPE_REPLACEMENTS with the correction. The
* ordering is determined by the patternKey and the replacementKey.
*
* @author Vel0x [dalemy@microsoft.com]
* @author Matt C [matt@artemisbot.uk]
*
* @param {string} input
* @param {string} pattern_key
* @param {string} replacement_key
* @returns {string}
*/
_replaceByKeys: function(input, patternKey, replacementKey) {
let output = input;
// Catch the \\x encoded characters
if (patternKey === "escaped") output = Utils.parseEscapedChars(input);
StrUtils.ESCAPE_REPLACEMENTS.forEach(replacement => {
output = output.split(replacement[patternKey]).join(replacement[replacementKey]);
});
return output;
}, },
@@ -471,19 +465,19 @@ const StrUtils = {
number = args[1]; number = args[1];
delimiter = Utils.charRep[delimiter]; delimiter = Utils.charRep[delimiter];
let splitInput = input.split(delimiter); const splitInput = input.split(delimiter);
return splitInput return splitInput
.filter((line, lineIndex) => { .filter((line, lineIndex) => {
lineIndex += 1; lineIndex += 1;
if (number < 0) { if (number < 0) {
return lineIndex <= splitInput.length + number; return lineIndex <= splitInput.length + number;
} else { } else {
return lineIndex <= number; return lineIndex <= number;
} }
}) })
.join(delimiter); .join(delimiter);
}, },
@@ -499,19 +493,19 @@ const StrUtils = {
number = args[1]; number = args[1];
delimiter = Utils.charRep[delimiter]; delimiter = Utils.charRep[delimiter];
let splitInput = input.split(delimiter); const splitInput = input.split(delimiter);
return splitInput return splitInput
.filter((line, lineIndex) => { .filter((line, lineIndex) => {
lineIndex += 1; lineIndex += 1;
if (number < 0) { if (number < 0) {
return lineIndex > -number; return lineIndex > -number;
} else { } else {
return lineIndex > splitInput.length - number; return lineIndex > splitInput.length - number;
} }
}) })
.join(delimiter); .join(delimiter);
}, },

View File

@@ -16,32 +16,32 @@ const Tidy = {
* @constant * @constant
* @default * @default
*/ */
REMOVE_SPACES : true, REMOVE_SPACES: true,
/** /**
* @constant * @constant
* @default * @default
*/ */
REMOVE_CARIAGE_RETURNS : true, REMOVE_CARIAGE_RETURNS: true,
/** /**
* @constant * @constant
* @default * @default
*/ */
REMOVE_LINE_FEEDS : true, REMOVE_LINE_FEEDS: true,
/** /**
* @constant * @constant
* @default * @default
*/ */
REMOVE_TABS : true, REMOVE_TABS: true,
/** /**
* @constant * @constant
* @default * @default
*/ */
REMOVE_FORM_FEEDS : true, REMOVE_FORM_FEEDS: true,
/** /**
* @constant * @constant
* @default * @default
*/ */
REMOVE_FULL_STOPS : false, REMOVE_FULL_STOPS: false,
/** /**
* Remove whitespace operation. * Remove whitespace operation.
@@ -89,17 +89,17 @@ const Tidy = {
* @constant * @constant
* @default * @default
*/ */
APPLY_TO_EACH_LINE : false, APPLY_TO_EACH_LINE: false,
/** /**
* @constant * @constant
* @default * @default
*/ */
DROP_START : 0, DROP_START: 0,
/** /**
* @constant * @constant
* @default * @default
*/ */
DROP_LENGTH : 5, DROP_LENGTH: 5,
/** /**
* Drop bytes operation. * Drop bytes operation.
@@ -200,17 +200,17 @@ const Tidy = {
* @constant * @constant
* @default * @default
*/ */
PAD_POSITION : ["Start", "End"], PAD_POSITION: ["Start", "End"],
/** /**
* @constant * @constant
* @default * @default
*/ */
PAD_LENGTH : 5, PAD_LENGTH: 5,
/** /**
* @constant * @constant
* @default * @default
*/ */
PAD_CHAR : " ", PAD_CHAR: " ",
/** /**
* Pad lines operation. * Pad lines operation.

View File

@@ -127,7 +127,7 @@ const URL_ = {
.replace(/\(/g, "%28") .replace(/\(/g, "%28")
.replace(/\)/g, "%29") .replace(/\)/g, "%29")
.replace(/\*/g, "%2A") .replace(/\*/g, "%2A")
.replace(/\-/g, "%2D") .replace(/-/g, "%2D")
.replace(/\./g, "%2E") .replace(/\./g, "%2E")
.replace(/_/g, "%5F") .replace(/_/g, "%5F")
.replace(/~/g, "%7E"); .replace(/~/g, "%7E");

View File

@@ -9,17 +9,19 @@ require("babel-polyfill");
const Chef = require("../core/Chef.js").default; const Chef = require("../core/Chef.js").default;
const CyberChef = module.exports = { const CyberChef = {
bake: function(input, recipeConfig) { bake: function(input, recipeConfig) {
this.chef = new Chef(); this.chef = new Chef();
return this.chef.bake( return this.chef.bake(
input, input,
recipeConfig, recipeConfig,
{}, {},
0, 0,
false false
); );
} }
}; };
module.exports = CyberChef;

View File

@@ -1,5 +1,4 @@
import Utils from "../core/Utils.js"; import Utils from "../core/Utils.js";
import Chef from "../core/Chef.js";
import Manager from "./Manager.js"; import Manager from "./Manager.js";
import HTMLCategory from "./HTMLCategory.js"; import HTMLCategory from "./HTMLCategory.js";
import HTMLOperation from "./HTMLOperation.js"; import HTMLOperation from "./HTMLOperation.js";
@@ -21,21 +20,19 @@ import Split from "split.js";
* @param {Object} options - Default setting for app options. * @param {Object} options - Default setting for app options.
*/ */
const App = function(categories, operations, defaultFavourites, defaultOptions) { const App = function(categories, operations, defaultFavourites, defaultOptions) {
this.categories = categories; this.categories = categories;
this.operations = operations; this.operations = operations;
this.dfavourites = defaultFavourites; this.dfavourites = defaultFavourites;
this.doptions = defaultOptions; this.doptions = defaultOptions;
this.options = Utils.extend({}, defaultOptions); this.options = Utils.extend({}, defaultOptions);
this.chef = new Chef(); this.manager = new Manager(this);
this.manager = new Manager(this);
this.baking = false; this.baking = false;
this.autoBake_ = false; this.autoBake_ = false;
this.progress = 0; this.autoBakePause = false;
this.ingId = 0; this.progress = 0;
this.ingId = 0;
window.chef = this.chef;
}; };
@@ -53,6 +50,37 @@ App.prototype.setup = function() {
this.resetLayout(); this.resetLayout();
this.setCompileMessage(); this.setCompileMessage();
this.loadURIParams(); this.loadURIParams();
this.appLoaded = true;
this.loaded();
};
/**
* Fires once all setup activities have completed.
*
* @fires Manager#apploaded
*/
App.prototype.loaded = function() {
// Check that both the app and the worker have loaded successfully, and that
// we haven't already loaded before attempting to remove the loading screen.
if (!this.workerLoaded || !this.appLoaded ||
!document.getElementById("loader-wrapper")) return;
// Trigger CSS animations to remove preloader
document.body.classList.add("loaded");
// Wait for animations to complete then remove the preloader and loaded style
// so that the animations for existing elements don't play again.
setTimeout(function() {
document.getElementById("loader-wrapper").remove();
document.body.classList.remove("loaded");
}, 1000);
// Clear the loading message interval
clearInterval(window.loadingMsgsInt);
document.dispatchEvent(this.manager.apploaded);
}; };
@@ -69,76 +97,24 @@ App.prototype.handleError = function(err) {
/** /**
* Updates the UI to show if baking is in process or not. * Asks the ChefWorker to bake the current input using the current recipe.
*
* @param {bakingStatus}
*/
App.prototype.setBakingStatus = function(bakingStatus) {
this.baking = bakingStatus;
let inputLoadingIcon = document.querySelector("#input .title .loading-icon"),
outputLoadingIcon = document.querySelector("#output .title .loading-icon"),
outputElement = document.querySelector("#output-text");
if (bakingStatus) {
inputLoadingIcon.style.display = "inline-block";
outputLoadingIcon.style.display = "inline-block";
outputElement.classList.add("disabled");
outputElement.disabled = true;
} else {
inputLoadingIcon.style.display = "none";
outputLoadingIcon.style.display = "none";
outputElement.classList.remove("disabled");
outputElement.disabled = false;
}
};
/**
* Calls the Chef to bake the current input using the current recipe.
* *
* @param {boolean} [step] - Set to true if we should only execute one operation instead of the * @param {boolean} [step] - Set to true if we should only execute one operation instead of the
* whole recipe. * whole recipe.
*/ */
App.prototype.bake = async function(step) { App.prototype.bake = function(step) {
let response;
if (this.baking) return; if (this.baking) return;
this.setBakingStatus(true); // Reset attemptHighlight flag
this.options.attemptHighlight = true;
try { this.manager.worker.bake(
response = await this.chef.bake( this.getInput(), // The user's input
this.getInput(), // The user's input this.getRecipeConfig(), // The configuration of the recipe
this.getRecipeConfig(), // The configuration of the recipe this.options, // Options set by the user
this.options, // Options set by the user this.progress, // The current position in the recipe
this.progress, // The current position in the recipe step // Whether or not to take one step or execute the whole recipe
step // Whether or not to take one step or execute the whole recipe );
);
} catch (err) {
this.handleError(err);
}
this.setBakingStatus(false);
if (!response) return;
if (response.error) {
this.handleError(response.error);
}
this.options = response.options;
this.dishStr = response.type === "html" ? Utils.stripHtmlTags(response.result, true) : response.result;
this.progress = response.progress;
this.manager.recipe.updateBreakpointIndicator(response.progress);
this.manager.output.set(response.result, response.type, response.duration);
// If baking took too long, disable auto-bake
if (response.duration > this.options.autoBakeThreshold && this.autoBake_) {
this.manager.controls.setAutoBake(false);
this.alert("Baking took longer than " + this.options.autoBakeThreshold +
"ms, Auto Bake has been disabled.", "warning", 5000);
}
}; };
@@ -146,30 +122,36 @@ App.prototype.bake = async function(step) {
* Runs Auto Bake if it is set. * Runs Auto Bake if it is set.
*/ */
App.prototype.autoBake = function() { App.prototype.autoBake = function() {
if (this.autoBake_) { // 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) {
this.bake(); this.bake();
} else {
this.manager.controls.showStaleIndicator();
} }
}; };
/** /**
* Runs a silent bake forcing the browser to load and cache all the relevant JavaScript code needed * Runs a silent bake, forcing the browser to load and cache all the relevant JavaScript code needed
* to do a real bake. * to do a real bake.
* *
* The output will not be modified (hence "silent" bake). This will only actually execute the * The output will not be modified (hence "silent" bake). This will only actually execute the recipe
* recipe if auto-bake is enabled, otherwise it will just load the recipe, ingredients and dish. * if auto-bake is enabled, otherwise it will just wake up the ChefWorker with an empty recipe.
*
* @returns {number} - The number of miliseconds it took to run the silent bake.
*/ */
App.prototype.silentBake = function() { App.prototype.silentBake = function() {
let startTime = new Date().getTime(), let recipeConfig = [];
recipeConfig = this.getRecipeConfig();
if (this.autoBake_) { if (this.autoBake_) {
this.chef.silentBake(recipeConfig); // If auto-bake is not enabled we don't want to actually run the recipe as it may be disabled
// for a good reason.
recipeConfig = this.getRecipeConfig();
} }
return new Date().getTime() - startTime; this.manager.worker.silentBake(recipeConfig);
}; };
@@ -179,13 +161,7 @@ App.prototype.silentBake = function() {
* @returns {string} * @returns {string}
*/ */
App.prototype.getInput = function() { App.prototype.getInput = function() {
const input = this.manager.input.get(); return this.manager.input.get();
// Save to session storage in case we need to restore it later
sessionStorage.setItem("inputLength", input.length);
sessionStorage.setItem("input", input);
return input;
}; };
@@ -195,8 +171,6 @@ App.prototype.getInput = function() {
* @param {string} input - The string to set the input to * @param {string} input - The string to set the input to
*/ */
App.prototype.setInput = function(input) { App.prototype.setInput = function(input) {
sessionStorage.setItem("inputLength", input.length);
sessionStorage.setItem("input", input);
this.manager.input.set(input); this.manager.input.set(input);
}; };
@@ -247,7 +221,7 @@ App.prototype.populateOperationsList = function() {
App.prototype.initialiseSplitter = function() { App.prototype.initialiseSplitter = function() {
this.columnSplitter = Split(["#operations", "#recipe", "#IO"], { this.columnSplitter = Split(["#operations", "#recipe", "#IO"], {
sizes: [20, 30, 50], sizes: [20, 30, 50],
minSize: [240, 325, 440], minSize: [240, 325, 450],
gutterSize: 4, gutterSize: 4,
onDrag: function() { onDrag: function() {
this.manager.controls.adjustWidth(); this.manager.controls.adjustWidth();
@@ -271,7 +245,7 @@ App.prototype.initialiseSplitter = function() {
App.prototype.loadLocalStorage = function() { App.prototype.loadLocalStorage = function() {
// Load options // Load options
let lOptions; let lOptions;
if (localStorage.options !== undefined) { if (this.isLocalStorageAvailable() && localStorage.options !== undefined) {
lOptions = JSON.parse(localStorage.options); lOptions = JSON.parse(localStorage.options);
} }
this.manager.options.load(lOptions); this.manager.options.load(lOptions);
@@ -287,13 +261,17 @@ App.prototype.loadLocalStorage = function() {
* If the user currently has no saved favourites, the defaults from the view constructor are used. * If the user currently has no saved favourites, the defaults from the view constructor are used.
*/ */
App.prototype.loadFavourites = function() { App.prototype.loadFavourites = function() {
let favourites = localStorage.favourites && let favourites;
localStorage.favourites.length > 2 ?
JSON.parse(localStorage.favourites) :
this.dfavourites;
favourites = this.validFavourites(favourites); if (this.isLocalStorageAvailable()) {
this.saveFavourites(favourites); favourites = localStorage.favourites && localStorage.favourites.length > 2 ?
JSON.parse(localStorage.favourites) :
this.dfavourites;
favourites = this.validFavourites(favourites);
this.saveFavourites(favourites);
} else {
favourites = this.dfavourites;
}
const favCat = this.categories.filter(function(c) { const favCat = this.categories.filter(function(c) {
return c.name === "Favourites"; return c.name === "Favourites";
@@ -337,6 +315,15 @@ App.prototype.validFavourites = function(favourites) {
* @param {string[]} favourites - A list of the user's favourite operations * @param {string[]} favourites - A list of the user's favourite operations
*/ */
App.prototype.saveFavourites = function(favourites) { App.prototype.saveFavourites = function(favourites) {
if (!this.isLocalStorageAvailable()) {
this.alert(
"Your security settings do not allow access to local storage so your favourites cannot be saved.",
"danger",
5000
);
return false;
}
localStorage.setItem("favourites", JSON.stringify(this.validFavourites(favourites))); localStorage.setItem("favourites", JSON.stringify(this.validFavourites(favourites)));
}; };
@@ -378,39 +365,29 @@ App.prototype.addFavourite = function(name) {
* Checks for input and recipe in the URI parameters and loads them if present. * Checks for input and recipe in the URI parameters and loads them if present.
*/ */
App.prototype.loadURIParams = function() { App.prototype.loadURIParams = function() {
// Load query string from URI // Load query string or hash from URI (depending on which is populated)
this.queryString = (function(a) { // We prefer getting the hash by splitting the href rather than referencing
if (a === "") return {}; // location.hash as some browsers (Firefox) automatically URL decode it,
const b = {}; // which cause issues.
for (let i = 0; i < a.length; i++) { const params = window.location.search ||
const p = a[i].split("="); window.location.href.split("#")[1] ||
if (p.length !== 2) { window.location.hash;
b[a[i]] = true; this.uriParams = Utils.parseURIParams(params);
} else {
b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
}
}
return b;
})(window.location.search.substr(1).split("&"));
// Turn off auto-bake while loading // Read in recipe from URI params
const autoBakeVal = this.autoBake_; if (this.uriParams.recipe) {
this.autoBake_ = false;
// Read in recipe from query string
if (this.queryString.recipe) {
try { try {
const recipeConfig = JSON.parse(this.queryString.recipe); const recipeConfig = Utils.parseRecipeConfig(this.uriParams.recipe);
this.setRecipeConfig(recipeConfig); this.setRecipeConfig(recipeConfig);
} catch (err) {} } catch (err) {}
} else if (this.queryString.op) { } else if (this.uriParams.op) {
// If there's no recipe, look for single operations // If there's no recipe, look for single operations
this.manager.recipe.clearRecipe(); this.manager.recipe.clearRecipe();
try { try {
this.manager.recipe.addOperation(this.queryString.op); this.manager.recipe.addOperation(this.uriParams.op);
} catch (err) { } catch (err) {
// If no exact match, search for nearest match and add that // If no exact match, search for nearest match and add that
const matchedOps = this.manager.ops.filterOperations(this.queryString.op, false); const matchedOps = this.manager.ops.filterOperations(this.uriParams.op, false);
if (matchedOps.length) { if (matchedOps.length) {
this.manager.recipe.addOperation(matchedOps[0].name); this.manager.recipe.addOperation(matchedOps[0].name);
} }
@@ -418,21 +395,23 @@ App.prototype.loadURIParams = function() {
// Populate search with the string // Populate search with the string
const search = document.getElementById("search"); const search = document.getElementById("search");
search.value = this.queryString.op; search.value = this.uriParams.op;
search.dispatchEvent(new Event("search")); search.dispatchEvent(new Event("search"));
} }
} }
// Read in input data from query string // Read in input data from URI params
if (this.queryString.input) { if (this.uriParams.input) {
this.autoBakePause = true;
try { try {
const inputData = Utils.fromBase64(this.queryString.input); const inputData = Utils.fromBase64(this.uriParams.input);
this.setInput(inputData); this.setInput(inputData);
} catch (err) {} } catch (err) {
} finally {
this.autoBakePause = false;
}
} }
// Restore auto-bake state
this.autoBake_ = autoBakeVal;
this.autoBake(); this.autoBake();
}; };
@@ -453,9 +432,7 @@ App.prototype.nextIngId = function() {
* @returns {Object[]} * @returns {Object[]}
*/ */
App.prototype.getRecipeConfig = function() { App.prototype.getRecipeConfig = function() {
const recipeConfig = this.manager.recipe.getConfig(); return this.manager.recipe.getConfig();
sessionStorage.setItem("recipeConfig", JSON.stringify(recipeConfig));
return recipeConfig;
}; };
@@ -465,15 +442,19 @@ App.prototype.getRecipeConfig = function() {
* @param {Object[]} recipeConfig - The recipe configuration * @param {Object[]} recipeConfig - The recipe configuration
*/ */
App.prototype.setRecipeConfig = function(recipeConfig) { App.prototype.setRecipeConfig = function(recipeConfig) {
sessionStorage.setItem("recipeConfig", JSON.stringify(recipeConfig));
document.getElementById("rec-list").innerHTML = null; 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++) { for (let i = 0; i < recipeConfig.length; i++) {
const item = this.manager.recipe.addOperation(recipeConfig[i].op); const item = this.manager.recipe.addOperation(recipeConfig[i].op);
// Populate arguments // Populate arguments
const args = item.querySelectorAll(".arg"); const args = item.querySelectorAll(".arg");
for (let j = 0; j < args.length; j++) { for (let j = 0; j < args.length; j++) {
if (recipeConfig[i].args[j] === undefined) continue;
if (args[j].getAttribute("type") === "checkbox") { if (args[j].getAttribute("type") === "checkbox") {
// checkbox // checkbox
args[j].checked = recipeConfig[i].args[j]; args[j].checked = recipeConfig[i].args[j];
@@ -499,6 +480,9 @@ App.prototype.setRecipeConfig = function(recipeConfig) {
this.progress = 0; this.progress = 0;
} }
// Unpause auto bake
this.autoBakePause = false;
}; };
@@ -520,19 +504,44 @@ App.prototype.resetLayout = function() {
App.prototype.setCompileMessage = function() { App.prototype.setCompileMessage = function() {
// Display time since last build and compile message // Display time since last build and compile message
let now = new Date(), let now = new Date(),
timeSinceCompile = Utils.fuzzyTime(now.getTime() - window.compileTime), timeSinceCompile = Utils.fuzzyTime(now.getTime() - window.compileTime);
compileInfo = "<span style=\"font-weight: normal\">Last build: " +
timeSinceCompile.substr(0, 1).toUpperCase() + timeSinceCompile.substr(1) + " ago"; // Calculate previous version to compare to
let prev = PKG_VERSION.split(".").map(n => {
return parseInt(n, 10);
});
if (prev[2] > 0) prev[2]--;
else if (prev[1] > 0) prev[1]--;
else prev[0]--;
const compareURL = `https://github.com/gchq/CyberChef/compare/v${prev.join(".")}...v${PKG_VERSION}`;
let compileInfo = `<a href='${compareURL}'>Last build: ${timeSinceCompile.substr(0, 1).toUpperCase() + timeSinceCompile.substr(1)} ago</a>`;
if (window.compileMessage !== "") { if (window.compileMessage !== "") {
compileInfo += " - " + window.compileMessage; compileInfo += " - " + window.compileMessage;
} }
compileInfo += "</span>";
document.getElementById("notice").innerHTML = compileInfo; document.getElementById("notice").innerHTML = compileInfo;
}; };
/**
* Determines whether the browser supports Local Storage and if it is accessible.
*
* @returns {boolean}
*/
App.prototype.isLocalStorageAvailable = function() {
try {
if (!localStorage) return false;
return true;
} catch (err) {
// Access to LocalStorage is denied
return false;
}
};
/** /**
* Pops up a message to the user and writes it to the console log. * Pops up a message to the user and writes it to the console log.
* *
@@ -653,10 +662,27 @@ App.prototype.alertCloseClick = function() {
App.prototype.stateChange = function(e) { App.prototype.stateChange = function(e) {
this.autoBake(); this.autoBake();
// Set title
const recipeConfig = this.getRecipeConfig();
let title = "CyberChef";
if (recipeConfig.length === 1) {
title = `${recipeConfig[0].op} - ${title}`;
} else if (recipeConfig.length > 1) {
// See how long the full recipe is
const ops = recipeConfig.map(op => op.op).join(", ");
if (ops.length < 45) {
title = `${ops} - ${title}`;
} else {
// If it's too long, just use the first one and say how many more there are
title = `${recipeConfig[0].op}, ${recipeConfig.length - 1} more - ${title}`;
}
}
document.title = title;
// Update the current history state (not creating a new one) // Update the current history state (not creating a new one)
if (this.options.updateUrl) { if (this.options.updateUrl) {
this.lastStateUrl = this.manager.controls.generateStateUrl(true, true); this.lastStateUrl = this.manager.controls.generateStateUrl(true, true, recipeConfig);
window.history.replaceState({}, "CyberChef", this.lastStateUrl); window.history.replaceState({}, title, this.lastStateUrl);
} }
}; };
@@ -668,9 +694,7 @@ App.prototype.stateChange = function(e) {
* @param {event} e * @param {event} e
*/ */
App.prototype.popState = function(e) { App.prototype.popState = function(e) {
if (window.location.href.split("#")[0] !== this.lastStateUrl) { this.loadURIParams();
this.loadURIParams();
}
}; };

View File

@@ -23,14 +23,14 @@ const ControlsWaiter = function(app, manager) {
* without wrapping or overflowing. * without wrapping or overflowing.
*/ */
ControlsWaiter.prototype.adjustWidth = function() { ControlsWaiter.prototype.adjustWidth = function() {
let controls = document.getElementById("controls"), const controls = document.getElementById("controls");
step = document.getElementById("step"), const step = document.getElementById("step");
clrBreaks = document.getElementById("clr-breaks"), const clrBreaks = document.getElementById("clr-breaks");
saveImg = document.querySelector("#save img"), const saveImg = document.querySelector("#save img");
loadImg = document.querySelector("#load img"), const loadImg = document.querySelector("#load img");
stepImg = document.querySelector("#step img"), const stepImg = document.querySelector("#step img");
clrRecipImg = document.querySelector("#clr-recipe img"), const clrRecipImg = document.querySelector("#clr-recipe img");
clrBreaksImg = document.querySelector("#clr-breaks img"); const clrBreaksImg = document.querySelector("#clr-breaks img");
if (controls.clientWidth < 470) { if (controls.clientWidth < 470) {
step.childNodes[1].nodeValue = " Step"; step.childNodes[1].nodeValue = " Step";
@@ -78,10 +78,11 @@ ControlsWaiter.prototype.setAutoBake = function(value) {
* Handler to trigger baking. * Handler to trigger baking.
*/ */
ControlsWaiter.prototype.bakeClick = function() { ControlsWaiter.prototype.bakeClick = function() {
this.app.bake(); if (document.getElementById("bake").textContent.indexOf("Bake") > 0) {
const outputText = document.getElementById("output-text"); this.app.bake();
outputText.focus(); } else {
outputText.setSelectionRange(0, 0); this.manager.worker.cancelBake();
}
}; };
@@ -90,9 +91,6 @@ ControlsWaiter.prototype.bakeClick = function() {
*/ */
ControlsWaiter.prototype.stepClick = function() { ControlsWaiter.prototype.stepClick = function() {
this.app.bake(true); this.app.bake(true);
const outputText = document.getElementById("output-text");
outputText.focus();
outputText.setSelectionRange(0, 0);
}; };
@@ -100,17 +98,17 @@ ControlsWaiter.prototype.stepClick = function() {
* Handler for changes made to the Auto Bake checkbox. * Handler for changes made to the Auto Bake checkbox.
*/ */
ControlsWaiter.prototype.autoBakeChange = function() { ControlsWaiter.prototype.autoBakeChange = function() {
let autoBakeLabel = document.getElementById("auto-bake-label"), const autoBakeLabel = document.getElementById("auto-bake-label");
autoBakeCheckbox = document.getElementById("auto-bake"); const autoBakeCheckbox = document.getElementById("auto-bake");
this.app.autoBake_ = autoBakeCheckbox.checked; this.app.autoBake_ = autoBakeCheckbox.checked;
if (autoBakeCheckbox.checked) { if (autoBakeCheckbox.checked) {
autoBakeLabel.classList.remove("btn-default");
autoBakeLabel.classList.add("btn-success"); autoBakeLabel.classList.add("btn-success");
autoBakeLabel.classList.remove("btn-default");
} else { } else {
autoBakeLabel.classList.remove("btn-success");
autoBakeLabel.classList.add("btn-default"); autoBakeLabel.classList.add("btn-default");
autoBakeLabel.classList.remove("btn-success");
} }
}; };
@@ -145,10 +143,10 @@ ControlsWaiter.prototype.clearBreaksClick = function() {
ControlsWaiter.prototype.initialiseSaveLink = function(recipeConfig) { ControlsWaiter.prototype.initialiseSaveLink = function(recipeConfig) {
recipeConfig = recipeConfig || this.app.getRecipeConfig(); recipeConfig = recipeConfig || this.app.getRecipeConfig();
let includeRecipe = document.getElementById("save-link-recipe-checkbox").checked, const includeRecipe = document.getElementById("save-link-recipe-checkbox").checked;
includeInput = document.getElementById("save-link-input-checkbox").checked, const includeInput = document.getElementById("save-link-input-checkbox").checked;
saveLinkEl = document.getElementById("save-link"), const saveLinkEl = document.getElementById("save-link");
saveLink = this.generateStateUrl(includeRecipe, includeInput, recipeConfig); const saveLink = this.generateStateUrl(includeRecipe, includeInput, recipeConfig);
saveLinkEl.innerHTML = Utils.truncate(saveLink, 120); saveLinkEl.innerHTML = Utils.truncate(saveLink, 120);
saveLinkEl.setAttribute("href", saveLink); saveLinkEl.setAttribute("href", saveLink);
@@ -167,23 +165,28 @@ ControlsWaiter.prototype.initialiseSaveLink = function(recipeConfig) {
ControlsWaiter.prototype.generateStateUrl = function(includeRecipe, includeInput, recipeConfig, baseURL) { ControlsWaiter.prototype.generateStateUrl = function(includeRecipe, includeInput, recipeConfig, baseURL) {
recipeConfig = recipeConfig || this.app.getRecipeConfig(); recipeConfig = recipeConfig || this.app.getRecipeConfig();
let link = baseURL || window.location.protocol + "//" + const link = baseURL || window.location.protocol + "//" +
window.location.host + window.location.host +
window.location.pathname, window.location.pathname;
recipeStr = JSON.stringify(recipeConfig), const recipeStr = Utils.generatePrettyRecipe(recipeConfig);
inputStr = Utils.toBase64(this.app.getInput(), "A-Za-z0-9+/"); // B64 alphabet with no padding const inputStr = Utils.toBase64(this.app.getInput(), "A-Za-z0-9+/"); // B64 alphabet with no padding
includeRecipe = includeRecipe && (recipeConfig.length > 0); includeRecipe = includeRecipe && (recipeConfig.length > 0);
includeInput = includeInput && (inputStr.length > 0) && (inputStr.length < 8000); // Only inlcude input if it is less than 50KB (51200 * 4/3 as it is Base64 encoded)
includeInput = includeInput && (inputStr.length > 0) && (inputStr.length <= 68267);
if (includeRecipe) { const params = [
link += "?recipe=" + encodeURIComponent(recipeStr); includeRecipe ? ["recipe", recipeStr] : undefined,
} includeInput ? ["input", inputStr] : undefined,
];
if (includeRecipe && includeInput) { const hash = params
link += "&input=" + encodeURIComponent(inputStr); .filter(v => v)
} else if (includeInput) { .map(([key, value]) => `${key}=${Utils.encodeURIFragment(value)}`)
link += "?input=" + encodeURIComponent(inputStr); .join("&");
if (hash) {
return `${link}#${hash}`;
} }
return link; return link;
@@ -193,9 +196,9 @@ ControlsWaiter.prototype.generateStateUrl = function(includeRecipe, includeInput
/** /**
* Handler for changes made to the save dialog text area. Re-initialises the save link. * Handler for changes made to the save dialog text area. Re-initialises the save link.
*/ */
ControlsWaiter.prototype.saveTextChange = function() { ControlsWaiter.prototype.saveTextChange = function(e) {
try { try {
const recipeConfig = JSON.parse(document.getElementById("save-text").value); const recipeConfig = Utils.parseRecipeConfig(e.target.value);
this.initialiseSaveLink(recipeConfig); this.initialiseSaveLink(recipeConfig);
} catch (err) {} } catch (err) {}
}; };
@@ -205,10 +208,17 @@ ControlsWaiter.prototype.saveTextChange = function() {
* Handler for the 'Save' command. Pops up the save dialog box. * Handler for the 'Save' command. Pops up the save dialog box.
*/ */
ControlsWaiter.prototype.saveClick = function() { ControlsWaiter.prototype.saveClick = function() {
let recipeConfig = this.app.getRecipeConfig(), const recipeConfig = this.app.getRecipeConfig();
recipeStr = JSON.stringify(recipeConfig).replace(/},{/g, "},\n{"); const recipeStr = JSON.stringify(recipeConfig);
document.getElementById("save-text").value = recipeStr; document.getElementById("save-text-chef").value = Utils.generatePrettyRecipe(recipeConfig, true);
document.getElementById("save-text-clean").value = JSON.stringify(recipeConfig, null, 2)
.replace(/{\n\s+"/g, "{ \"")
.replace(/\[\n\s{3,}/g, "[")
.replace(/\n\s{3,}]/g, "]")
.replace(/\s*\n\s*}/g, " }")
.replace(/\n\s{6,}/g, " ");
document.getElementById("save-text-compact").value = recipeStr;
this.initialiseSaveLink(recipeConfig); this.initialiseSaveLink(recipeConfig);
$("#save-modal").modal(); $("#save-modal").modal();
@@ -244,8 +254,17 @@ ControlsWaiter.prototype.loadClick = function() {
* Saves the recipe specified in the save textarea to local storage. * Saves the recipe specified in the save textarea to local storage.
*/ */
ControlsWaiter.prototype.saveButtonClick = function() { ControlsWaiter.prototype.saveButtonClick = function() {
let recipeName = Utils.escapeHtml(document.getElementById("save-name").value); if (!this.app.isLocalStorageAvailable()) {
let recipeStr = document.getElementById("save-text").value; this.app.alert(
"Your security settings do not allow access to local storage so your recipe cannot be saved.",
"danger",
5000
);
return false;
}
const recipeName = Utils.escapeHtml(document.getElementById("save-name").value);
const recipeStr = document.querySelector("#save-texts .tab-pane.active textarea").value;
if (!recipeName) { if (!recipeName) {
this.app.alert("Please enter a recipe name", "danger", 2000); this.app.alert("Please enter a recipe name", "danger", 2000);
@@ -273,6 +292,8 @@ ControlsWaiter.prototype.saveButtonClick = function() {
* Populates the list of saved recipes in the load dialog box from local storage. * Populates the list of saved recipes in the load dialog box from local storage.
*/ */
ControlsWaiter.prototype.populateLoadRecipesList = function() { ControlsWaiter.prototype.populateLoadRecipesList = function() {
if (!this.app.isLocalStorageAvailable()) return false;
const loadNameEl = document.getElementById("load-name"); const loadNameEl = document.getElementById("load-name");
// Remove current recipes from select // Remove current recipes from select
@@ -283,7 +304,7 @@ ControlsWaiter.prototype.populateLoadRecipesList = function() {
// Add recipes to select // Add recipes to select
const savedRecipes = localStorage.savedRecipes ? const savedRecipes = localStorage.savedRecipes ?
JSON.parse(localStorage.savedRecipes) : []; JSON.parse(localStorage.savedRecipes) : [];
for (i = 0; i < savedRecipes.length; i++) { for (i = 0; i < savedRecipes.length; i++) {
const opt = document.createElement("option"); const opt = document.createElement("option");
@@ -303,13 +324,13 @@ ControlsWaiter.prototype.populateLoadRecipesList = function() {
* Removes the currently selected recipe from local storage. * Removes the currently selected recipe from local storage.
*/ */
ControlsWaiter.prototype.loadDeleteClick = function() { ControlsWaiter.prototype.loadDeleteClick = function() {
let id = parseInt(document.getElementById("load-name").value, 10), if (!this.app.isLocalStorageAvailable()) return false;
savedRecipes = localStorage.savedRecipes ?
JSON.parse(localStorage.savedRecipes) : [];
savedRecipes = savedRecipes.filter(function(r) { const id = parseInt(document.getElementById("load-name").value, 10);
return r.id !== id; const rawSavedRecipes = localStorage.savedRecipes ?
}); JSON.parse(localStorage.savedRecipes) : [];
const savedRecipes = rawSavedRecipes.filter(r => r.id !== id);
localStorage.savedRecipes = JSON.stringify(savedRecipes); localStorage.savedRecipes = JSON.stringify(savedRecipes);
this.populateLoadRecipesList(); this.populateLoadRecipesList();
@@ -320,14 +341,14 @@ ControlsWaiter.prototype.loadDeleteClick = function() {
* Displays the selected recipe in the load text box. * Displays the selected recipe in the load text box.
*/ */
ControlsWaiter.prototype.loadNameChange = function(e) { ControlsWaiter.prototype.loadNameChange = function(e) {
let el = e.target, if (!this.app.isLocalStorageAvailable()) return false;
savedRecipes = localStorage.savedRecipes ?
JSON.parse(localStorage.savedRecipes) : [],
id = parseInt(el.value, 10);
const recipe = savedRecipes.filter(function(r) { const el = e.target;
return r.id === id; const savedRecipes = localStorage.savedRecipes ?
})[0]; JSON.parse(localStorage.savedRecipes) : [];
const id = parseInt(el.value, 10);
const recipe = savedRecipes.find(r => r.id === id);
document.getElementById("load-text").value = recipe.recipe; document.getElementById("load-text").value = recipe.recipe;
}; };
@@ -338,8 +359,9 @@ ControlsWaiter.prototype.loadNameChange = function(e) {
*/ */
ControlsWaiter.prototype.loadButtonClick = function() { ControlsWaiter.prototype.loadButtonClick = function() {
try { try {
const recipeConfig = JSON.parse(document.getElementById("load-text").value); const recipeConfig = Utils.parseRecipeConfig(document.getElementById("load-text").value);
this.app.setRecipeConfig(recipeConfig); this.app.setRecipeConfig(recipeConfig);
this.app.autoBake();
$("#rec-list [data-toggle=popover]").popover(); $("#rec-list [data-toggle=popover]").popover();
} catch (e) { } catch (e) {
@@ -350,14 +372,66 @@ ControlsWaiter.prototype.loadButtonClick = function() {
/** /**
* Populates the bug report information box with useful technical info. * Populates the bug report information box with useful technical info.
*
* @param {event} e
*/ */
ControlsWaiter.prototype.supportButtonClick = function() { ControlsWaiter.prototype.supportButtonClick = function(e) {
let reportBugInfo = document.getElementById("report-bug-info"), e.preventDefault();
saveLink = this.generateStateUrl(true, true, null, "https://gchq.github.io/CyberChef/");
reportBugInfo.innerHTML = "* CyberChef compile time: " + COMPILE_TIME + "\n" + const reportBugInfo = document.getElementById("report-bug-info");
"* User-Agent: \n" + navigator.userAgent + "\n" + const saveLink = this.generateStateUrl(true, true, null, "https://gchq.github.io/CyberChef/");
"* [Link to reproduce](" + saveLink + ")\n\n";
if (reportBugInfo) {
reportBugInfo.innerHTML = "* Version: " + PKG_VERSION + "\n" +
"* Compile time: " + COMPILE_TIME + "\n" +
"* User-Agent: \n" + navigator.userAgent + "\n" +
"* [Link to reproduce](" + saveLink + ")\n\n";
}
};
/**
* Shows the stale indicator to show that the input or recipe has changed
* since the last bake.
*/
ControlsWaiter.prototype.showStaleIndicator = function() {
const staleIndicator = document.getElementById("stale-indicator");
staleIndicator.style.visibility = "visible";
staleIndicator.style.opacity = 1;
};
/**
* Hides the stale indicator to show that the input or recipe has not changed
* since the last bake.
*/
ControlsWaiter.prototype.hideStaleIndicator = function() {
const staleIndicator = document.getElementById("stale-indicator");
staleIndicator.style.opacity = 0;
staleIndicator.style.visibility = "hidden";
};
/**
* Switches the Bake button between 'Bake' and 'Cancel' functions.
*
* @param {boolean} cancel - Whether to change to cancel or not
*/
ControlsWaiter.prototype.toggleBakeButtonFunction = function(cancel) {
const bakeButton = document.getElementById("bake"),
btnText = bakeButton.querySelector("span");
if (cancel) {
btnText.innerText = "Cancel";
bakeButton.classList.remove("btn-success");
bakeButton.classList.add("btn-danger");
} else {
btnText.innerText = "Bake!";
bakeButton.classList.remove("btn-danger");
bakeButton.classList.add("btn-success");
}
}; };
export default ControlsWaiter; export default ControlsWaiter;

View File

@@ -158,13 +158,12 @@ HTMLIngredient.prototype.toHtml = function() {
* @param {event} e * @param {event} e
*/ */
HTMLIngredient.prototype.toggleDisableArgs = function(e) { HTMLIngredient.prototype.toggleDisableArgs = function(e) {
let el = e.target, const el = e.target;
op = el.parentNode.parentNode, const op = el.parentNode.parentNode;
args = op.querySelectorAll(".arg-group"), const args = op.querySelectorAll(".arg-group");
els;
for (let i = 0; i < this.disableArgs.length; i++) { for (let i = 0; i < this.disableArgs.length; i++) {
els = args[this.disableArgs[i]].querySelectorAll("input, select, button"); const els = args[this.disableArgs[i]].querySelectorAll("input, select, button");
for (let j = 0; j < els.length; j++) { for (let j = 0; j < els.length; j++) {
if (els[j].getAttribute("disabled")) { if (els[j].getAttribute("disabled")) {
@@ -186,9 +185,9 @@ HTMLIngredient.prototype.toggleDisableArgs = function(e) {
* @param {event} e * @param {event} e
*/ */
HTMLIngredient.prototype.populateOptionChange = function(e) { HTMLIngredient.prototype.populateOptionChange = function(e) {
let el = e.target, const el = e.target;
op = el.parentNode.parentNode, const op = el.parentNode.parentNode;
target = op.querySelectorAll(".arg-group")[this.target].querySelector("input, select, textarea"); const target = op.querySelectorAll(".arg-group")[this.target].querySelector("input, select, textarea");
target.value = el.childNodes[el.selectedIndex].getAttribute("populate-value"); target.value = el.childNodes[el.selectedIndex].getAttribute("populate-value");

View File

@@ -10,9 +10,11 @@ import Utils from "../core/Utils.js";
* *
* @constructor * @constructor
* @param {App} app - The main view object for CyberChef. * @param {App} app - The main view object for CyberChef.
* @param {Manager} manager - The CyberChef event manager.
*/ */
const HighlighterWaiter = function(app) { const HighlighterWaiter = function(app, manager) {
this.app = app; this.app = app;
this.manager = manager;
this.mouseButtonDown = false; this.mouseButtonDown = false;
this.mouseTarget = null; this.mouseTarget = null;
@@ -64,8 +66,8 @@ HighlighterWaiter.prototype._isSelectionBackwards = function() {
* @returns {number} * @returns {number}
*/ */
HighlighterWaiter.prototype._getOutputHtmlOffset = function(node, offset) { HighlighterWaiter.prototype._getOutputHtmlOffset = function(node, offset) {
let sel = window.getSelection(), const sel = window.getSelection();
range = document.createRange(); const range = document.createRange();
range.selectNodeContents(document.getElementById("output-html")); range.selectNodeContents(document.getElementById("output-html"));
range.setEnd(node, offset); range.setEnd(node, offset);
@@ -85,8 +87,8 @@ HighlighterWaiter.prototype._getOutputHtmlOffset = function(node, offset) {
* @returns {number} pos.end * @returns {number} pos.end
*/ */
HighlighterWaiter.prototype._getOutputHtmlSelectionOffsets = function() { HighlighterWaiter.prototype._getOutputHtmlSelectionOffsets = function() {
let sel = window.getSelection(), const sel = window.getSelection();
range, let range,
start = 0, start = 0,
end = 0, end = 0,
backwards = false; backwards = false;
@@ -151,9 +153,9 @@ HighlighterWaiter.prototype.inputMousedown = function(e) {
this.mouseTarget = HighlighterWaiter.INPUT; this.mouseTarget = HighlighterWaiter.INPUT;
this.removeHighlights(); this.removeHighlights();
let el = e.target, const el = e.target;
start = el.selectionStart, const start = el.selectionStart;
end = el.selectionEnd; const end = el.selectionEnd;
if (start !== 0 || end !== 0) { if (start !== 0 || end !== 0) {
document.getElementById("input-selection-info").innerHTML = this.selectionInfo(start, end); document.getElementById("input-selection-info").innerHTML = this.selectionInfo(start, end);
@@ -173,9 +175,9 @@ HighlighterWaiter.prototype.outputMousedown = function(e) {
this.mouseTarget = HighlighterWaiter.OUTPUT; this.mouseTarget = HighlighterWaiter.OUTPUT;
this.removeHighlights(); this.removeHighlights();
let el = e.target, const el = e.target;
start = el.selectionStart, const start = el.selectionStart;
end = el.selectionEnd; const end = el.selectionEnd;
if (start !== 0 || end !== 0) { if (start !== 0 || end !== 0) {
document.getElementById("output-selection-info").innerHTML = this.selectionInfo(start, end); document.getElementById("output-selection-info").innerHTML = this.selectionInfo(start, end);
@@ -244,9 +246,9 @@ HighlighterWaiter.prototype.inputMousemove = function(e) {
this.mouseTarget !== HighlighterWaiter.INPUT) this.mouseTarget !== HighlighterWaiter.INPUT)
return; return;
let el = e.target, const el = e.target;
start = el.selectionStart, const start = el.selectionStart;
end = el.selectionEnd; const end = el.selectionEnd;
if (start !== 0 || end !== 0) { if (start !== 0 || end !== 0) {
document.getElementById("input-selection-info").innerHTML = this.selectionInfo(start, end); document.getElementById("input-selection-info").innerHTML = this.selectionInfo(start, end);
@@ -268,9 +270,9 @@ HighlighterWaiter.prototype.outputMousemove = function(e) {
this.mouseTarget !== HighlighterWaiter.OUTPUT) this.mouseTarget !== HighlighterWaiter.OUTPUT)
return; return;
let el = e.target, const el = e.target;
start = el.selectionStart, const start = el.selectionStart;
end = el.selectionEnd; const end = el.selectionEnd;
if (start !== 0 || end !== 0) { if (start !== 0 || end !== 0) {
document.getElementById("output-selection-info").innerHTML = this.selectionInfo(start, end); document.getElementById("output-selection-info").innerHTML = this.selectionInfo(start, end);
@@ -308,11 +310,11 @@ HighlighterWaiter.prototype.outputHtmlMousemove = function(e) {
* @returns {string} * @returns {string}
*/ */
HighlighterWaiter.prototype.selectionInfo = function(start, end) { HighlighterWaiter.prototype.selectionInfo = function(start, end) {
let width = end.toString().length; const len = end.toString().length;
width = width < 2 ? 2 : width; const width = len < 2 ? 2 : len;
let startStr = Utils.pad(start.toString(), width, " ").replace(/ /g, "&nbsp;"), const startStr = Utils.pad(start.toString(), width, " ").replace(/ /g, "&nbsp;");
endStr = Utils.pad(end.toString(), width, " ").replace(/ /g, "&nbsp;"), const endStr = Utils.pad(end.toString(), width, " ").replace(/ /g, "&nbsp;");
lenStr = Utils.pad((end-start).toString(), width, " ").replace(/ /g, "&nbsp;"); const lenStr = Utils.pad((end-start).toString(), width, " ").replace(/ /g, "&nbsp;");
return "start: " + startStr + "<br>end: " + endStr + "<br>length: " + lenStr; return "start: " + startStr + "<br>end: " + endStr + "<br>length: " + lenStr;
}; };
@@ -329,41 +331,6 @@ HighlighterWaiter.prototype.removeHighlights = function() {
}; };
/**
* Generates a list of all the highlight functions assigned to operations in the recipe, if the
* entire recipe supports highlighting.
*
* @returns {Object[]} highlights
* @returns {function} highlights[].f
* @returns {function} highlights[].b
* @returns {Object[]} highlights[].args
*/
HighlighterWaiter.prototype.generateHighlightList = function() {
let recipeConfig = this.app.getRecipeConfig(),
highlights = [];
for (let i = 0; i < recipeConfig.length; i++) {
if (recipeConfig[i].disabled) continue;
// If any breakpoints are set, do not attempt to highlight
if (recipeConfig[i].breakpoint) return false;
const op = this.app.operations[recipeConfig[i].op];
// If any of the operations do not support highlighting, fail immediately.
if (op.highlight === false || op.highlight === undefined) return false;
highlights.push({
f: op.highlight,
b: op.highlightReverse,
args: recipeConfig[i].args
});
}
return highlights;
};
/** /**
* Highlights the given offsets in the output. * Highlights the given offsets in the output.
* We will only highlight if: * We will only highlight if:
@@ -376,26 +343,8 @@ HighlighterWaiter.prototype.generateHighlightList = function() {
* @param {number} pos.end - The end offset. * @param {number} pos.end - The end offset.
*/ */
HighlighterWaiter.prototype.highlightOutput = function(pos) { HighlighterWaiter.prototype.highlightOutput = function(pos) {
const highlights = this.generateHighlightList(); if (!this.app.autoBake_ || this.app.baking) return false;
this.manager.worker.highlight(this.app.getRecipeConfig(), "forward", pos);
if (!highlights || !this.app.autoBake_) {
return false;
}
for (let i = 0; i < highlights.length; i++) {
// Remove multiple highlights before processing again
pos = [pos[0]];
if (typeof highlights[i].f == "function") {
pos = highlights[i].f(pos, highlights[i].args);
}
}
document.getElementById("output-selection-info").innerHTML = this.selectionInfo(pos[0].start, pos[0].end);
this.highlight(
document.getElementById("output-text"),
document.getElementById("output-highlighter"),
pos);
}; };
@@ -411,25 +360,28 @@ HighlighterWaiter.prototype.highlightOutput = function(pos) {
* @param {number} pos.end - The end offset. * @param {number} pos.end - The end offset.
*/ */
HighlighterWaiter.prototype.highlightInput = function(pos) { HighlighterWaiter.prototype.highlightInput = function(pos) {
const highlights = this.generateHighlightList(); if (!this.app.autoBake_ || this.app.baking) return false;
this.manager.worker.highlight(this.app.getRecipeConfig(), "reverse", pos);
};
if (!highlights || !this.app.autoBake_) {
return false;
}
for (let i = 0; i < highlights.length; i++) { /**
// Remove multiple highlights before processing again * Displays highlight offsets sent back from the Chef.
pos = [pos[0]]; *
* @param {Object} pos - The position object for the highlight.
* @param {number} pos.start - The start offset.
* @param {number} pos.end - The end offset.
* @param {string} direction
*/
HighlighterWaiter.prototype.displayHighlights = function(pos, direction) {
if (!pos) return;
if (typeof highlights[i].b == "function") { const io = direction === "forward" ? "output" : "input";
pos = highlights[i].b(pos, highlights[i].args);
}
}
document.getElementById("input-selection-info").innerHTML = this.selectionInfo(pos[0].start, pos[0].end); document.getElementById(io + "-selection-info").innerHTML = this.selectionInfo(pos[0].start, pos[0].end);
this.highlight( this.highlight(
document.getElementById("input-text"), document.getElementById(io + "-text"),
document.getElementById("input-highlighter"), document.getElementById(io + "-highlighter"),
pos); pos);
}; };
@@ -452,11 +404,11 @@ HighlighterWaiter.prototype.highlight = function(textarea, highlighter, pos) {
// be displayed by the HTML textarea and will mess up highlighting offsets. // 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.app.dishStr || this.app.dishStr.indexOf("\r") >= 0) return false;
let startPlaceholder = "[startHighlight]", const startPlaceholder = "[startHighlight]";
startPlaceholderRegex = /\[startHighlight\]/g, const startPlaceholderRegex = /\[startHighlight\]/g;
endPlaceholder = "[endHighlight]", const endPlaceholder = "[endHighlight]";
endPlaceholderRegex = /\[endHighlight\]/g, const endPlaceholderRegex = /\[endHighlight\]/g;
text = textarea.value; let text = textarea.value;
// Put placeholders in position // Put placeholders in position
// If there's only one value, select that // If there's only one value, select that
@@ -493,13 +445,14 @@ HighlighterWaiter.prototype.highlight = function(textarea, highlighter, pos) {
//if (colour) cssClass += "-"+colour; //if (colour) cssClass += "-"+colour;
// Remove HTML tags // Remove HTML tags
text = text.replace(/&/g, "&amp;") text = text
.replace(/</g, "&lt;") .replace(/&/g, "&amp;")
.replace(/>/g, "&gt;") .replace(/</g, "&lt;")
.replace(/\n/g, "&#10;") .replace(/>/g, "&gt;")
// Convert placeholders to tags .replace(/\n/g, "&#10;")
.replace(startPlaceholderRegex, "<span class=\""+cssClass+"\">") // Convert placeholders to tags
.replace(endPlaceholderRegex, "</span>") + "&nbsp;"; .replace(startPlaceholderRegex, "<span class=\""+cssClass+"\">")
.replace(endPlaceholderRegex, "</span>") + "&nbsp;";
// Adjust width to allow for scrollbars // Adjust width to allow for scrollbars
highlighter.style.width = textarea.clientWidth + "px"; highlighter.style.width = textarea.clientWidth + "px";

View File

@@ -92,8 +92,8 @@ InputWaiter.prototype.inputChange = function(e) {
this.app.progress = 0; this.app.progress = 0;
// Update the input metadata info // Update the input metadata info
let inputText = this.get(), const inputText = this.get();
lines = inputText.count("\n") + 1; const lines = inputText.count("\n") + 1;
this.setInputInfo(inputText.length, lines); this.setInputInfo(inputText.length, lines);
@@ -149,26 +149,22 @@ InputWaiter.prototype.inputDrop = function(e) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
let el = e.target, const el = e.target;
file = e.dataTransfer.files[0], const file = e.dataTransfer.files[0];
text = e.dataTransfer.getData("Text"), const text = e.dataTransfer.getData("Text");
reader = new FileReader(), const reader = new FileReader();
inputCharcode = "", let inputCharcode = "";
offset = 0, let offset = 0;
CHUNK_SIZE = 20480; // 20KB const CHUNK_SIZE = 20480; // 20KB
const setInput = function() { const setInput = function() {
if (inputCharcode.length > 100000 && this.app.autoBake_) { const recipeConfig = this.app.getRecipeConfig();
this.manager.controls.setAutoBake(false); if (!recipeConfig[0] || recipeConfig[0].op !== "From Hex") {
this.app.alert("Turned off Auto Bake as the input is large", "warning", 5000); recipeConfig.unshift({op: "From Hex", args: ["Space"]});
this.app.setRecipeConfig(recipeConfig);
} }
this.set(inputCharcode); this.set(inputCharcode);
const recipeConfig = this.app.getRecipeConfig();
if (!recipeConfig[0] || recipeConfig[0].op !== "From Hex") {
recipeConfig.unshift({op:"From Hex", args:["Space"]});
this.app.setRecipeConfig(recipeConfig);
}
el.classList.remove("loadingFile"); el.classList.remove("loadingFile");
}.bind(this); }.bind(this);

View File

@@ -1,3 +1,4 @@
import WorkerWaiter from "./WorkerWaiter.js";
import WindowWaiter from "./WindowWaiter.js"; import WindowWaiter from "./WindowWaiter.js";
import ControlsWaiter from "./ControlsWaiter.js"; import ControlsWaiter from "./ControlsWaiter.js";
import RecipeWaiter from "./RecipeWaiter.js"; import RecipeWaiter from "./RecipeWaiter.js";
@@ -27,6 +28,10 @@ const Manager = function(app) {
* @event Manager#appstart * @event Manager#appstart
*/ */
this.appstart = new CustomEvent("appstart", {bubbles: true}); this.appstart = new CustomEvent("appstart", {bubbles: true});
/**
* @event Manager#apploaded
*/
this.apploaded = new CustomEvent("apploaded", {bubbles: true});
/** /**
* @event Manager#operationadd * @event Manager#operationadd
*/ */
@@ -45,6 +50,7 @@ const Manager = function(app) {
this.statechange = new CustomEvent("statechange", {bubbles: true}); this.statechange = new CustomEvent("statechange", {bubbles: true});
// Define Waiter objects to handle various areas // Define Waiter objects to handle various areas
this.worker = new WorkerWaiter(this.app, this);
this.window = new WindowWaiter(this.app); this.window = new WindowWaiter(this.app);
this.controls = new ControlsWaiter(this.app, this); this.controls = new ControlsWaiter(this.app, this);
this.recipe = new RecipeWaiter(this.app, this); this.recipe = new RecipeWaiter(this.app, this);
@@ -52,7 +58,7 @@ const Manager = function(app) {
this.input = new InputWaiter(this.app, this); this.input = new InputWaiter(this.app, this);
this.output = new OutputWaiter(this.app, this); this.output = new OutputWaiter(this.app, this);
this.options = new OptionsWaiter(this.app); this.options = new OptionsWaiter(this.app);
this.highlighter = new HighlighterWaiter(this.app); this.highlighter = new HighlighterWaiter(this.app, this);
this.seasonal = new SeasonalWaiter(this.app, this); this.seasonal = new SeasonalWaiter(this.app, this);
// Object to store dynamic handlers to fire on elements that may not exist yet // Object to store dynamic handlers to fire on elements that may not exist yet
@@ -66,6 +72,7 @@ const Manager = function(app) {
* Sets up the various components and listeners. * Sets up the various components and listeners.
*/ */
Manager.prototype.setup = function() { Manager.prototype.setup = function() {
this.worker.registerChefWorker();
this.recipe.initialiseOperationDragNDrop(); this.recipe.initialiseOperationDragNDrop();
this.controls.autoBakeChange(); this.controls.autoBakeChange();
this.seasonal.load(); this.seasonal.load();
@@ -98,7 +105,7 @@ Manager.prototype.initialiseEventListeners = function() {
document.getElementById("load-name").addEventListener("change", this.controls.loadNameChange.bind(this.controls)); document.getElementById("load-name").addEventListener("change", this.controls.loadNameChange.bind(this.controls));
document.getElementById("load-button").addEventListener("click", this.controls.loadButtonClick.bind(this.controls)); document.getElementById("load-button").addEventListener("click", this.controls.loadButtonClick.bind(this.controls));
document.getElementById("support").addEventListener("click", this.controls.supportButtonClick.bind(this.controls)); document.getElementById("support").addEventListener("click", this.controls.supportButtonClick.bind(this.controls));
this.addMultiEventListener("#save-text", "keyup paste", this.controls.saveTextChange, this.controls); this.addMultiEventListeners("#save-texts textarea", "keyup paste", this.controls.saveTextChange, this.controls);
// Operations // Operations
this.addMultiEventListener("#search", "keyup paste search", this.ops.searchOperations, this.ops); this.addMultiEventListener("#search", "keyup paste search", this.ops.searchOperations, this.ops);
@@ -112,8 +119,8 @@ Manager.prototype.initialiseEventListeners = function() {
this.addDynamicListener("li.operation", "operationadd", this.recipe.opAdd.bind(this.recipe)); this.addDynamicListener("li.operation", "operationadd", this.recipe.opAdd.bind(this.recipe));
// Recipe // Recipe
this.addDynamicListener(".arg", "keyup", this.recipe.ingChange, this.recipe); this.addDynamicListener(".arg:not(select)", "input", this.recipe.ingChange, this.recipe);
this.addDynamicListener(".arg", "change", 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(".disable-icon", "click", this.recipe.disableClick, this.recipe);
this.addDynamicListener(".breakpoint", "click", this.recipe.breakpointClick, this.recipe); this.addDynamicListener(".breakpoint", "click", this.recipe.breakpointClick, this.recipe);
this.addDynamicListener("#rec-list li.operation", "dblclick", this.recipe.operationDblclick, this.recipe); this.addDynamicListener("#rec-list li.operation", "dblclick", this.recipe.operationDblclick, this.recipe);
@@ -145,6 +152,7 @@ Manager.prototype.initialiseEventListeners = function() {
document.getElementById("output-html").addEventListener("mousemove", this.highlighter.outputHtmlMousemove.bind(this.highlighter)); document.getElementById("output-html").addEventListener("mousemove", this.highlighter.outputHtmlMousemove.bind(this.highlighter));
this.addMultiEventListener("#output-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter); 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.addMultiEventListener("#output-html", "mousedown dblclick select", this.highlighter.outputHtmlMousedown, this.highlighter);
this.addDynamicListener(".file-switch", "click", this.output.fileSwitch, this.output);
// Options // Options
document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options)); document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options));
@@ -154,6 +162,7 @@ Manager.prototype.initialiseEventListeners = function() {
this.addDynamicListener(".option-item input[type=number]", "keyup", this.options.numberChange, this.options); 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 input[type=number]", "change", this.options.numberChange, this.options);
this.addDynamicListener(".option-item select", "change", this.options.selectChange, this.options); this.addDynamicListener(".option-item select", "change", this.options.selectChange, this.options);
document.getElementById("theme").addEventListener("change", this.options.themeChange.bind(this.options));
// Misc // Misc
document.getElementById("alert-close").addEventListener("click", this.app.alertCloseClick.bind(this.app)); document.getElementById("alert-close").addEventListener("click", this.app.alertCloseClick.bind(this.app));
@@ -261,15 +270,16 @@ Manager.prototype.addDynamicListener = function(selector, eventType, callback, s
* @param {Event} e - The event to be handled * @param {Event} e - The event to be handled
*/ */
Manager.prototype.dynamicListenerHandler = function(e) { Manager.prototype.dynamicListenerHandler = function(e) {
let handlers = this.dynamicHandlers[e.type], const { type, target } = e;
matches = e.target.matches || const handlers = this.dynamicHandlers[type];
e.target.webkitMatchesSelector || const matches = target.matches ||
e.target.mozMatchesSelector || target.webkitMatchesSelector ||
e.target.msMatchesSelector || target.mozMatchesSelector ||
e.target.oMatchesSelector; target.msMatchesSelector ||
target.oMatchesSelector;
for (let i = 0; i < handlers.length; i++) { for (let i = 0; i < handlers.length; i++) {
if (matches && e.target[matches.name](handlers[i].selector)) { if (matches && matches.call(target, handlers[i].selector)) {
handlers[i].callback(e); handlers[i].callback(e);
} }
} }

View File

@@ -38,7 +38,6 @@ OperationsWaiter.prototype.searchOperations = function(e) {
selected = this.getSelectedOp(ops); selected = this.getSelectedOp(ops);
if (selected > -1) { if (selected > -1) {
this.manager.recipe.addOperation(ops[selected].innerHTML); this.manager.recipe.addOperation(ops[selected].innerHTML);
this.app.autoBake();
} }
} }
} }
@@ -68,9 +67,9 @@ OperationsWaiter.prototype.searchOperations = function(e) {
ops[selected-1].classList.add("selected-op"); ops[selected-1].classList.add("selected-op");
} }
} else { } else {
let searchResultsEl = document.getElementById("search-results"), const searchResultsEl = document.getElementById("search-results");
el = e.target, const el = e.target;
str = el.value; const str = el.value;
while (searchResultsEl.firstChild) { while (searchResultsEl.firstChild) {
try { try {
@@ -81,12 +80,10 @@ OperationsWaiter.prototype.searchOperations = function(e) {
$("#categories .in").collapse("hide"); $("#categories .in").collapse("hide");
if (str) { if (str) {
let matchedOps = this.filterOperations(str, true), const matchedOps = this.filterOperations(str, true);
matchedOpsHtml = ""; const matchedOpsHtml = matchedOps
.map(v => v.toStubHtml())
for (let i = 0; i < matchedOps.length; i++) { .join("");
matchedOpsHtml += matchedOps[i].toStubHtml();
}
searchResultsEl.innerHTML = matchedOpsHtml; searchResultsEl.innerHTML = matchedOpsHtml;
searchResultsEl.dispatchEvent(this.manager.oplistcreate); searchResultsEl.dispatchEvent(this.manager.oplistcreate);
@@ -103,16 +100,16 @@ OperationsWaiter.prototype.searchOperations = function(e) {
* name and description * name and description
* @returns {string[]} * @returns {string[]}
*/ */
OperationsWaiter.prototype.filterOperations = function(searchStr, highlight) { OperationsWaiter.prototype.filterOperations = function(inStr, highlight) {
let matchedOps = [], const matchedOps = [];
matchedDescs = []; const matchedDescs = [];
searchStr = searchStr.toLowerCase(); const searchStr = inStr.toLowerCase();
for (const opName in this.app.operations) { for (const opName in this.app.operations) {
let op = this.app.operations[opName], const op = this.app.operations[opName];
namePos = opName.toLowerCase().indexOf(searchStr), const namePos = opName.toLowerCase().indexOf(searchStr);
descPos = op.description.toLowerCase().indexOf(searchStr); const descPos = op.description.toLowerCase().indexOf(searchStr);
if (namePos >= 0 || descPos >= 0) { if (namePos >= 0 || descPos >= 0) {
const operation = new HTMLOperation(opName, this.app.operations[opName], this.app, this.manager); const operation = new HTMLOperation(opName, this.app.operations[opName], this.app, this.manager);
@@ -157,7 +154,35 @@ OperationsWaiter.prototype.getSelectedOp = function(ops) {
*/ */
OperationsWaiter.prototype.opListCreate = function(e) { OperationsWaiter.prototype.opListCreate = function(e) {
this.manager.recipe.createSortableSeedList(e.target); this.manager.recipe.createSortableSeedList(e.target);
$("[data-toggle=popover]").popover(); this.enableOpsListPopovers(e.target);
};
/**
* Sets up popovers, allowing the popover itself to gain focus which enables scrolling
* and other interactions.
*
* @param {Element} el - The element to start selecting from
*/
OperationsWaiter.prototype.enableOpsListPopovers = function(el) {
$(el).find("[data-toggle=popover]").addBack("[data-toggle=popover]")
.popover({trigger: "manual"})
.on("mouseenter", function() {
const _this = this;
$(this).popover("show");
$(".popover").on("mouseleave", function () {
$(_this).popover("hide");
});
}).on("mouseleave", function () {
const _this = this;
setTimeout(function() {
// Determine if the popover associated with this element is being hovered over
if ($(_this).data("bs.popover") &&
!$(_this).data("bs.popover").$tip.is(":hover")) {
$(_this).popover("hide");
}
}, 50);
});
}; };
@@ -171,7 +196,6 @@ OperationsWaiter.prototype.operationDblclick = function(e) {
const li = e.target; const li = e.target;
this.manager.recipe.addOperation(li.textContent); this.manager.recipe.addOperation(li.textContent);
this.app.autoBake();
}; };
@@ -205,7 +229,7 @@ OperationsWaiter.prototype.editFavouritesClick = function(e) {
filter: ".remove-icon", filter: ".remove-icon",
onFilter: function (evt) { onFilter: function (evt) {
const el = editableList.closest(evt.item); const el = editableList.closest(evt.item);
if (el) { if (el && el.parentNode) {
$(el).popover("destroy"); $(el).popover("destroy");
el.parentNode.removeChild(el); el.parentNode.removeChild(el);
} }
@@ -236,12 +260,8 @@ OperationsWaiter.prototype.editFavouritesClick = function(e) {
* Saves the selected favourites and reloads them. * Saves the selected favourites and reloads them.
*/ */
OperationsWaiter.prototype.saveFavouritesClick = function() { OperationsWaiter.prototype.saveFavouritesClick = function() {
let favouritesList = [], const favs = document.querySelectorAll("#edit-favourites-list li");
favs = document.querySelectorAll("#edit-favourites-list li"); const favouritesList = Array.from(favs, e => e.textContent);
for (let i = 0; i < favs.length; i++) {
favouritesList.push(favs[i].textContent);
}
this.app.saveFavourites(favouritesList); this.app.saveFavourites(favouritesList);
this.app.loadFavourites(); this.app.loadFavourites();
@@ -281,8 +301,8 @@ OperationsWaiter.prototype.opIconMouseover = function(e) {
* @param {event} e * @param {event} e
*/ */
OperationsWaiter.prototype.opIconMouseleave = function(e) { OperationsWaiter.prototype.opIconMouseleave = function(e) {
let opEl = e.target.parentNode, const opEl = e.target.parentNode;
toEl = e.toElement || e.relatedElement; const toEl = e.toElement || e.relatedElement;
if (e.target.getAttribute("data-toggle") === "popover" && toEl === opEl) { if (e.target.getAttribute("data-toggle") === "popover" && toEl === opEl) {
$(opEl).popover("show"); $(opEl).popover("show");

View File

@@ -43,8 +43,13 @@ OptionsWaiter.prototype.load = function(options) {
const selects = document.querySelectorAll("#options-body select"); const selects = document.querySelectorAll("#options-body select");
for (i = 0; i < selects.length; i++) { for (i = 0; i < selects.length; i++) {
selects[i].value = this.app.options[selects[i].getAttribute("option")]; const val = this.app.options[selects[i].getAttribute("option")];
selects[i].dispatchEvent(new CustomEvent("change", {bubbles: true})); if (val) {
selects[i].value = val;
selects[i].dispatchEvent(new CustomEvent("change", {bubbles: true}));
} else {
selects[i].selectedIndex = 0;
}
} }
}; };
@@ -52,8 +57,11 @@ OptionsWaiter.prototype.load = function(options) {
/** /**
* Handler for options click events. * Handler for options click events.
* Dispays the options pane. * Dispays the options pane.
*
* @param {event} e
*/ */
OptionsWaiter.prototype.optionsClick = function() { OptionsWaiter.prototype.optionsClick = function(e) {
e.preventDefault();
$("#options-modal").modal(); $("#options-modal").modal();
}; };
@@ -75,11 +83,13 @@ OptionsWaiter.prototype.resetOptionsClick = function() {
* @param {boolean} state * @param {boolean} state
*/ */
OptionsWaiter.prototype.switchChange = function(e, state) { OptionsWaiter.prototype.switchChange = function(e, state) {
let el = e.target, const el = e.target;
option = el.getAttribute("option"); const option = el.getAttribute("option");
this.app.options[option] = state; this.app.options[option] = state;
localStorage.setItem("options", JSON.stringify(this.app.options));
if (this.app.isLocalStorageAvailable())
localStorage.setItem("options", JSON.stringify(this.app.options));
}; };
@@ -90,11 +100,13 @@ OptionsWaiter.prototype.switchChange = function(e, state) {
* @param {event} e * @param {event} e
*/ */
OptionsWaiter.prototype.numberChange = function(e) { OptionsWaiter.prototype.numberChange = function(e) {
let el = e.target, const el = e.target;
option = el.getAttribute("option"); const option = el.getAttribute("option");
this.app.options[option] = parseInt(el.value, 10); this.app.options[option] = parseInt(el.value, 10);
localStorage.setItem("options", JSON.stringify(this.app.options));
if (this.app.isLocalStorageAvailable())
localStorage.setItem("options", JSON.stringify(this.app.options));
}; };
@@ -105,11 +117,13 @@ OptionsWaiter.prototype.numberChange = function(e) {
* @param {event} e * @param {event} e
*/ */
OptionsWaiter.prototype.selectChange = function(e) { OptionsWaiter.prototype.selectChange = function(e) {
let el = e.target, const el = e.target;
option = el.getAttribute("option"); const option = el.getAttribute("option");
this.app.options[option] = el.value; this.app.options[option] = el.value;
localStorage.setItem("options", JSON.stringify(this.app.options));
if (this.app.isLocalStorageAvailable())
localStorage.setItem("options", JSON.stringify(this.app.options));
}; };
@@ -132,4 +146,14 @@ OptionsWaiter.prototype.setWordWrap = function() {
} }
}; };
/**
* Changes the theme by setting the class of the <html> element.
*/
OptionsWaiter.prototype.themeChange = function (e) {
const themeClass = e.target.value;
document.querySelector(":root").className = themeClass;
};
export default OptionsWaiter; export default OptionsWaiter;

View File

@@ -36,10 +36,10 @@ OutputWaiter.prototype.get = function() {
* @param {number} duration - The length of time (ms) it took to generate the output * @param {number} duration - The length of time (ms) it took to generate the output
*/ */
OutputWaiter.prototype.set = function(dataStr, type, duration) { OutputWaiter.prototype.set = function(dataStr, type, duration) {
let outputText = document.getElementById("output-text"), const outputText = document.getElementById("output-text");
outputHtml = document.getElementById("output-html"), const outputHtml = document.getElementById("output-html");
outputHighlighter = document.getElementById("output-highlighter"), const outputHighlighter = document.getElementById("output-highlighter");
inputHighlighter = document.getElementById("input-highlighter"); const inputHighlighter = document.getElementById("input-highlighter");
if (type === "html") { if (type === "html") {
outputText.style.display = "none"; outputText.style.display = "none";
@@ -103,11 +103,11 @@ OutputWaiter.prototype.setOutputInfo = function(length, lines, duration) {
* without wrapping or overflowing. * without wrapping or overflowing.
*/ */
OutputWaiter.prototype.adjustWidth = function() { OutputWaiter.prototype.adjustWidth = function() {
let output = document.getElementById("output"), const output = document.getElementById("output");
saveToFile = document.getElementById("save-to-file"), const saveToFile = document.getElementById("save-to-file");
switchIO = document.getElementById("switch"), const switchIO = document.getElementById("switch");
undoSwitch = document.getElementById("undo-switch"), const undoSwitch = document.getElementById("undo-switch");
maximiseOutput = document.getElementById("maximise-output"); const maximiseOutput = document.getElementById("maximise-output");
if (output.clientWidth < 680) { if (output.clientWidth < 680) {
saveToFile.childNodes[1].nodeValue = ""; saveToFile.childNodes[1].nodeValue = "";
@@ -129,8 +129,8 @@ OutputWaiter.prototype.adjustWidth = function() {
* Saves the current output to a file, downloaded as a URL octet stream. * Saves the current output to a file, downloaded as a URL octet stream.
*/ */
OutputWaiter.prototype.saveClick = function() { OutputWaiter.prototype.saveClick = function() {
let data = Utils.toBase64(this.app.dishStr), const data = Utils.toBase64(this.app.dishStr);
filename = window.prompt("Please enter a filename:", "download.dat"); const filename = window.prompt("Please enter a filename:", "download.dat");
if (filename) { if (filename) {
const el = document.createElement("a"); const el = document.createElement("a");
@@ -167,6 +167,17 @@ OutputWaiter.prototype.undoSwitchClick = function() {
document.getElementById("undo-switch").disabled = true; document.getElementById("undo-switch").disabled = true;
}; };
/**
* Handler for file switch click events.
* Moves a files data for items created via Utils.displayFilesAsHTML to the input.
*/
OutputWaiter.prototype.fileSwitch = function(e) {
e.preventDefault();
this.switchOrigData = this.manager.input.get();
this.app.setInput(e.target.getAttribute("fileValue"));
document.getElementById("undo-switch").disabled = false;
};
/** /**
* Handler for maximise output click events. * Handler for maximise output click events.
@@ -190,4 +201,44 @@ OutputWaiter.prototype.maximiseOutputClick = function(e) {
} }
}; };
/**
* Shows or hides the loading icon.
*
* @param {boolean} value
*/
OutputWaiter.prototype.toggleLoader = function(value) {
const outputLoader = document.getElementById("output-loader"),
outputElement = document.getElementById("output-text");
if (value) {
this.manager.controls.hideStaleIndicator();
this.bakingStatusTimeout = setTimeout(function() {
outputElement.disabled = true;
outputLoader.style.visibility = "visible";
outputLoader.style.opacity = 1;
this.manager.controls.toggleBakeButtonFunction(true);
}.bind(this), 200);
} else {
clearTimeout(this.bakingStatusTimeout);
outputElement.disabled = false;
outputLoader.style.opacity = 0;
outputLoader.style.visibility = "hidden";
this.manager.controls.toggleBakeButtonFunction(false);
this.setStatusMsg("");
}
};
/**
* Sets the baking status message value.
*
* @param {string} msg
*/
OutputWaiter.prototype.setStatusMsg = function(msg) {
const el = document.querySelector("#output-loader .loading-msg");
el.textContent = msg;
};
export default OutputWaiter; export default OutputWaiter;

View File

@@ -1,5 +1,6 @@
import HTMLOperation from "./HTMLOperation.js"; import HTMLOperation from "./HTMLOperation.js";
import Sortable from "sortablejs"; import Sortable from "sortablejs";
import Utils from "../core/Utils.js";
/** /**
@@ -60,8 +61,8 @@ RecipeWaiter.prototype.initialiseOperationDragNDrop = function() {
}.bind(this)); }.bind(this));
Sortable.utils.on(recList, "touchend", function(e) { Sortable.utils.on(recList, "touchend", function(e) {
let loc = e.changedTouches[0], const loc = e.changedTouches[0];
target = document.elementFromPoint(loc.clientX, loc.clientY); const target = document.elementFromPoint(loc.clientX, loc.clientY);
this.removeIntent = !recList.contains(target); this.removeIntent = !recList.contains(target);
}.bind(this)); }.bind(this));
@@ -93,7 +94,7 @@ RecipeWaiter.prototype.createSortableSeedList = function(listEl) {
// Removes popover element and event bindings from the dragged operation but not the // Removes popover element and event bindings from the dragged operation but not the
// event bindings from the one left in the operations list. Without manually removing // event bindings from the one left in the operations list. Without manually removing
// these bindings, we cannot re-initialise the popover on the stub operation. // these bindings, we cannot re-initialise the popover on the stub operation.
$(evt.item).popover("destroy"); $(evt.item).popover("destroy").removeData("bs.popover").off("mouseenter").off("mouseleave");
$(evt.clone).off(".popover").removeData("bs.popover"); $(evt.clone).off(".popover").removeData("bs.popover");
evt.item.setAttribute("data-toggle", "popover-disabled"); evt.item.setAttribute("data-toggle", "popover-disabled");
}, },
@@ -120,8 +121,7 @@ RecipeWaiter.prototype.opSortEnd = function(evt) {
// Reinitialise the popover on the original element in the ops list because for some reason it // Reinitialise the popover on the original element in the ops list because for some reason it
// gets destroyed and recreated. // gets destroyed and recreated.
$(evt.clone).popover(); this.manager.ops.enableOpsListPopovers(evt.clone);
$(evt.clone).children("[data-toggle=popover]").popover();
if (evt.item.parentNode.id !== "rec-list") { if (evt.item.parentNode.id !== "rec-list") {
return; return;
@@ -192,7 +192,7 @@ RecipeWaiter.prototype.favDrop = function(e) {
* *
* @fires Manager#statechange * @fires Manager#statechange
*/ */
RecipeWaiter.prototype.ingChange = function() { RecipeWaiter.prototype.ingChange = function(e) {
window.dispatchEvent(this.manager.statechange); window.dispatchEvent(this.manager.statechange);
}; };
@@ -276,8 +276,9 @@ RecipeWaiter.prototype.operationChildDblclick = function(e) {
* @returns {recipeConfig} * @returns {recipeConfig}
*/ */
RecipeWaiter.prototype.getConfig = function() { RecipeWaiter.prototype.getConfig = function() {
let config = [], ingredients, ingList, disabled, bp, item, const config = [];
operations = document.querySelectorAll("#rec-list li.operation"); let ingredients, ingList, disabled, bp, item;
const operations = document.querySelectorAll("#rec-list li.operation");
for (let i = 0; i < operations.length; i++) { for (let i = 0; i < operations.length; i++) {
ingredients = []; ingredients = [];
@@ -295,6 +296,9 @@ RecipeWaiter.prototype.getConfig = function() {
option: ingList[j].previousSibling.children[0].textContent.slice(0, -1), option: ingList[j].previousSibling.children[0].textContent.slice(0, -1),
string: ingList[j].value string: ingList[j].value
}; };
} else if (ingList[j].getAttribute("type") === "number") {
// number
ingredients[j] = parseFloat(ingList[j].value, 10);
} else { } else {
// all others // all others
ingredients[j] = ingList[j].value; ingredients[j] = ingList[j].value;
@@ -353,8 +357,7 @@ RecipeWaiter.prototype.buildRecipeOperation = function(el) {
el.classList.add("flow-control-op"); el.classList.add("flow-control-op");
} }
// Disable auto-bake if this is a manual op - this should be moved to the 'operationadd' // Disable auto-bake if this is a manual op
// handler after event restructuring
if (op.manualBake && this.app.autoBake_) { if (op.manualBake && this.app.autoBake_) {
this.manager.controls.setAutoBake(false); this.manager.controls.setAutoBake(false);
this.app.alert("Auto-Bake is disabled by default when using this operation.", "info", 5000); this.app.alert("Auto-Bake is disabled by default when using this operation.", "info", 5000);
@@ -402,8 +405,8 @@ RecipeWaiter.prototype.clearRecipe = function() {
* @param {event} e * @param {event} e
*/ */
RecipeWaiter.prototype.dropdownToggleClick = function(e) { RecipeWaiter.prototype.dropdownToggleClick = function(e) {
let el = e.target, const el = e.target;
button = el.parentNode.parentNode.previousSibling; const button = el.parentNode.parentNode.previousSibling;
button.innerHTML = el.textContent + " <span class='caret'></span>"; button.innerHTML = el.textContent + " <span class='caret'></span>";
this.ingChange(); this.ingChange();
@@ -433,4 +436,30 @@ RecipeWaiter.prototype.opRemove = function(e) {
window.dispatchEvent(this.manager.statechange); 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; export default RecipeWaiter;

View File

@@ -19,69 +19,12 @@ const SeasonalWaiter = function(app, manager) {
* Loads all relevant items depending on the current date. * Loads all relevant items depending on the current date.
*/ */
SeasonalWaiter.prototype.load = function() { SeasonalWaiter.prototype.load = function() {
//var now = new Date();
// SpiderChef
// if (now.getMonth() === 3 && now.getDate() === 1) { // Apr 1
// this.insertSpiderIcons();
// this.insertSpiderText();
// }
// Konami code // Konami code
this.kkeys = []; this.kkeys = [];
window.addEventListener("keydown", this.konamiCodeListener.bind(this)); window.addEventListener("keydown", this.konamiCodeListener.bind(this));
}; };
/**
* Replaces chef icons with spider icons.
* #spiderchef
*/
SeasonalWaiter.prototype.insertSpiderIcons = function() {
let spider16 = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB3UlEQVQ4y2NgGJaAmYGBgVnf0oKJgYGBobWtXamqqoYTn2I4CI+LTzM2NTulpKbu+vPHz2dV5RWlluZmi3j5+KqFJSSEzpw8uQPdAEYYIzo5Kfjrl28rWFlZzjAzMYuEBQao3Lh+g+HGvbsMzExMDN++fWf4/PXLBzY2tqYNK1f2+4eHM2xcuRLigsT09Igf3384MTExbf767etBI319jU8fPsi+//jx/72HDxh5uLkZ7ty7y/Dz1687Avz8n2UUFR3Z2NjOySoqfmdhYGBg+PbtuwI7O8e5H79+8X379t357PnzYo+ePP7y6cuXc9++f69nYGRsvf/w4XdtLS2R799/bBUWFHr57sP7Jbs3b/ZkzswvUP3165fZ7z9//r988WIVAyPDr8tXr576+u3bpb9//7YwMjKeV1dV41NWVGoVEhDgPH761DJREeHaz1+/lqlpafUx6+jrRfz4+fPy+w8fTu/fsf3uw7t3L39+//4cv7DwGQYGhpdPbt9m4BcRFlNWVJC4fuvWASszs4C379792Ldt2xZBUdEdDP5hYSqQGIjDGa965uYKCalpZQwMDAxhMTG9DAwMDLaurhIkJY7A8IgGBgYGBgd3Dz2yUpeFo6O4rasrA9T24ZRxAAMTwMpgEJwLAAAAAElFTkSuQmCC",
spider32 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAACYVBMVEUAAAAcJSU2Pz85QkM9RUWEhIWMjI2MkJEcJSU2Pz85QkM9RUWWlpc9RUVXXl4cJSU2Pz85QkM8REU9RUVRWFh6ens9RUVCSkpNVFRdY2McJSU5QkM7REQ9RUVGTk5KUlJQVldcY2Rla2uTk5WampscJSVUWltZX2BrcHF1e3scJSUjLCw9RUVASEhFTU1HTk9bYWJeZGRma2xudHV1eHiZmZocJSUyOjpJUFFQVldSWlpTWVpXXl5YXl5rb3B9fX6RkZIcJSUmLy8tNTU9RUVFTU1IT1BOVldRV1hTWlp0enocJSUfKChJUFBWXV1hZ2hnbGwcJSVETExLUlJLU1NNVVVPVlZYXl9cY2RiaGlobW5rcXFyd3h0eHgcJSUpMTFDS0tQV1dRV1hSWFlWXF1bYWJma2tobW5uc3SsrK0cJSVJUFBMVFROVlZVW1xZX2BdYmNhZ2hjaGhla2tqcHBscHE4Pz9KUlJRWVlSWVlXXF1aYGFbYWFfZWZlampqbW4cJSUgKSkiKysuNjY0PD01PT07QkNES0tHTk5JUFBMUlNMU1NOU1ROVVVPVVZRVlZRV1dSWVlWXFxXXV5aX2BbYWFbYWJcYmJcYmNcY2RdYmNgZmZhZmdkaWpkampkamtlamtla2tma2tma2xnbG1obW5pbG1pb3Bqb3Brb3BtcXJudHVvcHFvcXJvc3NwcXNwdXVxc3RzeXl1eXp2eXl3ent6e3x+gYKAhISBg4SKi4yLi4yWlpeampudnZ6fn6CkpaanqKiur6+vr7C4uLm6urq6u7u8vLy9vb3Av8DR0dL2b74UAAAAgHRSTlMAEBAQEBAQECAgICAgMDBAQEBAQEBAUFBQUGBgYGBgYGBgYGBgcHBwcHCAgICAgICAgICAgICPj4+Pj4+Pj4+Pj5+fn5+fn5+fn5+vr6+vr6+/v7+/v7+/v7+/v7+/z8/Pz8/Pz8/Pz8/P39/f39/f39/f39/f7+/v7+/v7+/v78x6RlYAAAGBSURBVDjLY2AYWUCSgUGAk4GBTdlUhQebvP7yjIgCPQbWzBMnjx5wwJSX37Rwfm1isqj9/iPHTuxYlyeMJi+yunfptBkZOw/uWj9h3vatcycu8eRGlldb3Vsts3ph/cFTh7fN3bCoe2Vf8+TZoQhTvBa6REozVC7cuPvQnmULJm1e2z+308eyJieEBSLPXbKQIUqQIczk+N6eNaumtnZMaWhaHM89m8XVCqJA02Y5w0xmga6yfVsamtrN4xoXNzS0JTHkK3CXy4EVFMumcxUy2LbENTVkZfEzMDAudtJyTmNwS2XQreAFyvOlK9louDNVaXurmjkGgnTMkWDgXswtNouFISEX6Awv+RihQi5OcYY4DtVARpCCFCMGhiJ1hjwFBpagEAaWEpFoC0WQOCOjFMRRwXYMDB4BDLJ+QLYsg7GBGjtasLnEMjCIrWBgyAZ7058FI9x1SoFEnTCDsCyIhynPILYYSFgbYpUDA5bpQBluXzxpI1yYAbd2sCMYRhwAAHB9ZPztbuMUAAAAAElFTkSuQmCC",
spider64 = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAJZUlEQVR42u1ZaXMU1xXlJ+gHpFITOy5sAcnIYCi2aIL2bTSSZrSP1NpHK41kISQBHgFaQIJBCMwi4TFUGYcPzggwEMcxHVGxQaag5QR/np/QP+Hmnsdr0hpmtEACwulb9aq7p7d3zz333Pt61q2zzTbbbLPNNttss80222yzzTbbVmu7MzKcJRWVkXjntqam6jyURPeGQqeTpqbOqp+evxC5dGlam5m5rE3PzGi8Hzx/4aLzbXDe09HdYxwZHaPc4mLFXVoW9pRXGNv3pDngeHlNLfE2Ljjj4xPOUGjSYKfpq6/+TLdv36bbX39Nt27epGvXvqSLl6bp3LlPtdOnz7jWrPNZ7kLCKCovp5bOTmP/4EHq6vmYMtzuSKbbbQCAHE8Rxd47MjrmuHjxkjF3/z4tLCzQkyc6PX78mB49ekQPHjygub/P0d27f6FrX/6JpqbO0YkT48E1R/sCr9cYHZ+gqrp64mPq+riXcoqKKC0vP9q6VyV/fQOiH+LrsPVY7z82PBKZnb1Bd+7cpfn5eQbgCT1hAADC/MN5uj83R99881eanZ2lL5gN/nrxjihAXwvOJ7l9vuiBQ4dF9LEtLC0V+2rv/ijTX6luaCS3rxT57wADAMTBQ4c9PIIDg4PBwYOHaHhklM5MnSWkwLff/o0+v3qVHv34Iz344QEDc4d8VVXUEAhQXXMzVdQqzKweKq6oABARzOGNOZ+Wl6fD6T25ubQrPT0E5xF93o82tbdjkkZ+iZfAAgbD6fZ6o339A8S0p7HjJ2h4eIQOHf6EujlV9nX3UOj0JDXzfXje+KlTdOPGDeF0T1+fGHg+2JSen08tHZ0CiPySEoPn8vq1IaOgIAzneQK0UzjcQd6qaqrlCVfV1+tpubnRnv5+2p2ZqYMF/oZGPTh0xLhy5Sr9wLn9j++/p5nLn9FxBoLZQJ1dKrkys6iYNeTExEnx3PqWFuF4W9deKq2upkEGCyzyMBC709MFC7r391Fjayv9MSdHZyCU1xJ5FjrNdN6VnU1KS4CjU4Yoh/m8CsezCguFJgAMV05ueP+BfhF5OL+gL9A/f/qJ7t3TaPLMFB09eoy6mTkMGg2PjTELOsS20OcTACgMKqJugqA0NtE7ycn0202b6A+ZmYIVAAKApGZlgRHB/0lqQPAqFEVE9hntM0R0ZblTzeswWdCeU8HAtYW+Uu0AUx+0f/jwoXD+56c/073v7tHU2XMiFbrUfVTNAtfL10FIAQL2QftsBrOEnavld5kg7E7PoF+99x79ev162rJrV9RMi6a2dvKUlQsR5uAgII7/ivMsbEE4g2hggjzC7LQL1OftovoO0WJKUn0gYEAn2hmMXo4QHIXQIfLfsfOXPwuLvB86cpQqamooyEzg1BLMwv04RkoE+B3B4BBBMHEcCwIP0N+ByJdUVhpgBJ7j4WvdANDjeTUglOaWEChfJF7uJzPX2HEPaj1vg7EAbHO5QnAeIPgqKvUB7gtAdbBgcvKMqOnc/NAIVwCcq21qElFnCgvaI9cBBFKhlSPbPzBIbbzduGULpWzfLkDAdZs++sgEwSlZqoIJMg2CzFSNGzODwdBfOi26+w4YTCm9LhDQwQDzdzguFf4FALjciTws8/u1yyx2N2/dovPnL9DRY8PkZ204xtuhoSM0wI7V8DEiirQCCHD+99u2CUdx3Lmvmz7kfemoGDgPEDr4HNKAf1MlAC4wgMGLWFJXQUrklZSEX6rLE2rOyDIQGlhgBUAyYFEZkm2vAGVi4qQ+x83M0389pevXr6OToy07d4qcR+krr/KzqpeJ/IfjGO+npDx3FCKHVPjd1q2LAMBI3ryZ9vL7U56BEzLfD80ACFba876OlGCQV9dAcT0Pyw7PgWij6zPP5Xt9EYgg+n3LosdVzdfz5CI8KY1LH31+5Yro9KanZwjHmPzmHTsoOeVDemfDBuE8dGVnWpqx3unUrE4CDLCAG64XAHB88IFgQV5xMY7DFmc16A6CZvnNBYYVcW+yKj0A/VHTsQ8dwMPNc6X+Gg0VIGbVpzYGWundjRujmGQWi9Eol7+TJ0/R2Nhx2sNlM9YJRPDdDRsM5DGPJB4KHOIhngHhAwixAGAAuDZ2lsuiYnFWBQOYrdEYNochilyiV6YHoH+rRNJkAG+fUw31PzU7Z1EFKPD69CIuQ1Bm6URoh8tFmVym3nc6rZOPyi0cD8HxeHPg3x2InNrbS79JTsYzNXmPuBclsO3ZvKwAOJEGsmI5rT0M+gSf3y9K5LIA1LUEIlL1k0AhCYBH5r9TCqBqib4D+c/1PyInGOThkvuaHCYALhlpbQWBMGR/4IpzTqlpbKQyf0045vdoe0zATHagSYMeWFMkbscnHRYPZjoFJaIiUkz9EJy15j/X3qCsAIqMcFjSWrNE1Iygg0fEmrtLzEUTdT/OhBFht9fHDVCbEUt3LJxi08B8Xj6vTDESriq9lVWqBECgHujqiqAUmufb1X3cfRXoluhjZWiwkOnSUcUS6ZD8LUmmhks6b5j1ezkAkAKZBe5QvPPcNBnoCawMwT66Qxk0R2xwwRAui2iSDGuaPDcubzo3EJq8wcx/9Vmk3QryH42QBQCFF0UagIiJtjX6DskIXTLEucJSHIIIMuO0BOcjn3A3ybU/lu5RCUBc5qA0Ih0Q2EWiCPRk7VfMNhjLW1zETic1tLYZDMKyuSsdfh5l6bwho5+0il4kyA0VohlNcF5FP8DlWo/VB16HYB2hJ0pzgIe2mcXxP2IOumPRY17U0tll8KIkZNb+sppafOxYkQPSaYfchyYoL9GMqWYpTLRIq1QUcT4O3aPQgqVqPwIOIMwDhzX6mQUFIQAgo+9MzcrWrML3mj6+YIKiFCZyhL87RqVQKrEskF+P1BUvfLCAkfRwoPUtq6l5o5+lZb5SolJo6oT8avTCl+c9OTmat6pKW8mLkvBpGzlvsiGuQr4ZEEwA1EQgoR/gNtxIxKBluz+OtMJiF31jHxqXBiAqAUj4WRxpADFM0DCFlv1khvX7Wol4vF4AIldVVxdZqlrIfiCYQPHDy6bAGv7nKYRVY6JewExZVAP+ey5Rv+Ba97aaUHMW5NauLmMZFkegBb/EP14d6NoS9QLWFSzWBmuZza8CQmSpXsAqmGtVy14VALWuuYWWy+W3OteXa4jwceQX6+BKG6J1/8+2VCNkm2222WabbbbZZpttttlmm22rt38DCdA0vq3bcAkAAAAASUVORK5CYII=";
// Favicon
document.querySelector("link[rel=icon]").setAttribute("href", "data:image/png;base64," + spider16);
// Bake button
document.querySelector("#bake img").setAttribute("src", "data:image/png;base64," + spider32);
// About box
document.querySelector(".about-img-left").setAttribute("src", "data:image/png;base64," + spider64);
};
/**
* Replaces all instances of the word "cyber" with "spider".
* #spiderchef
*/
SeasonalWaiter.prototype.insertSpiderText = function() {
// Title
document.title = document.title.replace(/Cyber/g, "Spider");
// Body
SeasonalWaiter.treeWalk(document.body, function(node) {
// process only text nodes
if (node.nodeType === 3) {
node.nodeValue = node.nodeValue.replace(/Cyber/g, "Spider");
}
}, true);
// Bake button
SeasonalWaiter.treeWalk(document.getElementById("bake-group"), function(node) {
// process only text nodes
if (node.nodeType === 3) {
node.nodeValue = node.nodeValue.replace(/Bake/g, "Spin");
}
}, true);
// Recipe title
document.querySelector("#recipe .title").innerHTML = "Web";
};
/** /**
* Listen for the Konami code sequence of keys. Turn the page upside down if they are all heard in * Listen for the Konami code sequence of keys. Turn the page upside down if they are all heard in
* sequence. * sequence.
@@ -102,53 +45,4 @@ SeasonalWaiter.prototype.konamiCodeListener = function(e) {
} }
}; };
/**
* Walks through the entire DOM starting at the specified element and operates on each node.
*
* @static
* @param {element} parent - The DOM node to start from
* @param {Function} fn - The callback function to operate on each node
* @param {booleam} allNodes - Whether to operate on every node or not
*/
SeasonalWaiter.treeWalk = (function() {
// Create closure for constants
const skipTags = {
"SCRIPT": true, "IFRAME": true, "OBJECT": true,
"EMBED": true, "STYLE": true, "LINK": true, "META": true
};
return function(parent, fn, allNodes) {
let node = parent.firstChild;
while (node && node !== parent) {
if (allNodes || node.nodeType === 1) {
if (fn(node) === false) {
return false;
}
}
// If it's an element &&
// has children &&
// has a tagname && is not in the skipTags list
// then, we can enumerate children
if (node.nodeType === 1 &&
node.firstChild &&
!(node.tagName && skipTags[node.tagName])) {
node = node.firstChild;
} else if (node.nextSibling) {
node = node.nextSibling;
} else {
// No child and no nextsibling
// Find parent that has a nextSibling
while ((node = node.parentNode) !== parent) {
if (node.nextSibling) {
node = node.nextSibling;
break;
}
}
}
}
};
})();
export default SeasonalWaiter; export default SeasonalWaiter;

183
src/web/WorkerWaiter.js Normal file
View File

@@ -0,0 +1,183 @@
import Utils from "../core/Utils.js";
import ChefWorker from "worker-loader?inline&fallback=false!../core/ChefWorker.js";
/**
* Waiter to handle conversations with the ChefWorker.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @constructor
* @param {App} app - The main view object for CyberChef.
* @param {Manager} manager - The CyberChef event manager.
*/
const WorkerWaiter = function(app, manager) {
this.app = app;
this.manager = manager;
};
/**
* Sets up the ChefWorker and associated listeners.
*/
WorkerWaiter.prototype.registerChefWorker = function() {
this.chefWorker = new ChefWorker();
this.chefWorker.addEventListener("message", this.handleChefMessage.bind(this));
let docURL = document.location.href.split(/[#?]/)[0];
const index = docURL.lastIndexOf("/");
if (index > 0) {
docURL = docURL.substring(0, index);
}
this.chefWorker.postMessage({"action": "docURL", "data": docURL});
};
/**
* Handler for messages sent back by the ChefWorker.
*
* @param {MessageEvent} e
*/
WorkerWaiter.prototype.handleChefMessage = function(e) {
const r = e.data;
switch (r.action) {
case "bakeSuccess":
this.bakingComplete(r.data);
break;
case "bakeError":
this.app.handleError(r.data);
this.setBakingStatus(false);
break;
case "silentBakeComplete":
break;
case "workerLoaded":
this.app.workerLoaded = true;
this.app.loaded();
break;
case "statusMessage":
this.manager.output.setStatusMsg(r.data);
break;
case "optionUpdate":
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);
break;
}
};
/**
* Updates the UI to show if baking is in process or not.
*
* @param {bakingStatus}
*/
WorkerWaiter.prototype.setBakingStatus = function(bakingStatus) {
this.app.baking = bakingStatus;
this.manager.output.toggleLoader(bakingStatus);
};
/**
* Cancels the current bake by terminating the ChefWorker and creating a new one.
*/
WorkerWaiter.prototype.cancelBake = function() {
this.chefWorker.terminate();
this.registerChefWorker();
this.setBakingStatus(false);
this.manager.controls.showStaleIndicator();
};
/**
* Handler for completed bakes.
*
* @param {Object} response
*/
WorkerWaiter.prototype.bakingComplete = function(response) {
this.setBakingStatus(false);
if (!response) return;
if (response.error) {
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);
};
/**
* Asks the ChefWorker to bake the current input using the current recipe.
*
* @param {string} input
* @param {Object[]} recipeConfig
* @param {Object} options
* @param {number} progress
* @param {boolean} step
*/
WorkerWaiter.prototype.bake = function(input, recipeConfig, options, progress, step) {
this.setBakingStatus(true);
this.chefWorker.postMessage({
action: "bake",
data: {
input: input,
recipeConfig: recipeConfig,
options: options,
progress: progress,
step: step
}
});
};
/**
* 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]
*/
WorkerWaiter.prototype.silentBake = function(recipeConfig) {
this.chefWorker.postMessage({
action: "silentBake",
data: {
recipeConfig: recipeConfig
}
});
};
/**
* Asks the ChefWorker to calculate highlight offsets if possible.
*
* @param {Object[]} recipeConfig
* @param {string} direction
* @param {Object} pos - The position object for the highlight.
* @param {number} pos.start - The start offset.
* @param {number} pos.end - The end offset.
*/
WorkerWaiter.prototype.highlight = function(recipeConfig, direction, pos) {
this.chefWorker.postMessage({
action: "highlight",
data: {
recipeConfig: recipeConfig,
direction: direction,
pos: pos
}
});
};
export default WorkerWaiter;

View File

@@ -1,18 +0,0 @@
/**
* CSS index
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import "google-code-prettify/src/prettify.css";
import "./lib/bootstrap.less";
import "bootstrap-switch/src/less/bootstrap3/build.less";
import "bootstrap-colorpicker/dist/css/bootstrap-colorpicker.css";
import "./structure/overrides.css";
import "./structure/layout.css";
import "./structure/utils.css";
import "./themes/classic.css";

File diff suppressed because one or more lines are too long

View File

@@ -1,113 +0,0 @@
/* Bootstrap */
button,
a:focus {
outline: none;
-moz-outline-style: none;
}
.btn-default {
border-color: #ddd;
}
.btn-default:focus {
background-color: #fff;
border-color: #adadad;
}
.btn-default:hover,
.btn-default:active {
background-color: #ebebeb;
border-color: #adadad;
}
.btn,
.btn-lg,
.nav-tabs>li>a,
.form-control,
.popover,
.alert,
.modal-content,
.tooltip-inner,
.dropdown-menu {
border-radius: 0 !important;
}
input[type="search"] {
-webkit-appearance: searchfield;
box-shadow: none;
}
input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: searchfield-cancel-button;
}
.modal {
overflow-y: auto;
}
.form-control {
background-color: transparent;
}
code {
border: 0;
white-space: pre-wrap;
font-family: Consolas, monospace;
}
pre {
border-radius: 0 !important;
}
blockquote {
font-size: inherit;
}
blockquote a {
cursor: pointer;
}
optgroup {
font-weight: bold;
}
.panel-body:before,
.panel-body:after {
content: "";
}
.table-nonfluid {
width: auto !important;
}
/* Bootstrap-switch */
.bootstrap-switch,
.bootstrap-switch-container,
.bootstrap-switch-handle-on,
.bootstrap-switch-handle-off,
.bootstrap-switch-label {
border-radius: 0 !important;
}
/* Sortable */
.sortable-ghost {
opacity: 0.6;
}
/* Bootstrap Colorpicker */
.colorpicker-element {
float: left;
margin-right: 15px;
}
.colorpicker-color,
.colorpicker-color div {
height: 100px;
}

View File

@@ -1,37 +0,0 @@
.word-wrap {
white-space: pre !important;
word-wrap: normal !important;
overflow-x: scroll !important;
}
.clearfix {
clear: both;
height: 0;
}
.blur {
color: transparent !important;
text-shadow: rgba(0, 0, 0, 0.95) 0 0 10px !important;
}
.no-select {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.konami {
-ms-transform: rotate(180deg);
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
-moz-transform: rotate(180deg);
}
.hl1, .hlyellow { background-color: #fff000; }
.hl2, .hlblue { background-color: #95dfff; }
.hl3, .hlred { background-color: #ffb6b6; } /* Half-Life 3 confirmed :O */
.hl4, .hlorange { background-color: #fcf8e3; }
.hl5, .hlgreen { background-color: #8de768; }

View File

@@ -1,258 +0,0 @@
#banner {
border-bottom: 1px solid #ddd;
}
.title {
border-bottom: 1px solid #ddd;
font-weight: bold;
color: #424242;
background-color: #fafafa;
}
.gutter {
background-color: #eee;
background-repeat: no-repeat;
background-position: 50%;
}
.gutter.gutter-horizontal {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAAeCAYAAAAGos/EAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAlSURBVChTYzxz5sx/BiBgAhEgwPju3TtUEZZ79+6BGcNcDQMDACWJMFs4hNOSAAAAAElFTkSuQmCC');
cursor: ew-resize;
}
.gutter.gutter-vertical {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAACCAYAAABPJGxCAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACHDwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKL2lDQ1BJQ0MgUHJvZmlsZQAASMedlndUVNcWh8+9d3qhzTDSGXqTLjCA9C4gHQRRGGYGGMoAwwxNbIioQEQREQFFkKCAAaOhSKyIYiEoqGAPSBBQYjCKqKhkRtZKfHl57+Xl98e939pn73P32XuftS4AJE8fLi8FlgIgmSfgB3o401eFR9Cx/QAGeIABpgAwWempvkHuwUAkLzcXerrICfyL3gwBSPy+ZejpT6eD/0/SrFS+AADIX8TmbE46S8T5Ik7KFKSK7TMipsYkihlGiZkvSlDEcmKOW+Sln30W2VHM7GQeW8TinFPZyWwx94h4e4aQI2LER8QFGVxOpohvi1gzSZjMFfFbcWwyh5kOAIoktgs4rHgRm4iYxA8OdBHxcgBwpLgvOOYLFnCyBOJDuaSkZvO5cfECui5Lj25qbc2ge3IykzgCgaE/k5XI5LPpLinJqUxeNgCLZ/4sGXFt6aIiW5paW1oamhmZflGo/7r4NyXu7SK9CvjcM4jW94ftr/xS6gBgzIpqs+sPW8x+ADq2AiB3/w+b5iEAJEV9a7/xxXlo4nmJFwhSbYyNMzMzjbgclpG4oL/rfzr8DX3xPSPxdr+Xh+7KiWUKkwR0cd1YKUkpQj49PZXJ4tAN/zzE/zjwr/NYGsiJ5fA5PFFEqGjKuLw4Ubt5bK6Am8Kjc3n/qYn/MOxPWpxrkSj1nwA1yghI3aAC5Oc+gKIQARJ5UNz13/vmgw8F4psXpjqxOPefBf37rnCJ+JHOjfsc5xIYTGcJ+RmLa+JrCdCAACQBFcgDFaABdIEhMANWwBY4AjewAviBYBAO1gIWiAfJgA8yQS7YDApAEdgF9oJKUAPqQSNoASdABzgNLoDL4Dq4Ce6AB2AEjIPnYAa8AfMQBGEhMkSB5CFVSAsygMwgBmQPuUE+UCAUDkVDcRAPEkK50BaoCCqFKqFaqBH6FjoFXYCuQgPQPWgUmoJ+hd7DCEyCqbAyrA0bwwzYCfaGg+E1cBycBufA+fBOuAKug4/B7fAF+Dp8Bx6Bn8OzCECICA1RQwwRBuKC+CERSCzCRzYghUg5Uoe0IF1IL3ILGUGmkXcoDIqCoqMMUbYoT1QIioVKQ21AFaMqUUdR7age1C3UKGoG9QlNRiuhDdA2aC/0KnQcOhNdgC5HN6Db0JfQd9Dj6DcYDIaG0cFYYTwx4ZgEzDpMMeYAphVzHjOAGcPMYrFYeawB1g7rh2ViBdgC7H7sMew57CB2HPsWR8Sp4sxw7rgIHA+XhyvHNeHO4gZxE7h5vBReC2+D98Oz8dn4Enw9vgt/Az+OnydIE3QIdoRgQgJhM6GC0EK4RHhIeEUkEtWJ1sQAIpe4iVhBPE68QhwlviPJkPRJLqRIkpC0k3SEdJ50j/SKTCZrkx3JEWQBeSe5kXyR/Jj8VoIiYSThJcGW2ChRJdEuMSjxQhIvqSXpJLlWMkeyXPKk5A3JaSm8lLaUixRTaoNUldQpqWGpWWmKtKm0n3SydLF0k/RV6UkZrIy2jJsMWyZf5rDMRZkxCkLRoLhQWJQtlHrKJco4FUPVoXpRE6hF1G+o/dQZWRnZZbKhslmyVbJnZEdoCE2b5kVLopXQTtCGaO+XKC9xWsJZsmNJy5LBJXNyinKOchy5QrlWuTty7+Xp8m7yifK75TvkHymgFPQVAhQyFQ4qXFKYVqQq2iqyFAsVTyjeV4KV9JUCldYpHVbqU5pVVlH2UE5V3q98UXlahabiqJKgUqZyVmVKlaJqr8pVLVM9p/qMLkt3oifRK+g99Bk1JTVPNaFarVq/2ry6jnqIep56q/ojDYIGQyNWo0yjW2NGU1XTVzNXs1nzvhZei6EVr7VPq1drTltHO0x7m3aH9qSOnI6XTo5Os85DXbKug26abp3ubT2MHkMvUe+A3k19WN9CP16/Sv+GAWxgacA1OGAwsBS91Hopb2nd0mFDkqGTYYZhs+GoEc3IxyjPqMPohbGmcYTxbuNe408mFiZJJvUmD0xlTFeY5pl2mf5qpm/GMqsyu21ONnc332jeaf5ymcEyzrKDy+5aUCx8LbZZdFt8tLSy5Fu2WE5ZaVpFW1VbDTOoDH9GMeOKNdra2Xqj9WnrdzaWNgKbEza/2BraJto22U4u11nOWV6/fMxO3Y5pV2s3Yk+3j7Y/ZD/ioObAdKhzeOKo4ch2bHCccNJzSnA65vTC2cSZ79zmPOdi47Le5bwr4urhWuja7ybjFuJW6fbYXd09zr3ZfcbDwmOdx3lPtKe3527PYS9lL5ZXo9fMCqsV61f0eJO8g7wrvZ/46Pvwfbp8Yd8Vvnt8H67UWslb2eEH/Lz89vg98tfxT/P/PgAT4B9QFfA00DQwN7A3iBIUFdQU9CbYObgk+EGIbogwpDtUMjQytDF0Lsw1rDRsZJXxqvWrrocrhHPDOyOwEaERDRGzq91W7109HmkRWRA5tEZnTdaaq2sV1iatPRMlGcWMOhmNjg6Lbor+wPRj1jFnY7xiqmNmWC6sfaznbEd2GXuKY8cp5UzE2sWWxk7G2cXtiZuKd4gvj5/munAruS8TPBNqEuYS/RKPJC4khSW1JuOSo5NP8WR4ibyeFJWUrJSBVIPUgtSRNJu0vWkzfG9+QzqUvia9U0AV/Uz1CXWFW4WjGfYZVRlvM0MzT2ZJZ/Gy+rL1s3dkT+S453y9DrWOta47Vy13c+7oeqf1tRugDTEbujdqbMzfOL7JY9PRzYTNiZt/yDPJK817vSVsS1e+cv6m/LGtHlubCyQK+AXD22y31WxHbedu799hvmP/jk+F7MJrRSZF5UUfilnF174y/ariq4WdsTv7SyxLDu7C7OLtGtrtsPtoqXRpTunYHt897WX0ssKy13uj9l4tX1Zes4+wT7hvpMKnonO/5v5d+z9UxlfeqXKuaq1Wqt5RPXeAfWDwoOPBlhrlmqKa94e4h+7WetS212nXlR/GHM44/LQ+tL73a8bXjQ0KDUUNH4/wjowcDTza02jV2Nik1FTSDDcLm6eORR67+Y3rN50thi21rbTWouPguPD4s2+jvx064X2i+yTjZMt3Wt9Vt1HaCtuh9uz2mY74jpHO8M6BUytOdXfZdrV9b/T9kdNqp6vOyJ4pOUs4m3924VzOudnzqeenL8RdGOuO6n5wcdXF2z0BPf2XvC9duex++WKvU++5K3ZXTl+1uXrqGuNax3XL6+19Fn1tP1j80NZv2d9+w+pG503rm10DywfODjoMXrjleuvyba/b1++svDMwFDJ0dzhyeOQu++7kvaR7L+9n3J9/sOkh+mHhI6lH5Y+VHtf9qPdj64jlyJlR19G+J0FPHoyxxp7/lP7Th/H8p+Sn5ROqE42TZpOnp9ynbj5b/Wz8eerz+emCn6V/rn6h++K7Xxx/6ZtZNTP+kv9y4dfiV/Kvjrxe9rp71n/28ZvkN/NzhW/l3x59x3jX+z7s/cR85gfsh4qPeh+7Pnl/eriQvLDwG/eE8/s3BCkeAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAI0lEQVQYV2M8c+bMfwYgUFJSAlEM9+7dA9O05jOBSboDBgYAtPcYZ1oUA30AAAAASUVORK5CYII=');
cursor: ns-resize;
}
.operation {
border: 1px solid #999;
border-top-width: 0;
}
.op-list .operation { /*blue*/
color: #3a87ad;
background-color: #d9edf7;
border-color: #bce8f1;
}
#rec-list .operation { /*green*/
color: #468847;
background-color: #dff0d8;
border-color: #d6e9c6;
}
#controls {
border-top: 1px solid #ddd;
background-color: #fafafa;
}
.textarea-wrapper textarea,
.textarea-wrapper div {
font-family: Consolas, monospace;
font-size: inherit;
}
.io-info {
font-family: Consolas, monospace;
font-weight: normal;
font-size: 8pt;
}
.arg-title {
font-weight: bold;
}
.arg-input {
height: 34px;
font-size: 15px;
line-height: 1.428571429;
color: #424242;
background-color: #fff;
border: 1px solid #ddd;
font-family: Consolas, monospace;
}
select {
padding: 6px 8px;
height: 34px;
border: 1px solid #ddd;
background-color: #fff;
color: #424242;
}
.arg[disabled] {
background-color: #eee;
}
textarea.arg {
color: #424242;
font-family: Consolas, monospace;
}
.break {
color: #b94a48 !important;
background-color: #f2dede !important;
border-color: #eed3d7 !important;
}
.category-title {
background-color: #fafafa;
border-bottom: 1px solid #eee;
font-weight: bold;
}
.category-title[href='#catFavourites'] {
border-bottom-color: #ddd;
}
.category-title[aria-expanded=true] {
border-bottom-color: #ddd;
}
.category-title.collapsed {
border-bottom-color: #eee;
}
.category-title:hover {
color: #3a87ad;
}
#search {
border-bottom: 1px solid #e3e3e3;
}
.dropping-file {
border: 5px dashed #3a87ad !important;
}
.selected-op {
color: #c09853 !important;
background-color: #fcf8e3 !important;
border-color: #fbeed5 !important;
}
.option-item input[type=number] {
font-size: 14px;
line-height: 1.428571429;
color: #555;
background-color: #fff;
border: 1px solid #ccc;
}
.favourites-hover {
color: #468847;
background-color: #dff0d8;
border: 2px dashed #468847 !important;
padding: 8px 8px 9px 8px;
}
#edit-favourites-list {
border: 1px solid #bce8f1;
}
#edit-favourites-list .operation {
border-left: none;
border-right: none;
}
#edit-favourites-list .operation:last-child {
border-bottom: none;
}
.subtext {
font-style: italic;
font-size: 13px;
color: #999;
}
#save-footer {
border-bottom: 1px solid #e5e5e5;
}
.flow-control-op {
color: #396f3a !important;
background-color: #c7e4ba !important;
border-color: #b3dba2 !important;
}
.flow-control-op.break {
color: #94312f !important;
background-color: #eabfbf !important;
border-color: #e2aeb5 !important;
}
#support-modal textarea {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
#save-text,
#load-text {
font-family: Consolas, monospace;
}
button.dropdown-toggle {
background-color: #f4f4f4;
}
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
background-color: #fafafa;
}
::-webkit-scrollbar-thumb {
background-color: #ccc;
}
::-webkit-scrollbar-thumb:hover {
background-color: #bbb;
}
::-webkit-scrollbar-corner {
background-color: #fafafa;
}
.disabled {
color: #999 !important;
background-color: #dfdfdf !important;
border-color: #cdcdcd !important;
}
.grey {
color: #333;
background-color: #f5f5f5;
border-color: #ddd;
}
.dark-blue {
color: #fff;
background-color: #428bca;
border-color: #428bca;
}
.red {
color: #b94a48;
background-color: #f2dede;
border-color: #eed3d7;
}
.amber {
color: #c09853;
background-color: #fcf8e3;
border-color: #fbeed5;
}
.green {
color: #468847;
background-color: #dff0d8;
border-color: #d6e9c6;
}
.blue {
color: #3a87ad;
background-color: #d9edf7;
border-color: #bce8f1;
}

View File

@@ -20,61 +20,132 @@
--> -->
<!-- htmlmin:ignore --> <!-- htmlmin:ignore -->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en" class="classic">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>CyberChef</title> <title>CyberChef</title>
<meta name="copyright" content="Crown Copyright 2016" /> <meta name="copyright" content="Crown Copyright 2016" />
<meta name="description" content="The Cyber Swiss Army Knife" /> <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" /> <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" />
<link rel="icon" type="image/ico" href="<%- require('../static/images/favicon.ico') %>" /> <link rel="icon" type="image/ico" href="<%- require('../static/images/favicon.ico') %>" />
<script type="application/javascript">
"use strict";
// Load theme before the preloader is shown
try {
document.querySelector(":root").className = (JSON.parse(localStorage.getItem("options")) || {}).theme;
} catch (err) {
// LocalStorage access is denied by security settings
}
// Define loading messages
const loadingMsgs = [
"Proving P = NP...",
"Computing 6 x 9...",
"Mining bitcoin...",
"Dividing by 0...",
"Initialising Skynet...",
"[REDACTED]",
"Downloading more RAM...",
"Ordering 1s and 0s...",
"Navigating neural network...",
"Importing machine learning...",
"Issuing Alice and Bob one-time pads...",
"Mining bitcoin cash...",
"Generating key material by trying to escape vim...",
"for i in range(additional): Pylon()",
"(creating unresolved tension...",
"Symlinking emacs and vim to ed...",
];
// Shuffle array using Durstenfeld algorithm
for (let i = loadingMsgs.length - 1; i > 0; --i) {
const j = Math.floor(Math.random() * (i + 1));
const temp = loadingMsgs[i];
loadingMsgs[i] = loadingMsgs[j];
loadingMsgs[j] = temp;
}
// Show next loading message and move it to the end of the array
function changeLoadingMsg() {
const msg = loadingMsgs.shift();
loadingMsgs.push(msg);
try {
const el = document.getElementById("preloader-msg");
if (!el.classList.contains("loading"))
el.classList.add("loading"); // Causes CSS transition on first message
el.innerHTML = msg;
} catch (err) {} // Ignore errors if DOM not yet ready
}
changeLoadingMsg();
window.loadingMsgsInt = setInterval(changeLoadingMsg, (Math.random() * 2000) + 1500);
</script>
<% if (!htmlWebpackPlugin.options.inline) { %>
<script type="application/ld+json">
<% print(JSON.stringify(require("../static/structuredData.json"))); %>
</script>
<% } %>
</head> </head>
<body> <body>
<!-- Preloader overlay -->
<div id="loader-wrapper">
<div id="preloader" class="loader"></div>
<div id="preloader-msg" class="loading-msg"></div>
</div>
<!-- End preloader overlay -->
<span id="edit-favourites" class="btn btn-default btn-sm"><img aria-hidden="true" src="<%- require('../static/images/favourite-16x16.png') %>" alt="Star Icon"/> Edit</span> <span id="edit-favourites" class="btn btn-default btn-sm"><img aria-hidden="true" src="<%- require('../static/images/favourite-16x16.png') %>" alt="Star Icon"/> Edit</span>
<div id="alert" class="alert alert-danger"> <div id="alert" class="alert alert-danger">
<button type="button" class="close" id="alert-close">&times;</button> <button type="button" class="close" id="alert-close">&times;</button>
<span id="alert-content"></span> <span id="alert-content"></span>
</div> </div>
<div id="content-wrapper"> <div id="content-wrapper">
<div id="banner" class="green"> <div id="banner">
<% if (htmlWebpackPlugin.options.inline) { %> <div class="col-md-4" style="text-align: left; padding-left: 10px;">
<span style="float: left; margin-left: 10px;">Compile time: <%= htmlWebpackPlugin.options.compileTime %></span> <% if (htmlWebpackPlugin.options.inline) { %>
<% } else { %> <span>Version <%= htmlWebpackPlugin.options.version %></span>
<a href="cyberchef.htm" style="float: left; margin-left: 10px; margin-right: 80px;" download>Download CyberChef<img aria-hidden="true" src="<%- require('../static/images/download-24x24.png') %>" alt="Download Icon"/></a> <% } else { %>
<% } %> <a href="cyberchef.htm" download>Download CyberChef<img aria-hidden="true" src="<%- require('../static/images/download-24x24.png') %>" alt="Download Icon"/></a>
<span id="notice"> <% } %>
<script type="text/javascript"> </div>
// Must be text/javascript rather than application/javascript otherwise IE won't recognise it... <div class="col-md-4" style="text-align: center;">
if (navigator.userAgent && navigator.userAgent.match(/MSIE \d\d?\./)) { <span id="notice">
document.write("Internet Explorer is not supported, please use Firefox or Chrome instead"); <script type="text/javascript">
alert("Internet Explorer is not supported, please use Firefox or Chrome instead"); // Must be text/javascript rather than application/javascript otherwise IE won't recognise it...
} if (navigator.userAgent && navigator.userAgent.match(/MSIE \d\d?\./)) {
</script> document.write("Internet Explorer is not supported, please use Firefox or Chrome instead");
<noscript>JavaScript is not enabled. Good luck.</noscript> alert("Internet Explorer is not supported, please use Firefox or Chrome instead");
</span> }
<a href="#" id="support" class="banner-right" data-toggle="modal" data-target="#support-modal">About / Support<img aria-hidden="true" src="<%- require('../static/images/help-22x22.png') %>" alt="Question Mark Icon"/></a> </script>
<a href="#" id="options" class="banner-right">Options<img aria-hidden="true" src="<%- require('../static/images/settings-22x22.png') %>" alt="Settings Icon"/></a> <noscript>JavaScript is not enabled. Good luck.</noscript>
</span>
</div>
<div class="col-md-4" style="text-align: right; padding-right: 0;">
<a href="#" id="options">Options<img aria-hidden="true" src="<%- require('../static/images/settings-22x22.png') %>" alt="Settings Icon"/></a>
<a href="#" id="support" data-toggle="modal" data-target="#support-modal">About / Support<img aria-hidden="true" src="<%- require('../static/images/help-22x22.png') %>" alt="Question Mark Icon"/></a>
</div>
</div> </div>
<div id="wrapper"> <div id="workspace-wrapper">
<div id="operations" class="split split-horizontal no-select"> <div id="operations" class="split split-horizontal no-select">
<div class="title no-select">Operations</div> <div class="title no-select">Operations</div>
<input type="search" class="form-control" id="search" placeholder="Search..." autocomplete="off"> <input id="search" type="search" class="form-control" placeholder="Search..." autocomplete="off">
<ul class="op-list" id="search-results"></ul> <ul id="search-results" class="op-list"></ul>
<div class="panel-group no-select" id="categories"></div> <div id="categories" class="panel-group no-select"></div>
</div> </div>
<div id="recipe" class="split split-horizontal no-select"> <div id="recipe" class="split split-horizontal no-select">
<div class="title no-select">Recipe</div> <div class="title no-select">Recipe</div>
<ul id="rec-list" class="no-select"></ul> <ul id="rec-list" class="list-area no-select"></ul>
<div id="controls" class="no-select"> <div id="controls" class="no-select">
<div id="operational-controls"> <div id="operational-controls">
<div id="bake-group"> <div id="bake-group">
<button type="button" class="btn btn-success btn-lg" id="bake"> <button type="button" class="btn btn-success btn-lg" id="bake">
<img aria-hidden="true" src="<%- require('../static/images/cook_male-32x32.png') %>" alt="Chef Icon"/> <img aria-hidden="true" src="<%- require('../static/images/cook_male-32x32.png') %>" alt="Chef Icon"/>
Bake! <span>Bake!</span>
</button> </button>
<label class="btn btn-success btn-lg" id="auto-bake-label" for="auto-bake"> <label class="btn btn-success btn-lg" id="auto-bake-label" for="auto-bake">
<input type="checkbox" checked="checked" id="auto-bake"> <input type="checkbox" checked="checked" id="auto-bake">
@@ -100,7 +171,6 @@
<div id="input" class="split no-select"> <div id="input" class="split no-select">
<div class="title no-select"> <div class="title no-select">
<label for="input-text">Input</label> <label for="input-text">Input</label>
<div class="loading-icon" style="display: none"></div>
<div class="btn-group io-btn-group"> <div class="btn-group io-btn-group">
<button type="button" class="btn btn-default btn-sm" id="clr-io"><img aria-hidden="true" src="<%- require('../static/images/recycle-16x16.png') %>" alt="Recycle Icon"/> Clear I/O</button> <button type="button" class="btn btn-default btn-sm" id="clr-io"><img aria-hidden="true" src="<%- require('../static/images/recycle-16x16.png') %>" alt="Recycle Icon"/> Clear I/O</button>
<button type="button" class="btn btn-default btn-sm" id="reset-layout"><img aria-hidden="true" src="<%- require('../static/images/layout-16x16.png') %>" alt="Grid Icon"/> Reset layout</button> <button type="button" class="btn btn-default btn-sm" id="reset-layout"><img aria-hidden="true" src="<%- require('../static/images/layout-16x16.png') %>" alt="Grid Icon"/> Reset layout</button>
@@ -117,7 +187,6 @@
<div id="output" class="split"> <div id="output" class="split">
<div class="title no-select"> <div class="title no-select">
<label for="output-text">Output</label> <label for="output-text">Output</label>
<div class="loading-icon" style="display: none"></div>
<div class="btn-group io-btn-group"> <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="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="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="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>
@@ -126,11 +195,16 @@
</div> </div>
<div class="io-info" id="output-info"></div> <div class="io-info" id="output-info"></div>
<div class="io-info" id="output-selection-info"></div> <div class="io-info" id="output-selection-info"></div>
<span id="stale-indicator" title="The output is stale.&#10;The input or recipe has changed since this output was generated. Bake again to get the new value.">&#x1F551;</span>
</div> </div>
<div class="textarea-wrapper"> <div class="textarea-wrapper">
<div id="output-highlighter" class="no-select"></div> <div id="output-highlighter" class="no-select"></div>
<div id="output-html"></div> <div id="output-html"></div>
<textarea id="output-text" readonly="readonly"></textarea> <textarea id="output-text" readonly="readonly"></textarea>
<div id="output-loader">
<div class="loader"></div>
<div class="loading-msg"></div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -147,7 +221,22 @@
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group">
<label for="save-text">Save your recipe to local storage or copy the following string to load later</label> <label for="save-text">Save your recipe to local storage or copy the following string to load later</label>
<textarea class="form-control" id="save-text" rows="5"></textarea> <ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#chef-format" role="tab" data-toggle="tab">Chef format</a></li>
<li role="presentation"><a href="#clean-json" role="tab" data-toggle="tab">Clean JSON</a></li>
<li role="presentation"><a href="#compact-json" role="tab" data-toggle="tab">Compact JSON</a></li>
</ul>
<div class="tab-content" id="save-texts">
<div role="tabpanel" class="tab-pane active" id="chef-format">
<textarea class="form-control" id="save-text-chef" rows="5"></textarea>
</div>
<div role="tabpanel" class="tab-pane" id="clean-json">
<textarea class="form-control" id="save-text-clean" rows="5"></textarea>
</div>
<div role="tabpanel" class="tab-pane" id="compact-json">
<textarea class="form-control" id="save-text-compact" rows="5"></textarea>
</div>
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="save-name">Recipe name</label> <label for="save-name">Recipe name</label>
@@ -165,6 +254,7 @@
<input type="checkbox" id="save-link-recipe-checkbox" checked> <label for="save-link-recipe-checkbox"> Include recipe </label> <input type="checkbox" id="save-link-recipe-checkbox" checked> <label for="save-link-recipe-checkbox"> Include recipe </label>
<input type="checkbox" id="save-link-input-checkbox" checked> <label for="save-link-input-checkbox"> Include input </label> <input type="checkbox" id="save-link-input-checkbox" checked> <label for="save-link-input-checkbox"> Include input </label>
</div> </div>
<br>
<a id="save-link" style="word-wrap: break-word;"></a> <a id="save-link" style="word-wrap: break-word;"></a>
</div> </div>
</div> </div>
@@ -208,32 +298,36 @@
<div class="modal-body" id="options-body"> <div class="modal-body" id="options-body">
<p style="font-weight: bold">Please note that these options will persist between sessions.</p> <p style="font-weight: bold">Please note that these options will persist between sessions.</p>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="update_url" id="update_url" checked /> <select option="theme" id="theme">
<label for="update_url"> Update the URL when the input or recipe changes </label> <option value="classic">Classic</option>
<option value="dark">Dark</option>
<option value="geocities">GeoCities</option>
</select>
<label for="theme"> Theme (only supported in modern browsers)</label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="show_highlighter" id="show_highlighter" checked /> <input type="checkbox" option="updateUrl" id="updateUrl" checked />
<label for="show_highlighter"> Highlight selected bytes in output and input (when possible) </label> <label for="updateUrl"> Update the URL when the input or recipe changes </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="treat_as_utf8" id="treat_as_utf8" checked /> <input type="checkbox" option="showHighlighter" id="showHighlighter" checked />
<label for="treat_as_utf8"> Treat output as UTF-8 if possible </label> <label for="showHighlighter"> Highlight selected bytes in output and input (when possible) </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="word_wrap" id="word_wrap" checked /> <input type="checkbox" option="treatAsUtf8" id="treatAsUtf8" checked />
<label for="word_wrap"> Word wrap the input and output </label> <label for="treatAsUtf8"> Treat output as UTF-8 if possible </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="show_errors" id="show_errors" checked /> <input type="checkbox" option="wordWrap" id="wordWrap" checked />
<label for="show_errors"> Operation error reporting (recommended) </label> <label for="wordWrap"> Word wrap the input and output </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="number" option="error_timeout" id="error_timeout" /> <input type="checkbox" option="showErrors" id="showErrors" checked />
<label for="error_timeout"> Operation error timeout in ms (0 for never) </label> <label for="showErrors"> Operation error reporting (recommended) </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="number" option="auto_bake_threshold" id="auto_bake_threshold"/> <input type="number" option="errorTimeout" id="errorTimeout" />
<label for="auto_bake_threshold"> Auto Bake threshold in ms </label> <label for="errorTimeout"> Operation error timeout in ms (0 for never) </label>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@@ -280,9 +374,12 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<img aria-hidden="true" class="about-img-left" src="<%- require('../static/images/cyberchef-128x128.png') %>" alt="CyberChef Logo"/> <img aria-hidden="true" class="about-img-left" src="<%- require('../static/images/cyberchef-128x128.png') %>" alt="CyberChef Logo"/>
<p class="subtext">Compile time: <%= htmlWebpackPlugin.options.compileTime %></p> <p class="subtext">
<p>&copy Crown Copyright 2016.</p> Version <%= htmlWebpackPlugin.options.version %><br>
<p>Licenced under the Apache Licence, Version 2.0.</p> Compile time: <%= htmlWebpackPlugin.options.compileTime %>
</p>
<p>&copy; Crown Copyright 2016.</p>
<p>Released under the Apache Licence, Version 2.0.</p>
<br> <br>
<br> <br>
<div> <div>
@@ -309,14 +406,16 @@
</a> </a>
</blockquote> </blockquote>
<div class="collapse" id="faq-examples"> <div class="collapse" id="faq-examples">
<p>There are well over 100 operations in CyberChef allowing you to carry simple and complex tasks easily. Here are some examples:</p> <p>There are around 200 operations in CyberChef allowing you to carry out simple and complex tasks easily. Here are some examples:</p>
<ul> <ul>
<li><a href="?recipe=%5B%7B%22op%22%3A%22From%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%2Ctrue%5D%7D%5D&input=VTI4Z2JHOXVaeUJoYm1RZ2RHaGhibXR6SUdadmNpQmhiR3dnZEdobElHWnBjMmd1">Decode a Base64-encoded string</a></li> <li><a href="#recipe=From_Base64('A-Za-z0-9%2B/%3D',true)&input=VTI4Z2JHOXVaeUJoYm1RZ2RHaGhibXR6SUdadmNpQmhiR3dnZEdobElHWnBjMmd1">Decode a Base64-encoded string</a></li>
<li><a href="?recipe=%5B%7B%22op%22%3A%22Translate%20DateTime%20Format%22%2C%22args%22%3A%5B%22Standard%20date%20and%20time%22%2C%22DD%2FMM%2FYYYY%20HH%3Amm%3Ass%22%2C%22UTC%22%2C%22dddd%20Do%20MMMM%20YYYY%20HH%3Amm%3Ass%20Z%20z%22%2C%22Australia%2FQueensland%22%5D%7D%5D&input=MTUvMDYvMjAxNSAyMDo0NTowMA">Convert a date and time to a different time zone</a></li> <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=%5B%7B%22op%22%3A%22Parse%20IPv6%20address%22%2C%22args%22%3A%5B%5D%7D%5D&input=MjAwMTowMDAwOjQxMzY6ZTM3ODo4MDAwOjYzYmY6M2ZmZjpmZGQy">Parse a Teredo IPv6 address</a></li> <li><a href="#recipe=Parse_IPv6_address()&input=MjAwMTowMDAwOjQxMzY6ZTM3ODo4MDAwOjYzYmY6M2ZmZjpmZGQy">Parse a Teredo IPv6 address</a></li>
<li><a href="?recipe=%5B%7B%22op%22%3A%22From%20Hexdump%22%2C%22args%22%3A%5B%5D%7D%2C%7B%22op%22%3A%22Gunzip%22%2C%22args%22%3A%5B%5D%7D%5D&input=MDAwMDAwMDAgIDFmIDhiIDA4IDAwIDEyIGJjIGYzIDU3IDAwIGZmIDBkIGM3IGMxIDA5IDAwIDIwICB8Li4uLi6881cu%2Fy7HwS4uIHwKMDAwMDAwMTAgIDA4IDA1IGQwIDU1IGZlIDA0IDJkIGQzIDA0IDFmIGNhIDhjIDQ0IDIxIDViIGZmICB8Li7QVf4uLdMuLsouRCFb%2F3wKMDAwMDAwMjAgIDYwIGM3IGQ3IDAzIDE2IGJlIDQwIDFmIDc4IDRhIDNmIDA5IDg5IDBiIDlhIDdkICB8YMfXLi6%2BQC54Sj8uLi4ufXwKMDAwMDAwMzAgIDRlIGM4IDRlIDZkIDA1IDFlIDAxIDhiIDRjIDI0IDAwIDAwIDAwICAgICAgICAgICB8TshObS4uLi5MJC4uLnw">Convert data from a hexdump, then decompress</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=%5B%7B%22op%22%3A%22Fork%22%2C%22args%22%3A%5B%22%5C%5Cn%22%2C%22%5C%5Cn%22%5D%7D%2C%7B%22op%22%3A%22From%20UNIX%20Timestamp%22%2C%22args%22%3A%5B%22Seconds%20(s)%22%5D%7D%5D&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA">Display multiple timestamps as full dates</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=%5B%7B%22op%22%3A%22Fork%22%2C%22args%22%3A%5B%22%5C%5Cn%22%2C%22%5C%5Cn%22%5D%7D%2C%7B%22op%22%3A%22Conditional%20Jump%22%2C%22args%22%3A%5B%221%22%2C%222%22%2C%2210%22%5D%7D%2C%7B%22op%22%3A%22To%20Hex%22%2C%22args%22%3A%5B%22Space%22%5D%7D%2C%7B%22op%22%3A%22Return%22%2C%22args%22%3A%5B%5D%7D%2C%7B%22op%22%3A%22To%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%5D%7D%5D&input=U29tZSBkYXRhIHdpdGggYSAxIGluIGl0ClNvbWUgZGF0YSB3aXRoIGEgMiBpbiBpdA">Carry out different operations on data of different types</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=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> </ul>
</div> </div>
<blockquote> <blockquote>
@@ -336,7 +435,7 @@
<div class="collapse" id="faq-fork"> <div class="collapse" id="faq-fork">
<p>Maybe you have 10 timestamps that you want to parse or 16 encoded strings that all have the same key.</p> <p>Maybe you have 10 timestamps that you want to parse or 16 encoded strings that all have the same key.</p>
<p>The 'Fork' operation (found in the 'Flow control' category) splits up the input line by line and runs all subsequent operations on each line separately. Each output is then displayed on a separate line. These delimiters can be changed, so if your inputs are separated by commas, you can change the split delimiter to a comma instead.</p> <p>The 'Fork' operation (found in the 'Flow control' category) splits up the input line by line and runs all subsequent operations on each line separately. Each output is then displayed on a separate line. These delimiters can be changed, so if your inputs are separated by commas, you can change the split delimiter to a comma instead.</p>
<p><a href='?recipe=%5B%7B"op"%3A"Fork"%2C"args"%3A%5B"%5C%5Cn"%2C"%5C%5Cn"%5D%7D%2C%7B"op"%3A"From%20UNIX%20Timestamp"%2C"args"%3A%5B"Seconds%20(s)"%5D%7D%5D&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA%3D%3D'>Click here</a> for an example.</p> <p><a href="#recipe=Fork('%5C%5Cn','%5C%5Cn',false)From_UNIX_Timestamp('Seconds%20(s)')&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA">Click here</a> for an example.</p>
</div> </div>
</div> </div>
<div role="tabpanel" class="tab-pane" id="report-bug"> <div role="tabpanel" class="tab-pane" id="report-bug">
@@ -348,21 +447,25 @@
<a class="btn btn-primary" href="https://github.com/gchq/CyberChef/issues/new" role="button">Raise issue on GitHub</a> <a class="btn btn-primary" href="https://github.com/gchq/CyberChef/issues/new" role="button">Raise issue on GitHub</a>
</div> </div>
<div role="tabpanel" class="tab-pane" id="about" style="padding: 20px;"> <div role="tabpanel" class="tab-pane" id="about" style="padding: 20px;">
<h4>What</h4> <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> <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>
<h4>Why</h4> <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> <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>
<h4>How</h4> <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>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> <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>
<h4>Who</h4> <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> <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>
<h4>Aim</h4> <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> <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> <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 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>

View File

@@ -4,8 +4,8 @@
* @license Apache-2.0 * @license Apache-2.0
*/ */
// CSS // Styles
import "./css/index.js"; import "./stylesheets/index.js";
// Libs // Libs
import "babel-polyfill"; import "babel-polyfill";
@@ -17,7 +17,7 @@ import CanvasComponents from "../core/lib/canvascomponents.js";
// CyberChef // CyberChef
import App from "./App.js"; import App from "./App.js";
import Categories from "../core/config/Categories.js"; import Categories from "../core/config/Categories.js";
import OperationConfig from "../core/config/OperationConfig.js"; import OperationConfig from "../core/config/MetaConfig.js";
/** /**
@@ -38,14 +38,14 @@ function main() {
]; ];
const defaultOptions = { const defaultOptions = {
updateUrl : true, updateUrl: true,
showHighlighter : true, showHighlighter: true,
treatAsUtf8 : true, treatAsUtf8: true,
wordWrap : true, wordWrap: true,
showErrors : true, showErrors: true,
errorTimeout : 4000, errorTimeout: 4000,
autoBakeThreshold : 200, attemptHighlight: true,
attemptHighlight : true, theme: "classic",
}; };
document.removeEventListener("DOMContentLoaded", main, false); document.removeEventListener("DOMContentLoaded", main, false);

View File

@@ -9,7 +9,7 @@
ga('create', 'UA-85682716-2', 'auto'); ga('create', 'UA-85682716-2', 'auto');
// Specifying location.pathname here overrides the default URL which would include arguments. // Specifying location.pathname here overrides the default URL which could include arguments.
// This method prevents Google Analytics from logging any recipe or input data in the URL. // This method prevents Google Analytics from logging any recipe or input data in the URL.
ga('send', 'pageview', location.pathname); ga('send', 'pageview', location.pathname);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 773 B

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 702 B

After

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 796 B

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Some files were not shown because too many files have changed in this diff Show More