mirror of
https://github.com/gchq/CyberChef
synced 2025-12-05 23:53:27 +00:00
Compare commits
211 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcc28438ff | ||
|
|
df7c1721f5 | ||
|
|
403296cc59 | ||
|
|
addb2b4448 | ||
|
|
8556bdcdeb | ||
|
|
8fc5f59647 | ||
|
|
f7729c0fd2 | ||
|
|
e2376c7c71 | ||
|
|
2cefd3b941 | ||
|
|
68ab2da866 | ||
|
|
72f7f0b70c | ||
|
|
d18a7df3bc | ||
|
|
f81012ef6e | ||
|
|
411bba53a8 | ||
|
|
e2af3c78e7 | ||
|
|
cb66508b8a | ||
|
|
c56038a1e2 | ||
|
|
81e62a6c22 | ||
|
|
c501720cdd | ||
|
|
92b5aa08f3 | ||
|
|
1097170a68 | ||
|
|
e6e2b169dc | ||
|
|
e41145427e | ||
|
|
caadf8e762 | ||
|
|
1143c23ad9 | ||
|
|
c1bb93eec1 | ||
|
|
1ae9c82e29 | ||
|
|
715ca1c292 | ||
|
|
2f5b0533d8 | ||
|
|
5ece79c74d | ||
|
|
12c226f874 | ||
|
|
567474ce00 | ||
|
|
209a5b7274 | ||
|
|
e962c9594b | ||
|
|
242bad09ea | ||
|
|
8dd223f1cb | ||
|
|
d15662dea4 | ||
|
|
f47a408755 | ||
|
|
8360c9e9f9 | ||
|
|
328142dac7 | ||
|
|
372b2378a8 | ||
|
|
574207c626 | ||
|
|
ead64dcb47 | ||
|
|
da473de9f0 | ||
|
|
a177e2ab7e | ||
|
|
8cef6db482 | ||
|
|
79a3128491 | ||
|
|
cb1fe80214 | ||
|
|
19f2e6dae0 | ||
|
|
74394a773d | ||
|
|
d491e95d1c | ||
|
|
881523ce54 | ||
|
|
502a9fbb92 | ||
|
|
cc44fe6557 | ||
|
|
5b1ac3de18 | ||
|
|
5a2a649d8e | ||
|
|
1aef193b40 | ||
|
|
750fa45c04 | ||
|
|
50f2819699 | ||
|
|
ab55b91da1 | ||
|
|
59f7774964 | ||
|
|
b5eb44af9f | ||
|
|
8518fa67f2 | ||
|
|
f6b68f9880 | ||
|
|
f6b2783f8b | ||
|
|
078849041f | ||
|
|
1c711f5e03 | ||
|
|
614af0602a | ||
|
|
e55cfe0bc1 | ||
|
|
2b703b2b9b | ||
|
|
170feaaff2 | ||
|
|
870c2b6d8b | ||
|
|
eee8b7db56 | ||
|
|
3c669a075e | ||
|
|
f528930ad2 | ||
|
|
231322eddf | ||
|
|
8e6763c165 | ||
|
|
f091918575 | ||
|
|
bb077c87b3 | ||
|
|
fe8f8bc712 | ||
|
|
abe87830cd | ||
|
|
7490651a06 | ||
|
|
6220128a74 | ||
|
|
ec205f4f7d | ||
|
|
512487328d | ||
|
|
6fbb2f26d1 | ||
|
|
c91bfeaa81 | ||
|
|
aa2b3b2843 | ||
|
|
90d8be48d4 | ||
|
|
aa3f781e18 | ||
|
|
aa6890432c | ||
|
|
192d0ed8a6 | ||
|
|
fff188eb30 | ||
|
|
71067939e3 | ||
|
|
b07c014b48 | ||
|
|
f2c073798b | ||
|
|
4cc38db895 | ||
|
|
2762319dbb | ||
|
|
8e5d43dfa8 | ||
|
|
82ad8cc444 | ||
|
|
50a3cc57ad | ||
|
|
1d6bf39548 | ||
|
|
7554cbda72 | ||
|
|
6a67fe09de | ||
|
|
f07263ca2a | ||
|
|
bfbefb7318 | ||
|
|
ec02b7deda | ||
|
|
fa938f832f | ||
|
|
6f59d9217c | ||
|
|
7176e5ca6e | ||
|
|
429829471f | ||
|
|
4760e539b7 | ||
|
|
f53e7ad617 | ||
|
|
28c83fa921 | ||
|
|
4588cd151c | ||
|
|
2d9f87abef | ||
|
|
491e6f5f5f | ||
|
|
ab7c05284d | ||
|
|
0586fa0e01 | ||
|
|
53eba2337c | ||
|
|
283d3e1e7b | ||
|
|
7992a540ae | ||
|
|
3f3e7a78eb | ||
|
|
8d3d39acd3 | ||
|
|
7b20aba2ff | ||
|
|
bbfb732d8f | ||
|
|
566adbcda5 | ||
|
|
1bff490fa4 | ||
|
|
d38375a08c | ||
|
|
56551712d6 | ||
|
|
c241d2f90b | ||
|
|
0ba28dc891 | ||
|
|
ac9af6d2ba | ||
|
|
90d9e087f7 | ||
|
|
50b24d9a56 | ||
|
|
10f42e9a7f | ||
|
|
987bd303a0 | ||
|
|
a3f58fb831 | ||
|
|
f52f5a0edb | ||
|
|
b58942f69a | ||
|
|
68e52d1645 | ||
|
|
9fc7e6cd98 | ||
|
|
87f346d88c | ||
|
|
e423ff2639 | ||
|
|
fa6905ef00 | ||
|
|
8684bc0158 | ||
|
|
a96eb450de | ||
|
|
d079420d46 | ||
|
|
124ff83206 | ||
|
|
2c9e67ad1d | ||
|
|
75a554e215 | ||
|
|
849d41ee56 | ||
|
|
e18ec5f2b2 | ||
|
|
50e4daeaf2 | ||
|
|
f7f07f2cb5 | ||
|
|
e2161ec934 | ||
|
|
1e4d2ba90d | ||
|
|
caf794b01d | ||
|
|
e81122739b | ||
|
|
a0aa363203 | ||
|
|
bad0816115 | ||
|
|
53a3f3d452 | ||
|
|
ff94172b3c | ||
|
|
af71ca6a25 | ||
|
|
0e7989111f | ||
|
|
db8955d90d | ||
|
|
5a5ce1101b | ||
|
|
8ee3742216 | ||
|
|
194eb184f3 | ||
|
|
98f59ace3a | ||
|
|
c1fb6d9776 | ||
|
|
670566b7eb | ||
|
|
fc7d2c2f52 | ||
|
|
0fea84ed7a | ||
|
|
5e7f8e3976 | ||
|
|
fe4c5f5899 | ||
|
|
d47b7b9242 | ||
|
|
09b6661e35 | ||
|
|
57b1667b69 | ||
|
|
5e9380b550 | ||
|
|
fc8a0480fb | ||
|
|
317327d097 | ||
|
|
ecd0ac2521 | ||
|
|
f9eaf67db2 | ||
|
|
99e0c8d5e3 | ||
|
|
7a951d86d8 | ||
|
|
d9dfaec84c | ||
|
|
80719ae368 | ||
|
|
9407809356 | ||
|
|
f7e958e7a1 | ||
|
|
5d75f8636a | ||
|
|
4e00ac9300 | ||
|
|
4ca2a30249 | ||
|
|
08a31523b2 | ||
|
|
224d79be05 | ||
|
|
435ed587a5 | ||
|
|
81082ea001 | ||
|
|
1d3229a729 | ||
|
|
a9e60d3450 | ||
|
|
f9ddee7d80 | ||
|
|
ef0d3b73b0 | ||
|
|
5368040e83 | ||
|
|
b9b4147c2f | ||
|
|
2b47631f4d | ||
|
|
298e8e8491 | ||
|
|
6ad3728314 | ||
|
|
772f9a806e | ||
|
|
ae8d1f2178 | ||
|
|
9bc6c46dc3 | ||
|
|
dcd8f98e8c | ||
|
|
60c8da7bbb |
2
.babelrc
2
.babelrc
@@ -5,7 +5,7 @@
|
||||
"chrome": 40,
|
||||
"firefox": 35,
|
||||
"edge": 14,
|
||||
"node": "6.5",
|
||||
"node": "6.5"
|
||||
},
|
||||
"modules": false,
|
||||
"useBuiltIns": true
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
}],
|
||||
|
||||
// disable rules from base configurations
|
||||
"no-console": "off",
|
||||
"no-control-regex": "off",
|
||||
|
||||
// stylistic conventions
|
||||
@@ -52,6 +51,7 @@
|
||||
"mode": "minimum"
|
||||
}],
|
||||
"indent": ["error", 4, {
|
||||
"ignoreComments": true,
|
||||
"ArrayExpression": "first",
|
||||
"SwitchCase": 1
|
||||
}],
|
||||
@@ -90,6 +90,7 @@
|
||||
"$": false,
|
||||
"jQuery": false,
|
||||
"moment": false,
|
||||
"log": false,
|
||||
|
||||
"COMPILE_TIME": false,
|
||||
"COMPILE_MSG": false,
|
||||
|
||||
@@ -11,6 +11,7 @@ script:
|
||||
- grunt node
|
||||
- grunt prod --msg="$COMPILE_MSG"
|
||||
before_deploy:
|
||||
- grunt exec:sitemap
|
||||
- grunt copy:ghPages
|
||||
deploy:
|
||||
- provider: pages
|
||||
|
||||
54
Gruntfile.js
54
Gruntfile.js
@@ -131,7 +131,7 @@ module.exports = function (grunt) {
|
||||
|
||||
grunt.initConfig({
|
||||
clean: {
|
||||
dev: ["build/dev/*", "src/core/config/MetaConfig.js"],
|
||||
dev: ["build/dev/*"],
|
||||
prod: ["build/prod/*", "src/core/config/MetaConfig.js"],
|
||||
test: ["build/test/*", "src/core/config/MetaConfig.js"],
|
||||
node: ["build/node/*", "src/core/config/MetaConfig.js"],
|
||||
@@ -185,8 +185,12 @@ module.exports = function (grunt) {
|
||||
webpack: {
|
||||
options: webpackConfig,
|
||||
metaConf: {
|
||||
mode: "production",
|
||||
target: "node",
|
||||
entry: "./src/core/config/OperationConfig.js",
|
||||
entry: [
|
||||
"babel-polyfill",
|
||||
"./src/core/config/OperationConfig.js"
|
||||
],
|
||||
output: {
|
||||
filename: "MetaConfig.js",
|
||||
path: __dirname + "/src/core/config/",
|
||||
@@ -197,8 +201,12 @@ module.exports = function (grunt) {
|
||||
externals: [NodeExternals()],
|
||||
},
|
||||
metaConfDev: {
|
||||
mode: "development",
|
||||
target: "node",
|
||||
entry: "./src/core/config/OperationConfig.js",
|
||||
entry: [
|
||||
"babel-polyfill",
|
||||
"./src/core/config/OperationConfig.js"
|
||||
],
|
||||
output: {
|
||||
filename: "MetaConfig.js",
|
||||
path: __dirname + "/src/core/config/",
|
||||
@@ -210,9 +218,11 @@ module.exports = function (grunt) {
|
||||
watch: true
|
||||
},
|
||||
web: {
|
||||
mode: "production",
|
||||
target: "web",
|
||||
entry: Object.assign({
|
||||
main: "./src/web/index.js"
|
||||
main: "./src/web/index.js",
|
||||
sitemap: "./src/web/static/sitemap.js"
|
||||
}, moduleEntryPoints),
|
||||
output: {
|
||||
path: __dirname + "/build/prod"
|
||||
@@ -224,15 +234,6 @@ module.exports = function (grunt) {
|
||||
},
|
||||
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: "index.html",
|
||||
template: "./src/web/html/index.html",
|
||||
@@ -249,6 +250,7 @@ module.exports = function (grunt) {
|
||||
]
|
||||
},
|
||||
webInline: {
|
||||
mode: "production",
|
||||
target: "web",
|
||||
entry: "./src/web/index.js",
|
||||
output: {
|
||||
@@ -256,21 +258,14 @@ module.exports = function (grunt) {
|
||||
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 webpack.DefinePlugin(Object.assign({}, BUILD_CONSTANTS, {
|
||||
INLINE: "true"
|
||||
})),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "cyberchef.htm",
|
||||
template: "./src/web/html/index.html",
|
||||
compileTime: compileTime,
|
||||
version: pkg.version,
|
||||
version: pkg.version + "s",
|
||||
inline: true,
|
||||
minify: {
|
||||
removeComments: true,
|
||||
@@ -282,6 +277,7 @@ module.exports = function (grunt) {
|
||||
]
|
||||
},
|
||||
tests: {
|
||||
mode: "development",
|
||||
target: "node",
|
||||
entry: "./test/index.js",
|
||||
externals: [NodeExternals()],
|
||||
@@ -294,6 +290,7 @@ module.exports = function (grunt) {
|
||||
]
|
||||
},
|
||||
node: {
|
||||
mode: "production",
|
||||
target: "node",
|
||||
entry: "./src/node/index.js",
|
||||
externals: [NodeExternals()],
|
||||
@@ -320,11 +317,13 @@ module.exports = function (grunt) {
|
||||
children: false,
|
||||
chunks: false,
|
||||
modules: false,
|
||||
warningsFilter: /source-map/,
|
||||
entrypoints: false,
|
||||
warningsFilter: [/source-map/, /dependency is an expression/],
|
||||
}
|
||||
},
|
||||
start: {
|
||||
webpack: {
|
||||
mode: "development",
|
||||
target: "web",
|
||||
entry: Object.assign({
|
||||
main: "./src/web/index.js"
|
||||
@@ -371,7 +370,7 @@ module.exports = function (grunt) {
|
||||
expand: true,
|
||||
src: "docs/**",
|
||||
dest: "build/prod/"
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -400,6 +399,9 @@ module.exports = function (grunt) {
|
||||
cleanGit: {
|
||||
command: "git gc --prune=now --aggressive"
|
||||
},
|
||||
sitemap: {
|
||||
command: "node build/prod/sitemap.js > build/prod/sitemap.xml"
|
||||
}
|
||||
},
|
||||
execute: {
|
||||
test: "build/test/index.js"
|
||||
|
||||
27
README.md
27
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://travis-ci.org/gchq/CyberChef)
|
||||
[](https://david-dm.org/gchq/CyberChef)
|
||||
[](https://www.npmjs.com/package/cyberchef)
|
||||
[](https://www.npmjs.com/package/cyberchef)
|
||||

|
||||
[](https://github.com/gchq/CyberChef/blob/master/LICENSE)
|
||||
[](https://gitter.im/gchq/CyberChef?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
@@ -27,7 +27,7 @@ Cryptographic operations in CyberChef should not be relied upon to provide secur
|
||||
|
||||
There are four main areas in CyberChef:
|
||||
|
||||
1. The **input** box in the top right, where you can paste, type or drag the data you want to operate on.
|
||||
1. The **input** box in the top right, where you can paste, type or drag the text or file you want to operate on.
|
||||
2. The **output** box in the bottom right, where the outcome of your processing will be displayed.
|
||||
3. The **operations** list on the far left, where you can find all the operations that CyberChef is capable of in categorised lists, or by searching.
|
||||
4. The **recipe** area in the middle, where you can drag the operations that you want to use and specify arguments and options.
|
||||
@@ -42,32 +42,32 @@ You can use as many operations as you like in simple or complex ways. Some examp
|
||||
- [Display multiple timestamps as full dates][7]
|
||||
- [Carry out different operations on data of different types][8]
|
||||
- [Use parts of the input as arguments to operations][9]
|
||||
- [Perform AES decryption, extracting the IV from the beginning of the cipher stream][10]
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- Drag and drop
|
||||
- Operations can be dragged in and out of the recipe list, or reorganised.
|
||||
- Files can be dragged over the input box to load them directly.
|
||||
- Files can be dragged over the input box to load them directly into the browser.
|
||||
- Auto Bake
|
||||
- Whenever you modify the input or the recipe, CyberChef will automatically “bake” for you and produce the output immediately.
|
||||
- Whenever you modify the input or the recipe, CyberChef will automatically "bake" for you and produce the output immediately.
|
||||
- This can be turned off and operated manually if it is affecting performance (if the input is very large, for instance).
|
||||
- If any bake takes longer than 200 milliseconds, auto bake will be switched off automatically to prevent further performance issues.
|
||||
- Breakpoints
|
||||
- You can set breakpoints on any operation in your recipe to pause execution before running it.
|
||||
- You can also step through the recipe one operation at a time to see what the data looks like at each stage.
|
||||
- Save and load recipes
|
||||
- If you come up with an awesome recipe that you know you’ll want to use again, just click save and add it to your local storage. It'll be waiting for you next time you visit CyberChef.
|
||||
- You can also copy a URL which includes your recipe and input which can be shared with others.
|
||||
- If you come up with an awesome recipe that you know you’ll want to use again, just click "Save recipe" and add it to your local storage. It'll be waiting for you next time you visit CyberChef.
|
||||
- You can also copy the URL, which includes your recipe and input, to easily share it with others.
|
||||
- Search
|
||||
- If you know the name of the operation you want or a word associated with it, start typing it into the search field and any matching operations will immediately be shown.
|
||||
- Highlighting
|
||||
- When you highlight text in the input or output, the offset and length values will be displayed and, if possible, the corresponding data will be highlighted in the output or input respectively (example: [highlight the word 'question' in the input to see where it appears in the output][10]).
|
||||
- When you highlight text in the input or output, the offset and length values will be displayed and, if possible, the corresponding data will be highlighted in the output or input respectively (example: [highlight the word 'question' in the input to see where it appears in the output][11]).
|
||||
- Save to file and load from file
|
||||
- You can save the output to a file at any time or load a file by dragging and dropping it into the input field (note that files larger than about 500kb may cause your browser to hang or even crash due to the way that browsers handle large amounts of textual data).
|
||||
- You can save the output to a file at any time or load a file by dragging and dropping it into the input field. Files up to around 500MB are supported (depending on your browser), however some operations may take a very long time to run over this much data.
|
||||
- CyberChef is entirely client-side
|
||||
- It should be noted that none of your input or recipe configuration is ever sent to the CyberChef web server - all processing is carried out within your browser, on your own computer.
|
||||
- Due to this feature, CyberChef can be compiled into a single HTML file. You can download this file and drop it into a virtual machine, share it with other people, or use it independently on your desktop.
|
||||
- It should be noted that none of your recipe configuration or input (either text or files) is ever sent to the CyberChef web server - all processing is carried out within your browser, on your own computer.
|
||||
- Due to this feature, CyberChef can be compiled into a single HTML file. You can download this file and drop it into a virtual machine, share it with other people, or use it independently on your local machine.
|
||||
|
||||
|
||||
## Browser support
|
||||
@@ -100,6 +100,7 @@ CyberChef is released under the [Apache 2.0 Licence](https://www.apache.org/lice
|
||||
[5]: https://gchq.github.io/CyberChef/#recipe=From_Hexdump()Gunzip()&input=MDAwMDAwMDAgIDFmIDhiIDA4IDAwIDEyIGJjIGYzIDU3IDAwIGZmIDBkIGM3IGMxIDA5IDAwIDIwICB8Li4uLi6881cu/y7HwS4uIHwKMDAwMDAwMTAgIDA4IDA1IGQwIDU1IGZlIDA0IDJkIGQzIDA0IDFmIGNhIDhjIDQ0IDIxIDViIGZmICB8Li7QVf4uLdMuLsouRCFb/3wKMDAwMDAwMjAgIDYwIGM3IGQ3IDAzIDE2IGJlIDQwIDFmIDc4IDRhIDNmIDA5IDg5IDBiIDlhIDdkICB8YMfXLi6%2BQC54Sj8uLi4ufXwKMDAwMDAwMzAgIDRlIGM4IDRlIDZkIDA1IDFlIDAxIDhiIDRjIDI0IDAwIDAwIDAwICAgICAgICAgICB8TshObS4uLi5MJC4uLnw
|
||||
[6]: https://gchq.github.io/CyberChef/#recipe=RC4(%7B'option':'UTF8','string':'secret'%7D,'Hex','Hex')Disassemble_x86('64','Full%20x86%20architecture',16,0,true,true)&input=MjFkZGQyNTQwMTYwZWU2NWZlMDc3NzEwM2YyYTM5ZmJlNWJjYjZhYTBhYWJkNDE0ZjkwYzZjYWY1MzEyNzU0YWY3NzRiNzZiM2JiY2QxOTNjYjNkZGZkYmM1YTI2NTMzYTY4NmI1OWI4ZmVkNGQzODBkNDc0NDIwMWFlYzIwNDA1MDcxMzhlMmZlMmIzOTUwNDQ2ZGIzMWQyYmM2MjliZTRkM2YyZWIwMDQzYzI5M2Q3YTVkMjk2MmMwMGZlNmRhMzAwNzJkOGM1YTZiNGZlN2Q4NTlhMDQwZWVhZjI5OTczMzYzMDJmNWEwZWMxOQ
|
||||
[7]: https://gchq.github.io/CyberChef/#recipe=Fork('%5C%5Cn','%5C%5Cn',false)From_UNIX_Timestamp('Seconds%20(s)')&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA
|
||||
[8]: https://gchq.github.ioeCyberChef/#recipe=Fork('%5C%5Cn','%5C%5Cn',false)Conditional_Jump('1',false,'base64',10)To_Hex('Space')Return()Label('base64')To_Base64('A-Za-z0-9%2B/%3D')&input=U29tZSBkYXRhIHdpdGggYSAxIGluIGl0ClNvbWUgZGF0YSB3aXRoIGEgMiBpbiBpdA
|
||||
[8]: https://gchq.github.io/CyberChef/#recipe=Fork('%5C%5Cn','%5C%5Cn',false)Conditional_Jump('1',false,'base64',10)To_Hex('Space')Return()Label('base64')To_Base64('A-Za-z0-9%2B/%3D')&input=U29tZSBkYXRhIHdpdGggYSAxIGluIGl0ClNvbWUgZGF0YSB3aXRoIGEgMiBpbiBpdA
|
||||
[9]: https://gchq.github.io/CyberChef/#recipe=Register('key%3D(%5B%5C%5Cda-f%5D*)',true,false)Find_/_Replace(%7B'option':'Regex','string':'.*data%3D(.*)'%7D,'$1',true,false,true)RC4(%7B'option':'Hex','string':'$R0'%7D,'Hex','Latin1')&input=aHR0cDovL21hbHdhcmV6LmJpei9iZWFjb24ucGhwP2tleT0wZTkzMmE1YyZkYXRhPThkYjdkNWViZTM4NjYzYTU0ZWNiYjMzNGUzZGIxMQ
|
||||
[10]: https://gchq.github.io/CyberChef/#recipe=XOR(%7B'option':'Hex','string':'3a'%7D,'',false)To_Hexdump(16,false,false)&input=VGhlIGFuc3dlciB0byB0aGUgdWx0aW1hdGUgcXVlc3Rpb24gb2YgbGlmZSwgdGhlIFVuaXZlcnNlLCBhbmQgZXZlcnl0aGluZyBpcyA0Mi4
|
||||
[10]: https://gchq.github.io/CyberChef/#recipe=Register('(.%7B32%7D)',true,false)Drop_bytes(0,32,false)AES_Decrypt(%7B'option':'Hex','string':'1748e7179bd56570d51fa4ba287cc3e5'%7D,%7B'option':'Hex','string':'$R0'%7D,'CTR','Hex','Raw',%7B'option':'Hex','string':''%7D)&input=NTFlMjAxZDQ2MzY5OGVmNWY3MTdmNzFmNWI0NzEyYWYyMGJlNjc0YjNiZmY1M2QzODU0NjM5NmVlNjFkYWFjNDkwOGUzMTljYTNmY2Y3MDg5YmZiNmIzOGVhOTllNzgxZDI2ZTU3N2JhOWRkNmYzMTFhMzk0MjBiODk3OGU5MzAxNGIwNDJkNDQ3MjZjYWVkZjU0MzZlYWY2NTI0MjljMGRmOTRiNTIxNjc2YzdjMmNlODEyMDk3YzI3NzI3M2M3YzcyY2Q4OWFlYzhkOWZiNGEyNzU4NmNjZjZhYTBhZWUyMjRjMzRiYTNiZmRmN2FlYjFkZGQ0Nzc2MjJiOTFlNzJjOWU3MDlhYjYwZjhkYWY3MzFlYzBjYzg1Y2UwZjc0NmZmMTU1NGE1YTNlYzI5MWNhNDBmOWU2MjlhODcyNTkyZDk4OGZkZDgzNDUzNGFiYTc5YzFhZDE2NzY3NjlhN2MwMTBiZjA0NzM5ZWNkYjY1ZDk1MzAyMzcxZDYyOWQ5ZTM3ZTdiNGEzNjFkYTQ2OGYxZWQ1MzU4OTIyZDJlYTc1MmRkMTFjMzY2ZjMwMTdiMTRhYTAxMWQyYWYwM2M0NGY5NTU3OTA5OGExNWUzY2Y5YjQ0ODZmOGZmZTljMjM5ZjM0ZGU3MTUxZjZjYTY1MDBmZTRiODUwYzNmMWMwMmU4MDFjYWYzYTI0NDY0NjE0ZTQyODAxNjE1YjhmZmFhMDdhYzgyNTE0OTNmZmRhN2RlNWRkZjMzNjg4ODBjMmI5NWIwMzBmNDFmOGYxNTA2NmFkZDA3MWE2NmNmNjBlNWY0NmYzYTIzMGQzOTdiNjUyOTYzYTIxYTUzZg
|
||||
[11]: https://gchq.github.io/CyberChef/#recipe=XOR(%7B'option':'Hex','string':'3a'%7D,'Standard',false)To_Hexdump(16,false,false)&input=VGhlIGFuc3dlciB0byB0aGUgdWx0aW1hdGUgcXVlc3Rpb24gb2YgbGlmZSwgdGhlIFVuaXZlcnNlLCBhbmQgZXZlcnl0aGluZyBpcyA0Mi4
|
||||
|
||||
6575
package-lock.json
generated
6575
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
94
package.json
94
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "6.6.2",
|
||||
"version": "7.11.1",
|
||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||
"author": "n1474335 <n1474335@gmail.com>",
|
||||
"homepage": "https://gchq.github.io/CyberChef",
|
||||
@@ -30,15 +30,17 @@
|
||||
"main": "build/node/CyberChef.js",
|
||||
"bugs": "https://github.com/gchq/CyberChef/issues",
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"css-loader": "^0.28.7",
|
||||
"exports-loader": "^0.6.4",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"file-loader": "^1.1.5",
|
||||
"grunt": ">=1.0.1",
|
||||
"grunt-accessibility": "~5.0.0",
|
||||
"css-loader": "^0.28.11",
|
||||
"eslint": "^4.19.1",
|
||||
"exports-loader": "^0.7.0",
|
||||
"extract-text-webpack-plugin": "^4.0.0-alpha0",
|
||||
"file-loader": "^1.1.11",
|
||||
"grunt": ">=1.0.2",
|
||||
"grunt-accessibility": "~6.0.0",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
"grunt-concurrent": "^2.3.1",
|
||||
"grunt-contrib-clean": "~1.1.0",
|
||||
@@ -46,56 +48,74 @@
|
||||
"grunt-eslint": "^20.1.0",
|
||||
"grunt-exec": "~3.0.0",
|
||||
"grunt-execute": "^0.2.2",
|
||||
"grunt-jsdoc": "^2.2.0",
|
||||
"grunt-webpack": "^3.0.2",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"imports-loader": "^0.7.1",
|
||||
"grunt-jsdoc": "^2.2.1",
|
||||
"grunt-webpack": "^3.1.1",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"imports-loader": "^0.8.0",
|
||||
"ink-docstrap": "^1.3.2",
|
||||
"jsdoc-babel": "^0.3.0",
|
||||
"less": "^2.7.3",
|
||||
"less-loader": "^4.0.5",
|
||||
"postcss-css-variables": "^0.8.0",
|
||||
"postcss-import": "^11.0.0",
|
||||
"postcss-loader": "^2.0.8",
|
||||
"style-loader": "^0.19.0",
|
||||
"url-loader": "^0.6.2",
|
||||
"jsdoc-babel": "^0.4.0",
|
||||
"less": "^3.0.2",
|
||||
"less-loader": "^4.1.0",
|
||||
"postcss-css-variables": "^0.8.1",
|
||||
"postcss-import": "^11.1.0",
|
||||
"postcss-loader": "^2.1.4",
|
||||
"sitemap": "^1.13.0",
|
||||
"style-loader": "^0.21.0",
|
||||
"url-loader": "^1.0.1",
|
||||
"val-loader": "^1.1.0",
|
||||
"web-resource-inliner": "^4.2.0",
|
||||
"webpack": "^3.8.1",
|
||||
"webpack-dev-server": "^2.9.4",
|
||||
"webpack-node-externals": "^1.6.0",
|
||||
"worker-loader": "^1.1.0"
|
||||
"web-resource-inliner": "^4.2.1",
|
||||
"webpack": "^4.6.0",
|
||||
"webpack-dev-server": "^3.1.3",
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"worker-loader": "^1.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bignumber.js": "^7.0.1",
|
||||
"bootstrap": "^3.3.7",
|
||||
"bootstrap-colorpicker": "^2.5.2",
|
||||
"bootstrap-switch": "^3.3.4",
|
||||
"crypto-api": "^0.7.5",
|
||||
"bson": "^2.0.6",
|
||||
"crypto-api": "^0.8.0",
|
||||
"crypto-js": "^3.1.9-1",
|
||||
"diff": "^3.4.0",
|
||||
"escodegen": "^1.9.0",
|
||||
"ctph.js": "0.0.5",
|
||||
"diff": "^3.5.0",
|
||||
"escodegen": "^1.9.1",
|
||||
"es6-promisify": "^6.0.0",
|
||||
"esmangle": "^1.0.1",
|
||||
"esprima": "^4.0.0",
|
||||
"exif-parser": "^0.1.12",
|
||||
"google-code-prettify": "^1.0.5",
|
||||
"jquery": "^3.2.1",
|
||||
"file-saver": "^1.3.8",
|
||||
"highlight.js": "^9.12.0",
|
||||
"jquery": "^3.3.1",
|
||||
"js-crc": "^0.2.0",
|
||||
"js-sha3": "^0.6.1",
|
||||
"js-sha3": "^0.7.0",
|
||||
"jsbn": "^1.1.0",
|
||||
"jsesc": "^2.5.1",
|
||||
"jsonpath": "^1.0.0",
|
||||
"jsrsasign": "8.0.4",
|
||||
"lodash": "^4.17.4",
|
||||
"moment": "^2.19.2",
|
||||
"moment-timezone": "^0.5.14",
|
||||
"jsrsasign": "8.0.12",
|
||||
"lodash": "^4.17.10",
|
||||
"loglevel": "^1.6.1",
|
||||
"kbpgp": "^2.0.77",
|
||||
"loglevel-message-prefix": "^3.0.0",
|
||||
"moment": "^2.22.1",
|
||||
"moment-timezone": "^0.5.16",
|
||||
"node-forge": "^0.7.5",
|
||||
"node-md6": "^0.1.0",
|
||||
"nwmatcher": "^1.4.4",
|
||||
"otp": "^0.1.3",
|
||||
"scryptsy": "^2.0.0",
|
||||
"sladex-blowfish": "^0.8.1",
|
||||
"sortablejs": "^1.7.0",
|
||||
"split.js": "^1.3.5",
|
||||
"ssdeep.js": "0.0.2",
|
||||
"ua-parser-js": "^0.7.17",
|
||||
"utf8": "^3.0.0",
|
||||
"vkbeautify": "^0.99.3",
|
||||
"xmldom": "^0.1.27",
|
||||
"xpath": "0.0.24",
|
||||
"xpath": "0.0.27",
|
||||
"xregexp": "^4.1.1",
|
||||
"zlibjs": "^0.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -19,7 +19,7 @@ const Chef = function() {
|
||||
/**
|
||||
* Runs the recipe over the input.
|
||||
*
|
||||
* @param {string} inputText - The input data as a string
|
||||
* @param {string|ArrayBuffer} input - The input data as a string or ArrayBuffer
|
||||
* @param {Object[]} recipeConfig - The recipe configuration object
|
||||
* @param {Object} options - The options object storing various user choices
|
||||
* @param {boolean} options.attempHighlight - Whether or not to attempt highlighting
|
||||
@@ -33,11 +33,13 @@ const Chef = function() {
|
||||
* @returns {number} response.duration - The number of ms it took to execute the recipe
|
||||
* @returns {number} response.error - The error object thrown by a failed operation (false if no error)
|
||||
*/
|
||||
Chef.prototype.bake = async function(inputText, recipeConfig, options, progress, step) {
|
||||
let startTime = new Date().getTime(),
|
||||
Chef.prototype.bake = async function(input, recipeConfig, options, progress, step) {
|
||||
log.debug("Chef baking");
|
||||
const startTime = new Date().getTime(),
|
||||
recipe = new Recipe(recipeConfig),
|
||||
containsFc = recipe.containsFlowControl(),
|
||||
error = false;
|
||||
notUTF8 = options && options.hasOwnProperty("treatAsUtf8") && !options.treatAsUtf8;
|
||||
let error = false;
|
||||
|
||||
if (containsFc && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
|
||||
|
||||
@@ -62,23 +64,30 @@ Chef.prototype.bake = async function(inputText, recipeConfig, options, progress,
|
||||
|
||||
// If starting from scratch, load data
|
||||
if (progress === 0) {
|
||||
this.dish.set(inputText, Dish.STRING);
|
||||
const type = input instanceof ArrayBuffer ? Dish.ARRAY_BUFFER : Dish.STRING;
|
||||
this.dish.set(input, type);
|
||||
}
|
||||
|
||||
try {
|
||||
progress = await recipe.execute(this.dish, progress);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
log.error(err);
|
||||
error = {
|
||||
displayStr: err.displayStr,
|
||||
};
|
||||
progress = err.progress;
|
||||
}
|
||||
|
||||
// Depending on the size of the output, we may send it back as a string or an ArrayBuffer.
|
||||
// This can prevent unnecessary casting as an ArrayBuffer can be easily downloaded as a file.
|
||||
// The threshold is specified in KiB.
|
||||
const threshold = (options.ioDisplayThreshold || 1024) * 1024;
|
||||
const returnType = this.dish.size() > threshold ? Dish.ARRAY_BUFFER : Dish.STRING;
|
||||
|
||||
return {
|
||||
result: this.dish.type === Dish.HTML ?
|
||||
this.dish.get(Dish.HTML) :
|
||||
this.dish.get(Dish.STRING),
|
||||
this.dish.get(Dish.HTML, notUTF8) :
|
||||
this.dish.get(returnType, notUTF8),
|
||||
type: Dish.enumLookup(this.dish.type),
|
||||
progress: progress,
|
||||
duration: new Date().getTime() - startTime,
|
||||
@@ -105,6 +114,8 @@ Chef.prototype.bake = async function(inputText, recipeConfig, options, progress,
|
||||
* @returns {number} The time it took to run the silent bake in milliseconds.
|
||||
*/
|
||||
Chef.prototype.silentBake = function(recipeConfig) {
|
||||
log.debug("Running silent bake");
|
||||
|
||||
let startTime = new Date().getTime(),
|
||||
recipe = new Recipe(recipeConfig),
|
||||
dish = new Dish("", Dish.STRING);
|
||||
|
||||
@@ -11,6 +11,15 @@ import Chef from "./Chef.js";
|
||||
import OperationConfig from "./config/MetaConfig.js";
|
||||
import OpModules from "./config/modules/Default.js";
|
||||
|
||||
// Add ">" to the start of all log messages in the Chef Worker
|
||||
import loglevelMessagePrefix from "loglevel-message-prefix";
|
||||
|
||||
loglevelMessagePrefix(log, {
|
||||
prefixes: [],
|
||||
staticPrefixes: [">"],
|
||||
prefixFormat: "%p"
|
||||
});
|
||||
|
||||
|
||||
// Set up Chef instance
|
||||
self.chef = new Chef();
|
||||
@@ -42,6 +51,8 @@ self.postMessage({
|
||||
self.addEventListener("message", function(e) {
|
||||
// Handle message
|
||||
const r = e.data;
|
||||
log.debug("ChefWorker receiving command '" + r.action + "'");
|
||||
|
||||
switch (r.action) {
|
||||
case "bake":
|
||||
bake(r.data);
|
||||
@@ -61,6 +72,9 @@ self.addEventListener("message", function(e) {
|
||||
r.data.pos
|
||||
);
|
||||
break;
|
||||
case "setLogLevel":
|
||||
log.setLevel(r.data, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -86,7 +100,7 @@ async function bake(data) {
|
||||
);
|
||||
|
||||
self.postMessage({
|
||||
action: "bakeSuccess",
|
||||
action: "bakeComplete",
|
||||
data: response
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -121,9 +135,10 @@ function loadRequiredModules(recipeConfig) {
|
||||
let module = self.OperationConfig[op.op].module;
|
||||
|
||||
if (!OpModules.hasOwnProperty(module)) {
|
||||
console.log("Loading module " + module);
|
||||
log.info("Loading module " + module);
|
||||
self.sendStatusMessage("Loading module " + module);
|
||||
self.importScripts(self.docURL + "/" + module + ".js");
|
||||
self.sendStatusMessage("");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
130
src/core/Dish.js
130
src/core/Dish.js
@@ -1,19 +1,21 @@
|
||||
import Utils from "./Utils.js";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
/**
|
||||
* The data being operated on by each operation.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @author Matt C [matt@artemisbot.uk]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @class
|
||||
* @param {byteArray|string|number} value - The value of the input data.
|
||||
* @param {byteArray|string|number|ArrayBuffer|BigNumber} value - The value of the input data.
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
*/
|
||||
const Dish = function(value, type) {
|
||||
this.value = value || typeof value == "string" ? value : null;
|
||||
this.type = type || Dish.BYTE_ARRAY;
|
||||
this.value = value || typeof value === "string" ? value : null;
|
||||
this.type = type || Dish.BYTE_ARRAY;
|
||||
};
|
||||
|
||||
|
||||
@@ -41,6 +43,18 @@ Dish.NUMBER = 2;
|
||||
* @enum
|
||||
*/
|
||||
Dish.HTML = 3;
|
||||
/**
|
||||
* Dish data type enum for ArrayBuffers.
|
||||
* @readonly
|
||||
* @enum
|
||||
*/
|
||||
Dish.ARRAY_BUFFER = 4;
|
||||
/**
|
||||
* Dish data type enum for BigNumbers.
|
||||
* @readonly
|
||||
* @enum
|
||||
*/
|
||||
Dish.BIG_NUMBER = 5;
|
||||
|
||||
|
||||
/**
|
||||
@@ -51,19 +65,22 @@ Dish.HTML = 3;
|
||||
* @returns {number} The data type enum value.
|
||||
*/
|
||||
Dish.typeEnum = function(typeStr) {
|
||||
switch (typeStr) {
|
||||
case "byteArray":
|
||||
case "Byte array":
|
||||
switch (typeStr.toLowerCase()) {
|
||||
case "bytearray":
|
||||
case "byte array":
|
||||
return Dish.BYTE_ARRAY;
|
||||
case "string":
|
||||
case "String":
|
||||
return Dish.STRING;
|
||||
case "number":
|
||||
case "Number":
|
||||
return Dish.NUMBER;
|
||||
case "html":
|
||||
case "HTML":
|
||||
return Dish.HTML;
|
||||
case "arraybuffer":
|
||||
case "array buffer":
|
||||
return Dish.ARRAY_BUFFER;
|
||||
case "bignumber":
|
||||
case "big number":
|
||||
return Dish.BIG_NUMBER;
|
||||
default:
|
||||
throw "Invalid data type string. No matching enum.";
|
||||
}
|
||||
@@ -74,8 +91,8 @@ Dish.typeEnum = function(typeStr) {
|
||||
* Returns the data type string for the given type enum.
|
||||
*
|
||||
* @static
|
||||
* @param {string} typeEnum - The enum value of the data type.
|
||||
* @returns {number} The data type as a string.
|
||||
* @param {number} typeEnum - The enum value of the data type.
|
||||
* @returns {string} The data type as a string.
|
||||
*/
|
||||
Dish.enumLookup = function(typeEnum) {
|
||||
switch (typeEnum) {
|
||||
@@ -87,6 +104,10 @@ Dish.enumLookup = function(typeEnum) {
|
||||
return "number";
|
||||
case Dish.HTML:
|
||||
return "html";
|
||||
case Dish.ARRAY_BUFFER:
|
||||
return "ArrayBuffer";
|
||||
case Dish.BIG_NUMBER:
|
||||
return "BigNumber";
|
||||
default:
|
||||
throw "Invalid data type enum. No matching type.";
|
||||
}
|
||||
@@ -96,12 +117,13 @@ Dish.enumLookup = function(typeEnum) {
|
||||
/**
|
||||
* Sets the data value and type and then validates them.
|
||||
*
|
||||
* @param {byteArray|string|number} value - The value of the input data.
|
||||
* @param {byteArray|string|number|ArrayBuffer|BigNumber} value - The value of the input data.
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
*/
|
||||
Dish.prototype.set = function(value, type) {
|
||||
log.debug("Dish type: " + Dish.enumLookup(type));
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
this.type = type;
|
||||
|
||||
if (!this.valid()) {
|
||||
const sample = Utils.truncate(JSON.stringify(this.value), 13);
|
||||
@@ -114,11 +136,12 @@ Dish.prototype.set = function(value, type) {
|
||||
* Returns the value of the data in the type format specified.
|
||||
*
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
* @returns {byteArray|string|number} The value of the output data.
|
||||
* @param {boolean} [notUTF8] - Do not treat strings as UTF8.
|
||||
* @returns {byteArray|string|number|ArrayBuffer|BigNumber} The value of the output data.
|
||||
*/
|
||||
Dish.prototype.get = function(type) {
|
||||
Dish.prototype.get = function(type, notUTF8) {
|
||||
if (this.type !== type) {
|
||||
this.translate(type);
|
||||
this.translate(type, notUTF8);
|
||||
}
|
||||
return this.value;
|
||||
};
|
||||
@@ -128,37 +151,59 @@ Dish.prototype.get = function(type) {
|
||||
* Translates the data to the given type format.
|
||||
*
|
||||
* @param {number} toType - The data type of value, see Dish enums.
|
||||
* @param {boolean} [notUTF8] - Do not treat strings as UTF8.
|
||||
*/
|
||||
Dish.prototype.translate = function(toType) {
|
||||
Dish.prototype.translate = function(toType, notUTF8) {
|
||||
log.debug(`Translating Dish from ${Dish.enumLookup(this.type)} to ${Dish.enumLookup(toType)}`);
|
||||
const byteArrayToStr = notUTF8 ? Utils.byteArrayToChars : Utils.byteArrayToUtf8;
|
||||
|
||||
// Convert data to intermediate byteArray type
|
||||
switch (this.type) {
|
||||
case Dish.STRING:
|
||||
this.value = this.value ? Utils.strToByteArray(this.value) : [];
|
||||
this.type = Dish.BYTE_ARRAY;
|
||||
break;
|
||||
case Dish.NUMBER:
|
||||
this.value = typeof this.value == "number" ? Utils.strToByteArray(this.value.toString()) : [];
|
||||
this.type = Dish.BYTE_ARRAY;
|
||||
break;
|
||||
case Dish.HTML:
|
||||
this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : [];
|
||||
this.type = Dish.BYTE_ARRAY;
|
||||
break;
|
||||
case Dish.ARRAY_BUFFER:
|
||||
// Array.from() would be nicer here, but it's slightly slower
|
||||
this.value = Array.prototype.slice.call(new Uint8Array(this.value));
|
||||
break;
|
||||
case Dish.BIG_NUMBER:
|
||||
this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toFixed()) : [];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.type = Dish.BYTE_ARRAY;
|
||||
|
||||
// Convert from byteArray to toType
|
||||
switch (toType) {
|
||||
case Dish.STRING:
|
||||
case Dish.HTML:
|
||||
this.value = this.value ? Utils.byteArrayToUtf8(this.value) : "";
|
||||
this.value = this.value ? byteArrayToStr(this.value) : "";
|
||||
this.type = Dish.STRING;
|
||||
break;
|
||||
case Dish.NUMBER:
|
||||
this.value = this.value ? parseFloat(Utils.byteArrayToUtf8(this.value)) : 0;
|
||||
this.value = this.value ? parseFloat(byteArrayToStr(this.value)) : 0;
|
||||
this.type = Dish.NUMBER;
|
||||
break;
|
||||
case Dish.ARRAY_BUFFER:
|
||||
this.value = new Uint8Array(this.value).buffer;
|
||||
this.type = Dish.ARRAY_BUFFER;
|
||||
break;
|
||||
case Dish.BIG_NUMBER:
|
||||
try {
|
||||
this.value = new BigNumber(byteArrayToStr(this.value));
|
||||
} catch (err) {
|
||||
this.value = new BigNumber(NaN);
|
||||
}
|
||||
this.type = Dish.BIG_NUMBER;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -180,7 +225,7 @@ Dish.prototype.valid = function() {
|
||||
|
||||
// Check that every value is a number between 0 - 255
|
||||
for (let i = 0; i < this.value.length; i++) {
|
||||
if (typeof this.value[i] != "number" ||
|
||||
if (typeof this.value[i] !== "number" ||
|
||||
this.value[i] < 0 ||
|
||||
this.value[i] > 255) {
|
||||
return false;
|
||||
@@ -189,18 +234,41 @@ Dish.prototype.valid = function() {
|
||||
return true;
|
||||
case Dish.STRING:
|
||||
case Dish.HTML:
|
||||
if (typeof this.value == "string") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return typeof this.value === "string";
|
||||
case Dish.NUMBER:
|
||||
if (typeof this.value == "number") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return typeof this.value === "number";
|
||||
case Dish.ARRAY_BUFFER:
|
||||
return this.value instanceof ArrayBuffer;
|
||||
case Dish.BIG_NUMBER:
|
||||
return this.value instanceof BigNumber;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Determines how much space the Dish takes up.
|
||||
* Numbers in JavaScript are 64-bit floating point, however for the purposes of the Dish,
|
||||
* we measure how many bytes are taken up when the number is written as a string.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
Dish.prototype.size = function() {
|
||||
switch (this.type) {
|
||||
case Dish.BYTE_ARRAY:
|
||||
case Dish.STRING:
|
||||
case Dish.HTML:
|
||||
return this.value.length;
|
||||
case Dish.NUMBER:
|
||||
case Dish.BIG_NUMBER:
|
||||
return this.value.toString().length;
|
||||
case Dish.ARRAY_BUFFER:
|
||||
return this.value.byteLength;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export default Dish;
|
||||
|
||||
@@ -52,13 +52,25 @@ const FlowControl = {
|
||||
output = "",
|
||||
progress = 0;
|
||||
|
||||
state.forkOffset += state.progress + 1;
|
||||
|
||||
recipe.addOperations(subOpList);
|
||||
|
||||
// Take a deep(ish) copy of the ingredient values
|
||||
const ingValues = subOpList.map(op => JSON.parse(JSON.stringify(op.getIngValues())));
|
||||
|
||||
// Run recipe over each tranche
|
||||
for (i = 0; i < inputs.length; i++) {
|
||||
log.debug(`Entering tranche ${i + 1} of ${inputs.length}`);
|
||||
|
||||
// Baseline ing values for each tranche so that registers are reset
|
||||
subOpList.forEach((op, i) => {
|
||||
op.setIngValues(JSON.parse(JSON.stringify(ingValues[i])));
|
||||
});
|
||||
|
||||
const dish = new Dish(inputs[i], inputType);
|
||||
try {
|
||||
progress = await recipe.execute(dish, 0);
|
||||
progress = await recipe.execute(dish, 0, state);
|
||||
} catch (err) {
|
||||
if (!ignoreErrors) {
|
||||
throw err;
|
||||
@@ -116,7 +128,7 @@ const FlowControl = {
|
||||
if (!registers) return state;
|
||||
|
||||
if (ENVIRONMENT_IS_WORKER()) {
|
||||
self.setRegisters(state.progress, state.numRegisters, registers.slice(1));
|
||||
self.setRegisters(state.forkOffset + state.progress, state.numRegisters, registers.slice(1));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,16 +181,19 @@ const FlowControl = {
|
||||
* @returns {Object} The updated state of the recipe.
|
||||
*/
|
||||
runJump: function(state) {
|
||||
let ings = state.opList[state.progress].getIngValues(),
|
||||
jmpIndex = FlowControl._getLabelIndex(ings[0], state),
|
||||
maxJumps = ings[1];
|
||||
const ings = state.opList[state.progress].getIngValues(),
|
||||
label = ings[0],
|
||||
maxJumps = ings[1],
|
||||
jmpIndex = FlowControl._getLabelIndex(label, state);
|
||||
|
||||
if (state.numJumps >= maxJumps || jmpIndex === -1) {
|
||||
log.debug("Maximum jumps reached or label cannot be found");
|
||||
return state;
|
||||
}
|
||||
|
||||
state.progress = jmpIndex;
|
||||
state.numJumps++;
|
||||
log.debug(`Jumping to label '${label}' at position ${jmpIndex} (jumps = ${state.numJumps})`);
|
||||
return state;
|
||||
},
|
||||
|
||||
@@ -194,14 +209,16 @@ const FlowControl = {
|
||||
* @returns {Object} The updated state of the recipe.
|
||||
*/
|
||||
runCondJump: function(state) {
|
||||
let ings = state.opList[state.progress].getIngValues(),
|
||||
const ings = state.opList[state.progress].getIngValues(),
|
||||
dish = state.dish,
|
||||
regexStr = ings[0],
|
||||
invert = ings[1],
|
||||
jmpIndex = FlowControl._getLabelIndex(ings[2], state),
|
||||
maxJumps = ings[3];
|
||||
label = ings[2],
|
||||
maxJumps = ings[3],
|
||||
jmpIndex = FlowControl._getLabelIndex(label, state);
|
||||
|
||||
if (state.numJumps >= maxJumps || jmpIndex === -1) {
|
||||
log.debug("Maximum jumps reached or label cannot be found");
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -210,6 +227,7 @@ const FlowControl = {
|
||||
if (!invert && strMatch || invert && !strMatch) {
|
||||
state.progress = jmpIndex;
|
||||
state.numJumps++;
|
||||
log.debug(`Jumping to label '${label}' at position ${jmpIndex} (jumps = ${state.numJumps})`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,6 +267,7 @@ const FlowControl = {
|
||||
/**
|
||||
* Returns the index of a label.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} state
|
||||
* @param {string} name
|
||||
* @returns {number}
|
||||
|
||||
@@ -141,23 +141,31 @@ Recipe.prototype.lastOpIndex = function(startIndex) {
|
||||
*
|
||||
* @param {Dish} dish
|
||||
* @param {number} [startFrom=0] - The index of the Operation to start executing from
|
||||
* @param {number} [forkState={}] - If this is a forked recipe, the state of the recipe up to this point
|
||||
* @returns {number} - The final progress through the recipe
|
||||
*/
|
||||
Recipe.prototype.execute = async function(dish, startFrom) {
|
||||
startFrom = startFrom || 0;
|
||||
let op, input, output, numJumps = 0, numRegisters = 0;
|
||||
Recipe.prototype.execute = async function(dish, startFrom = 0, forkState = {}) {
|
||||
let op, input, output,
|
||||
numJumps = 0,
|
||||
numRegisters = forkState.numRegisters || 0;
|
||||
|
||||
log.debug(`[*] Executing recipe of ${this.opList.length} operations, starting at ${startFrom}`);
|
||||
|
||||
for (let i = startFrom; i < this.opList.length; i++) {
|
||||
op = this.opList[i];
|
||||
log.debug(`[${i}] ${op.name} ${JSON.stringify(op.getIngValues())}`);
|
||||
if (op.isDisabled()) {
|
||||
log.debug("Operation is disabled, skipping");
|
||||
continue;
|
||||
}
|
||||
if (op.isBreakpoint()) {
|
||||
log.debug("Pausing at breakpoint");
|
||||
return i;
|
||||
}
|
||||
|
||||
try {
|
||||
input = dish.get(op.inputType);
|
||||
log.debug("Executing operation");
|
||||
|
||||
if (op.isFlowControl()) {
|
||||
// Package up the current state
|
||||
@@ -166,7 +174,8 @@ Recipe.prototype.execute = async function(dish, startFrom) {
|
||||
"dish": dish,
|
||||
"opList": this.opList,
|
||||
"numJumps": numJumps,
|
||||
"numRegisters": numRegisters
|
||||
"numRegisters": numRegisters,
|
||||
"forkOffset": forkState.forkOffset || 0
|
||||
};
|
||||
|
||||
state = await op.run(state);
|
||||
@@ -193,6 +202,7 @@ Recipe.prototype.execute = async function(dish, startFrom) {
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Recipe complete");
|
||||
return this.opList.length;
|
||||
};
|
||||
|
||||
@@ -250,4 +260,5 @@ Recipe.prototype.generateHighlightList = function() {
|
||||
return highlights;
|
||||
};
|
||||
|
||||
|
||||
export default Recipe;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import CryptoJS from "crypto-js";
|
||||
import utf8 from "utf8";
|
||||
import moment from "moment-timezone";
|
||||
|
||||
|
||||
/**
|
||||
@@ -64,58 +65,6 @@ const Utils = {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Adds leading zeros to strings
|
||||
*
|
||||
* @param {string} str - String to add leading characters to.
|
||||
* @param {number} max - Maximum width of the string.
|
||||
* @param {char} [chr='0'] - The character to pad with.
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
* // returns "0a"
|
||||
* Utils.padLeft("a", 2);
|
||||
*
|
||||
* // returns "000a"
|
||||
* Utils.padLeft("a", 4);
|
||||
*
|
||||
* // returns "xxxa"
|
||||
* Utils.padLeft("a", 4, "x");
|
||||
*
|
||||
* // returns "bcabchello"
|
||||
* Utils.padLeft("hello", 10, "abc");
|
||||
*/
|
||||
padLeft: function(str, max, chr) {
|
||||
chr = chr || "0";
|
||||
let startIndex = chr.length - (max - str.length);
|
||||
startIndex = startIndex < 0 ? 0 : startIndex;
|
||||
return str.length < max ?
|
||||
Utils.padLeft(chr.slice(startIndex, chr.length) + str, max, chr) : str;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Adds trailing spaces to strings.
|
||||
*
|
||||
* @param {string} str - String to add trailing characters to.
|
||||
* @param {number} max - Maximum width of the string.
|
||||
* @param {char} [chr='0'] - The character to pad with.
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
* // returns "a "
|
||||
* Utils.padRight("a", 4);
|
||||
*
|
||||
* // returns "axxx"
|
||||
* Utils.padRight("a", 4, "x");
|
||||
*/
|
||||
padRight: function(str, max, chr) {
|
||||
chr = chr || " ";
|
||||
return str.length < max ?
|
||||
Utils.padRight(str + chr.slice(0, max-str.length), max, chr) : str;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Adds trailing bytes to a byteArray.
|
||||
*
|
||||
@@ -152,14 +101,6 @@ const Utils = {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @alias Utils.padLeft
|
||||
*/
|
||||
pad: function(str, max, chr) {
|
||||
return Utils.padLeft(str, max, chr);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Truncates a long string to max length and adds suffix.
|
||||
*
|
||||
@@ -201,7 +142,7 @@ const Utils = {
|
||||
hex: function(c, length) {
|
||||
c = typeof c == "string" ? Utils.ord(c) : c;
|
||||
length = length || 2;
|
||||
return Utils.pad(c.toString(16), length);
|
||||
return c.toString(16).padStart(length, "0");
|
||||
},
|
||||
|
||||
|
||||
@@ -222,7 +163,7 @@ const Utils = {
|
||||
bin: function(c, length) {
|
||||
c = typeof c == "string" ? Utils.ord(c) : c;
|
||||
length = length || 8;
|
||||
return Utils.pad(c.toString(2), length);
|
||||
return c.toString(2).padStart(length, "0");
|
||||
},
|
||||
|
||||
|
||||
@@ -261,21 +202,34 @@ const Utils = {
|
||||
* Utils.parseEscapedChars("\\n");
|
||||
*/
|
||||
parseEscapedChars: function(str) {
|
||||
return str.replace(/(\\)?\\([nrtbf]|x[\da-f]{2})/g, function(m, a, b) {
|
||||
return str.replace(/(\\)?\\([bfnrtv0'"]|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\})/g, function(m, a, b) {
|
||||
if (a === "\\") return "\\"+b;
|
||||
switch (b[0]) {
|
||||
case "n":
|
||||
return "\n";
|
||||
case "r":
|
||||
return "\r";
|
||||
case "t":
|
||||
return "\t";
|
||||
case "0":
|
||||
return "\0";
|
||||
case "b":
|
||||
return "\b";
|
||||
case "t":
|
||||
return "\t";
|
||||
case "n":
|
||||
return "\n";
|
||||
case "v":
|
||||
return "\v";
|
||||
case "f":
|
||||
return "\f";
|
||||
case "r":
|
||||
return "\r";
|
||||
case '"':
|
||||
return '"';
|
||||
case "'":
|
||||
return "'";
|
||||
case "x":
|
||||
return Utils.chr(parseInt(b.substr(1), 16));
|
||||
return String.fromCharCode(parseInt(b.substr(1), 16));
|
||||
case "u":
|
||||
if (b[1] === "{")
|
||||
return String.fromCodePoint(parseInt(b.slice(2, -1), 16));
|
||||
else
|
||||
return String.fromCharCode(parseInt(b.substr(1), 16));
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -340,6 +294,72 @@ const Utils = {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Coverts data of varying types to a byteArray.
|
||||
* Accepts hex, Base64, UTF8 and Latin1 strings.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {string} type - One of "Hex", "Base64", "UTF8" or "Latin1"
|
||||
* @returns {byteArray}
|
||||
*
|
||||
* @example
|
||||
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
|
||||
* Utils.convertToByteArray("Привет", "utf8");
|
||||
*
|
||||
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
|
||||
* Utils.convertToByteArray("d097d0b4d180d0b0d0b2d181d182d0b2d183d0b9d182d0b5", "hex");
|
||||
*
|
||||
* // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
|
||||
* Utils.convertToByteArray("0JfQtNGA0LDQstGB0YLQstGD0LnRgtC1", "base64");
|
||||
*/
|
||||
convertToByteArray: function(str, type) {
|
||||
switch (type.toLowerCase()) {
|
||||
case "hex":
|
||||
return Utils.fromHex(str);
|
||||
case "base64":
|
||||
return Utils.fromBase64(str, null, "byteArray");
|
||||
case "utf8":
|
||||
return Utils.strToUtf8ByteArray(str);
|
||||
case "latin1":
|
||||
default:
|
||||
return Utils.strToByteArray(str);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Coverts data of varying types to a byte string.
|
||||
* Accepts hex, Base64, UTF8 and Latin1 strings.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {string} type - One of "Hex", "Base64", "UTF8" or "Latin1"
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
* // returns "ÐÑивеÑ"
|
||||
* Utils.convertToByteString("Привет", "utf8");
|
||||
*
|
||||
* // returns "ÐдÑавÑÑвÑйÑе"
|
||||
* Utils.convertToByteString("d097d0b4d180d0b0d0b2d181d182d0b2d183d0b9d182d0b5", "hex");
|
||||
*
|
||||
* // returns "ÐдÑавÑÑвÑйÑе"
|
||||
* Utils.convertToByteString("0JfQtNGA0LDQstGB0YLQstGD0LnRgtC1", "base64");
|
||||
*/
|
||||
convertToByteString: function(str, type) {
|
||||
switch (type.toLowerCase()) {
|
||||
case "hex":
|
||||
return Utils.byteArrayToChars(Utils.fromHex(str));
|
||||
case "base64":
|
||||
return Utils.byteArrayToChars(Utils.fromBase64(str, null, "byteArray"));
|
||||
case "utf8":
|
||||
return utf8.encode(str);
|
||||
case "latin1":
|
||||
default:
|
||||
return str;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Converts a string to a byte array.
|
||||
* Treats the string as UTF-8 if any values are over 255.
|
||||
@@ -381,17 +401,17 @@ const Utils = {
|
||||
* Utils.strToUtf8ByteArray("你好");
|
||||
*/
|
||||
strToUtf8ByteArray: function(str) {
|
||||
let wordArray = CryptoJS.enc.Utf8.parse(str),
|
||||
byteArray = Utils.wordArrayToByteArray(wordArray);
|
||||
const utf8Str = utf8.encode(str);
|
||||
|
||||
if (str.length !== wordArray.sigBytes) {
|
||||
if (str.length !== utf8Str.length) {
|
||||
if (ENVIRONMENT_IS_WORKER()) {
|
||||
self.setOption("attemptHighlight", false);
|
||||
} else if (ENVIRONMENT_IS_WEB()) {
|
||||
window.app.options.attemptHighlight = false;
|
||||
}
|
||||
}
|
||||
return byteArray;
|
||||
|
||||
return Utils.strToByteArray(utf8Str);
|
||||
},
|
||||
|
||||
|
||||
@@ -443,26 +463,21 @@ const Utils = {
|
||||
* Utils.byteArrayToUtf8([228,189,160,229,165,189]);
|
||||
*/
|
||||
byteArrayToUtf8: function(byteArray) {
|
||||
const str = Utils.byteArrayToChars(byteArray);
|
||||
try {
|
||||
// Try to output data as UTF-8 string
|
||||
const words = [];
|
||||
for (let i = 0; i < byteArray.length; i++) {
|
||||
words[i >>> 2] |= byteArray[i] << (24 - (i % 4) * 8);
|
||||
}
|
||||
let wordArray = new CryptoJS.lib.WordArray.init(words, byteArray.length),
|
||||
str = CryptoJS.enc.Utf8.stringify(wordArray);
|
||||
const utf8Str = utf8.decode(str);
|
||||
|
||||
if (str.length !== wordArray.sigBytes) {
|
||||
if (str.length !== utf8Str.length) {
|
||||
if (ENVIRONMENT_IS_WORKER()) {
|
||||
self.setOption("attemptHighlight", false);
|
||||
} else if (ENVIRONMENT_IS_WEB()) {
|
||||
window.app.options.attemptHighlight = false;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
return utf8Str;
|
||||
} catch (err) {
|
||||
// If it fails, treat it as ANSI
|
||||
return Utils.byteArrayToChars(byteArray);
|
||||
return str;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -470,7 +485,7 @@ const Utils = {
|
||||
/**
|
||||
* Converts a charcode array to a string.
|
||||
*
|
||||
* @param {byteArray} byteArray
|
||||
* @param {byteArray|Uint8Array} byteArray
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
@@ -491,33 +506,26 @@ const Utils = {
|
||||
|
||||
|
||||
/**
|
||||
* Converts a CryptoJS.lib.WordArray to a byteArray.
|
||||
* Converts an ArrayBuffer to a string.
|
||||
*
|
||||
* @param {CryptoJS.lib.WordArray} wordArray
|
||||
* @returns {byteArray}
|
||||
* @param {ArrayBuffer} arrayBuffer
|
||||
* @param {boolean} [utf8=true] - Whether to attempt to decode the buffer as UTF-8
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
* // returns [84, 101, 115, 116]
|
||||
* Utils.wordArrayToByteArray(CryptoJS.enc.Hex.parse("54657374"));
|
||||
* // returns "hello"
|
||||
* Utils.arrayBufferToStr(Uint8Array.from([104,101,108,108,111]).buffer);
|
||||
*/
|
||||
wordArrayToByteArray: function(wordArray) {
|
||||
if (wordArray.sigBytes <= 0) return [];
|
||||
|
||||
let words = wordArray.words,
|
||||
byteArray = [];
|
||||
|
||||
for (let i = 0; i < wordArray.sigBytes; i++) {
|
||||
byteArray.push((words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
|
||||
}
|
||||
|
||||
return byteArray;
|
||||
arrayBufferToStr: function(arrayBuffer, utf8=true) {
|
||||
const byteArray = Array.prototype.slice.call(new Uint8Array(arrayBuffer));
|
||||
return utf8 ? Utils.byteArrayToUtf8(byteArray) : Utils.byteArrayToChars(byteArray);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Base64's the input byte array using the given alphabet, returning a string.
|
||||
*
|
||||
* @param {byteArray|string} data
|
||||
* @param {byteArray|Uint8Array|string} data
|
||||
* @param {string} [alphabet]
|
||||
* @returns {string}
|
||||
*
|
||||
@@ -636,7 +644,7 @@ const Utils = {
|
||||
/**
|
||||
* Convert a byte array into a hex string.
|
||||
*
|
||||
* @param {byteArray} data
|
||||
* @param {Uint8Array|byteArray} data
|
||||
* @param {string} [delim=" "]
|
||||
* @param {number} [padding=2]
|
||||
* @returns {string}
|
||||
@@ -656,7 +664,7 @@ const Utils = {
|
||||
let output = "";
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
output += Utils.pad(data[i].toString(16), padding) + delim;
|
||||
output += data[i].toString(16).padStart(padding, "0") + delim;
|
||||
}
|
||||
|
||||
// Add \x or 0x to beginning
|
||||
@@ -710,10 +718,10 @@ const Utils = {
|
||||
* Utils.fromHex("0a:14:1e", "Colon");
|
||||
*/
|
||||
fromHex: function(data, delim, byteLen) {
|
||||
delim = delim || (data.indexOf(" ") >= 0 ? "Space" : "None");
|
||||
delim = delim || "Auto";
|
||||
byteLen = byteLen || 2;
|
||||
if (delim !== "None") {
|
||||
const delimRegex = Utils.regexRep[delim];
|
||||
const delimRegex = delim === "Auto" ? /[^a-f\d]/gi : Utils.regexRep[delim];
|
||||
data = data.replace(delimRegex, "");
|
||||
}
|
||||
|
||||
@@ -729,37 +737,43 @@ const Utils = {
|
||||
* Parses CSV data and returns it as a two dimensional array or strings.
|
||||
*
|
||||
* @param {string} data
|
||||
* @param {string[]} [cellDelims=[","]]
|
||||
* @param {string[]} [lineDelims=["\n", "\r"]]
|
||||
* @returns {string[][]}
|
||||
*
|
||||
* @example
|
||||
* // returns [["head1", "head2"], ["data1", "data2"]]
|
||||
* Utils.parseCSV("head1,head2\ndata1,data2");
|
||||
*/
|
||||
parseCSV: function(data) {
|
||||
|
||||
parseCSV: function(data, cellDelims=[","], lineDelims=["\n", "\r"]) {
|
||||
let b,
|
||||
ignoreNext = false,
|
||||
next,
|
||||
renderNext = false,
|
||||
inString = false,
|
||||
cell = "",
|
||||
line = [],
|
||||
lines = [];
|
||||
|
||||
// Remove BOM, often present in Excel CSV files
|
||||
if (data.length && data[0] === "\uFEFF") data = data.substr(1);
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
b = data[i];
|
||||
if (ignoreNext) {
|
||||
next = data[i+1] || "";
|
||||
if (renderNext) {
|
||||
cell += b;
|
||||
ignoreNext = false;
|
||||
renderNext = false;
|
||||
} else if (b === "\\") {
|
||||
cell += b;
|
||||
ignoreNext = true;
|
||||
renderNext = true;
|
||||
} else if (b === "\"" && !inString) {
|
||||
inString = true;
|
||||
} else if (b === "\"" && inString) {
|
||||
inString = false;
|
||||
} else if (b === "," && !inString) {
|
||||
if (next === "\"") renderNext = true;
|
||||
else inString = false;
|
||||
} else if (!inString && cellDelims.indexOf(b) >= 0) {
|
||||
line.push(cell);
|
||||
cell = "";
|
||||
} else if ((b === "\n" || b === "\r") && !inString) {
|
||||
} else if (!inString && lineDelims.indexOf(b) >= 0) {
|
||||
line.push(cell);
|
||||
cell = "";
|
||||
lines.push(line);
|
||||
@@ -859,7 +873,7 @@ const Utils = {
|
||||
*
|
||||
* fragment = *( pchar / "/" / "?" )
|
||||
* query = *( pchar / "/" / "?" )
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
@@ -910,9 +924,9 @@ const Utils = {
|
||||
* "Pretty" CyberChef recipe formats are designed to be included in the fragment (#) or query (?)
|
||||
* parts of the URL. They can also be loaded into CyberChef through the 'Load' interface. In order
|
||||
* to make this format as readable as possible, various special characters are used unescaped. This
|
||||
* reduces the amount of percent-encoding included in the URL which is typically difficult to read,
|
||||
* as well as substantially increasing the overall length. These characteristics can be quite
|
||||
* offputting for users.
|
||||
* reduces the amount of percent-encoding included in the URL which is typically difficult to read
|
||||
* and substantially increases the overall length. These characteristics can be quite off-putting
|
||||
* for users.
|
||||
*
|
||||
* @param {Object[]} recipeConfig
|
||||
* @param {boolean} newline - whether to add a newline after each operation
|
||||
@@ -929,12 +943,11 @@ const Utils = {
|
||||
name = op.op.replace(/ /g, "_");
|
||||
args = JSON.stringify(op.args)
|
||||
.slice(1, -1) // Remove [ and ] as they are implied
|
||||
// We now need to switch double-quoted (") strings to single-quotes (') as these do not
|
||||
// need to be percent-encoded.
|
||||
// We now need to switch double-quoted (") strings to single quotes (') as single quotes
|
||||
// do not need to be percent-encoded.
|
||||
.replace(/'/g, "\\'") // Escape single quotes
|
||||
.replace(/\\"/g, '"') // Unescape double quotes
|
||||
.replace(/(^|,|{|:)"/g, "$1'") // Replace opening " with '
|
||||
.replace(/"(,|:|}|$)/g, "'$1"); // Replace closing " with '
|
||||
.replace(/"((?:[^"\\]|\\.)*)"/g, "'$1'") // Replace opening and closing " with '
|
||||
.replace(/\\"/g, '"'); // Unescape double quotes
|
||||
|
||||
disabled = op.disabled ? "/disabled": "";
|
||||
bp = op.breakpoint ? "/breakpoint" : "";
|
||||
@@ -960,7 +973,7 @@ const Utils = {
|
||||
// Parse bespoke recipe format
|
||||
recipe = recipe.replace(/\n/g, "");
|
||||
let m,
|
||||
recipeRegex = /([^(]+)\(((?:'[^'\\]*(?:\\.[^'\\]*)*'|[^)/])*)(\/[^)]+)?\)/g,
|
||||
recipeRegex = /([^(]+)\(((?:'[^'\\]*(?:\\.[^'\\]*)*'|[^)/'])*)(\/[^)]+)?\)/g,
|
||||
recipeConfig = [],
|
||||
args;
|
||||
|
||||
@@ -1248,21 +1261,6 @@ const Utils = {
|
||||
"None": /\s+/g // Included here to remove whitespace when there shouldn't be any
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* A mapping of string formats to their classes in the CryptoJS library.
|
||||
* @constant
|
||||
*/
|
||||
format: {
|
||||
"Hex": CryptoJS.enc.Hex,
|
||||
"Base64": CryptoJS.enc.Base64,
|
||||
"UTF8": CryptoJS.enc.Utf8,
|
||||
"UTF16": CryptoJS.enc.Utf16,
|
||||
"UTF16LE": CryptoJS.enc.Utf16LE,
|
||||
"UTF16BE": CryptoJS.enc.Utf16BE,
|
||||
"Latin1": CryptoJS.enc.Latin1,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default Utils;
|
||||
@@ -1376,29 +1374,43 @@ String.prototype.count = function(chr) {
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Library overrides ///////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Override for the CryptoJS Hex encoding parser to remove whitespace before attempting to parse
|
||||
* the hex string.
|
||||
*
|
||||
* @param {string} hexStr
|
||||
* @returns {CryptoJS.lib.WordArray}
|
||||
/*
|
||||
* Polyfills
|
||||
*/
|
||||
CryptoJS.enc.Hex.parse = function (hexStr) {
|
||||
// Remove whitespace
|
||||
hexStr = hexStr.replace(/\s/g, "");
|
||||
|
||||
// Shortcut
|
||||
const hexStrLength = hexStr.length;
|
||||
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
|
||||
if (!String.prototype.padStart) {
|
||||
String.prototype.padStart = function padStart(targetLength, padString) {
|
||||
targetLength = targetLength>>0; //floor if number or convert non-number to 0;
|
||||
padString = String((typeof padString !== "undefined" ? padString : " "));
|
||||
if (this.length > targetLength) {
|
||||
return String(this);
|
||||
} else {
|
||||
targetLength = targetLength-this.length;
|
||||
if (targetLength > padString.length) {
|
||||
padString += padString.repeat(targetLength/padString.length); //append to original to ensure we are longer than needed
|
||||
}
|
||||
return padString.slice(0, targetLength) + String(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Convert
|
||||
const words = [];
|
||||
for (let i = 0; i < hexStrLength; i += 2) {
|
||||
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
|
||||
}
|
||||
|
||||
return new CryptoJS.lib.WordArray.init(words, hexStrLength / 2);
|
||||
};
|
||||
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
|
||||
if (!String.prototype.padEnd) {
|
||||
String.prototype.padEnd = function padEnd(targetLength, padString) {
|
||||
targetLength = targetLength>>0; //floor if number or convert non-number to 0;
|
||||
padString = String((typeof padString !== "undefined" ? padString : " "));
|
||||
if (this.length > targetLength) {
|
||||
return String(this);
|
||||
} else {
|
||||
targetLength = targetLength-this.length;
|
||||
if (targetLength > padString.length) {
|
||||
padString += padString.repeat(targetLength/padString.length); //append to original to ensure we are longer than needed
|
||||
}
|
||||
return String(this) + padString.slice(0, targetLength);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ const Categories = [
|
||||
"From HTML Entity",
|
||||
"URL Encode",
|
||||
"URL Decode",
|
||||
"Escape Unicode Characters",
|
||||
"Unescape Unicode Characters",
|
||||
"To Quoted Printable",
|
||||
"From Quoted Printable",
|
||||
@@ -79,8 +80,8 @@ const Categories = [
|
||||
"DES Decrypt",
|
||||
"Triple DES Encrypt",
|
||||
"Triple DES Decrypt",
|
||||
"Rabbit Encrypt",
|
||||
"Rabbit Decrypt",
|
||||
"RC2 Encrypt",
|
||||
"RC2 Decrypt",
|
||||
"RC4",
|
||||
"RC4 Drop",
|
||||
"ROT13",
|
||||
@@ -99,6 +100,9 @@ const Categories = [
|
||||
"Substitute",
|
||||
"Derive PBKDF2 key",
|
||||
"Derive EVP key",
|
||||
"Bcrypt",
|
||||
"Scrypt",
|
||||
"Pseudo-Random Number Generator",
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -110,10 +114,15 @@ const Categories = [
|
||||
"Hex to PEM",
|
||||
"Hex to Object Identifier",
|
||||
"Object Identifier to Hex",
|
||||
"Generate PGP Key Pair",
|
||||
"PGP Encrypt",
|
||||
"PGP Decrypt",
|
||||
"PGP Encrypt and Sign",
|
||||
"PGP Decrypt and Verify",
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Logical operations",
|
||||
name: "Arithmetic / Logic",
|
||||
ops: [
|
||||
"XOR",
|
||||
"XOR Brute Force",
|
||||
@@ -122,6 +131,13 @@ const Categories = [
|
||||
"AND",
|
||||
"ADD",
|
||||
"SUB",
|
||||
"Sum",
|
||||
"Subtract",
|
||||
"Multiply",
|
||||
"Divide",
|
||||
"Mean",
|
||||
"Median",
|
||||
"Standard Deviation",
|
||||
"Bit shift left",
|
||||
"Bit shift right",
|
||||
"Rotate left",
|
||||
@@ -166,6 +182,7 @@ const Categories = [
|
||||
"To Lower case",
|
||||
"Add line numbers",
|
||||
"Remove line numbers",
|
||||
"To Table",
|
||||
"Reverse",
|
||||
"Sort",
|
||||
"Unique",
|
||||
@@ -181,6 +198,7 @@ const Categories = [
|
||||
"Find / Replace",
|
||||
"Regular expression",
|
||||
"Offset checker",
|
||||
"Hamming Distance",
|
||||
"Convert distance",
|
||||
"Convert area",
|
||||
"Convert mass",
|
||||
@@ -191,6 +209,8 @@ const Categories = [
|
||||
"Parse colour code",
|
||||
"Escape string",
|
||||
"Unescape string",
|
||||
"Pseudo-Random Number Generator",
|
||||
"Sleep",
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -203,6 +223,7 @@ const Categories = [
|
||||
"Windows Filetime to UNIX Timestamp",
|
||||
"UNIX Timestamp to Windows Filetime",
|
||||
"Extract dates",
|
||||
"Sleep",
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -258,7 +279,15 @@ const Categories = [
|
||||
"HAS-160",
|
||||
"Whirlpool",
|
||||
"Snefru",
|
||||
"SSDEEP",
|
||||
"CTPH",
|
||||
"Compare SSDEEP hashes",
|
||||
"Compare CTPH hashes",
|
||||
"HMAC",
|
||||
"Bcrypt",
|
||||
"Bcrypt compare",
|
||||
"Bcrypt parse",
|
||||
"Scrypt",
|
||||
"Fletcher-8 Checksum",
|
||||
"Fletcher-16 Checksum",
|
||||
"Fletcher-32 Checksum",
|
||||
@@ -295,6 +324,8 @@ const Categories = [
|
||||
"To Snake case",
|
||||
"To Camel case",
|
||||
"To Kebab case",
|
||||
"BSON serialise",
|
||||
"BSON deserialise",
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -302,9 +333,11 @@ const Categories = [
|
||||
ops: [
|
||||
"Entropy",
|
||||
"Frequency distribution",
|
||||
"Chi Square",
|
||||
"Detect File Type",
|
||||
"Scan for Embedded Files",
|
||||
"Disassemble x86",
|
||||
"Pseudo-Random Number Generator",
|
||||
"Generate UUID",
|
||||
"Generate TOTP",
|
||||
"Generate HOTP",
|
||||
@@ -312,6 +345,7 @@ const Categories = [
|
||||
"Remove EXIF",
|
||||
"Extract EXIF",
|
||||
"Numberwang",
|
||||
"XKCD Random Number",
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
1463
src/core/config/OperationConfig.js
Executable file → Normal file
1463
src/core/config/OperationConfig.js
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
22
src/core/config/modules/BSON.js
Normal file
22
src/core/config/modules/BSON.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import BSON from "../../operations/BSON.js";
|
||||
|
||||
|
||||
/**
|
||||
* BSON module.
|
||||
*
|
||||
* Libraries:
|
||||
* - bson
|
||||
* - buffer
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
|
||||
|
||||
OpModules.BSON = {
|
||||
"BSON serialise": BSON.runBSONSerialise,
|
||||
"BSON deserialise": BSON.runBSONDeserialise,
|
||||
};
|
||||
|
||||
export default OpModules;
|
||||
@@ -6,7 +6,6 @@ import CharEnc from "../../operations/CharEnc.js";
|
||||
*
|
||||
* Libraries:
|
||||
* - cptable
|
||||
* - CryptoJS
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
|
||||
@@ -7,6 +7,7 @@ import Cipher from "../../operations/Cipher.js";
|
||||
* Libraries:
|
||||
* - CryptoJS
|
||||
* - Blowfish
|
||||
* - Forge
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
@@ -23,12 +24,12 @@ OpModules.Ciphers = {
|
||||
"DES Decrypt": Cipher.runDesDec,
|
||||
"Triple DES Encrypt": Cipher.runTripleDesEnc,
|
||||
"Triple DES Decrypt": Cipher.runTripleDesDec,
|
||||
"Rabbit Encrypt": Cipher.runRabbitEnc,
|
||||
"Rabbit Decrypt": Cipher.runRabbitDec,
|
||||
"Derive PBKDF2 key": Cipher.runPbkdf2,
|
||||
"Derive EVP key": Cipher.runEvpkdf,
|
||||
"RC4": Cipher.runRc4,
|
||||
"RC4 Drop": Cipher.runRc4drop,
|
||||
"RC2 Encrypt": Cipher.runRc2Enc,
|
||||
"RC2 Decrypt": Cipher.runRc2Dec,
|
||||
"Vigenère Encode": Cipher.runVigenereEnc,
|
||||
"Vigenère Decode": Cipher.runVigenereDec,
|
||||
"Bifid Cipher Encode": Cipher.runBifidEnc,
|
||||
@@ -37,6 +38,7 @@ OpModules.Ciphers = {
|
||||
"Affine Cipher Decode": Cipher.runAffineDec,
|
||||
"Atbash Cipher": Cipher.runAtbash,
|
||||
"Substitute": Cipher.runSubstitute,
|
||||
"Pseudo-Random Number Generator": Cipher.runPRNG,
|
||||
};
|
||||
|
||||
export default OpModules;
|
||||
|
||||
@@ -11,7 +11,7 @@ import Code from "../../operations/Code.js";
|
||||
* - xmldom
|
||||
* - xpath
|
||||
* - jpath
|
||||
* - googlecodeprettify
|
||||
* - highlight.js
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import FlowControl from "../../FlowControl.js";
|
||||
import Arithmetic from "../../operations/Arithmetic.js";
|
||||
import Base from "../../operations/Base.js";
|
||||
import Base58 from "../../operations/Base58.js";
|
||||
import Base64 from "../../operations/Base64.js";
|
||||
@@ -9,7 +10,7 @@ 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 Filetime from "../../operations/Filetime.js";
|
||||
import FileType from "../../operations/FileType.js";
|
||||
import Hexdump from "../../operations/Hexdump.js";
|
||||
import HTML from "../../operations/HTML.js";
|
||||
@@ -26,8 +27,11 @@ 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 ToTable from "../../operations/ToTable.js";
|
||||
import Unicode from "../../operations/Unicode.js";
|
||||
import UUID from "../../operations/UUID.js";
|
||||
import XKCD from "../../operations/XKCD.js";
|
||||
|
||||
|
||||
/**
|
||||
* Default module.
|
||||
@@ -37,9 +41,10 @@ import UUID from "../../operations/UUID.js";
|
||||
*
|
||||
* Libraries:
|
||||
* - Utils.js
|
||||
* - CryptoJS
|
||||
* - otp
|
||||
* - crypto
|
||||
* - bignumber.js
|
||||
* - jsesc
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
@@ -78,6 +83,7 @@ OpModules.Default = {
|
||||
"Strip HTML tags": HTML.runStripTags,
|
||||
"Parse colour code": HTML.runParseColourCode,
|
||||
"Unescape Unicode Characters": Unicode.runUnescape,
|
||||
"Escape Unicode Characters": Unicode.runEscape,
|
||||
"To Quoted Printable": QuotedPrintable.runTo,
|
||||
"From Quoted Printable": QuotedPrintable.runFrom,
|
||||
"Swap endianness": Endian.runSwapEndianness,
|
||||
@@ -89,7 +95,7 @@ OpModules.Default = {
|
||||
"Bit shift right": BitwiseOp.runBitShiftRight,
|
||||
"XOR": BitwiseOp.runXor,
|
||||
"XOR Brute Force": BitwiseOp.runXorBrute,
|
||||
"OR": BitwiseOp.runXor,
|
||||
"OR": BitwiseOp.runOr,
|
||||
"NOT": BitwiseOp.runNot,
|
||||
"AND": BitwiseOp.runAnd,
|
||||
"ADD": BitwiseOp.runAdd,
|
||||
@@ -99,17 +105,16 @@ OpModules.Default = {
|
||||
"Format MAC addresses": MAC.runFormat,
|
||||
"Encode NetBIOS Name": NetBIOS.runEncodeName,
|
||||
"Decode NetBIOS Name": NetBIOS.runDecodeName,
|
||||
"Regular expression": StrUtils.runRegex,
|
||||
"Offset checker": StrUtils.runOffsetChecker,
|
||||
"To Upper case": StrUtils.runUpper,
|
||||
"To Lower case": StrUtils.runLower,
|
||||
"Find / Replace": StrUtils.runFindReplace,
|
||||
"Split": StrUtils.runSplit,
|
||||
"Filter": StrUtils.runFilter,
|
||||
"Escape string": StrUtils.runEscape,
|
||||
"Unescape string": StrUtils.runUnescape,
|
||||
"Head": StrUtils.runHead,
|
||||
"Tail": StrUtils.runTail,
|
||||
"Hamming Distance": StrUtils.runHamming,
|
||||
"Remove whitespace": Tidy.runRemoveWhitespace,
|
||||
"Remove null bytes": Tidy.runRemoveNulls,
|
||||
"Drop bytes": Tidy.runDropBytes,
|
||||
@@ -132,17 +137,11 @@ OpModules.Default = {
|
||||
"Translate DateTime Format": DateTime.runTranslateFormat,
|
||||
"From UNIX Timestamp": DateTime.runFromUnixTimestamp,
|
||||
"To UNIX Timestamp": DateTime.runToUnixTimestamp,
|
||||
"Strings": Extract.runStrings,
|
||||
"Extract IP addresses": Extract.runIp,
|
||||
"Extract email addresses": Extract.runEmail,
|
||||
"Extract MAC addresses": Extract.runMac,
|
||||
"Extract URLs": Extract.runUrls,
|
||||
"Extract domains": Extract.runDomains,
|
||||
"Extract file paths": Extract.runFilePaths,
|
||||
"Extract dates": Extract.runDates,
|
||||
"Sleep": DateTime.runSleep,
|
||||
"Microsoft Script Decoder": MS.runDecodeScript,
|
||||
"Entropy": Entropy.runEntropy,
|
||||
"Frequency distribution": Entropy.runFreqDistrib,
|
||||
"Chi Square": Entropy.runChiSq,
|
||||
"Detect File Type": FileType.runDetect,
|
||||
"Scan for Embedded Files": FileType.runScanForEmbeddedFiles,
|
||||
"Generate UUID": UUID.runGenerateV4,
|
||||
@@ -158,6 +157,17 @@ OpModules.Default = {
|
||||
"Return": FlowControl.runReturn,
|
||||
"Comment": FlowControl.runComment,
|
||||
"PHP Deserialize": PHP.runDeserialize,
|
||||
"Sum": Arithmetic.runSum,
|
||||
"Subtract": Arithmetic.runSub,
|
||||
"Multiply": Arithmetic.runMulti,
|
||||
"Divide": Arithmetic.runDiv,
|
||||
"Mean": Arithmetic.runMean,
|
||||
"Median": Arithmetic.runMedian,
|
||||
"Standard Deviation": Arithmetic.runStdDev,
|
||||
"To Table": ToTable.runToTable,
|
||||
"Windows Filetime to UNIX Timestamp": Filetime.runFromFiletimeToUnix,
|
||||
"UNIX Timestamp to Windows Filetime": Filetime.runToFiletimeFromUnix,
|
||||
"XKCD Random Number": XKCD.runRandomNumber,
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -18,31 +18,39 @@ import Hash from "../../operations/Hash.js";
|
||||
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,
|
||||
"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,
|
||||
"CTPH": Hash.runCTPH,
|
||||
"SSDEEP": Hash.runSSDEEP,
|
||||
"Compare CTPH hashes": Hash.runCompareCTPH,
|
||||
"Compare SSDEEP hashes": Hash.runCompareSSDEEP,
|
||||
"HMAC": Hash.runHMAC,
|
||||
"Bcrypt": Hash.runBcrypt,
|
||||
"Bcrypt compare": Hash.runBcryptCompare,
|
||||
"Bcrypt parse": Hash.runBcryptParse,
|
||||
"Scrypt": Hash.runScrypt,
|
||||
"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;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import IP from "../../operations/IP.js";
|
||||
import Filetime from "../../operations/Filetime.js";
|
||||
|
||||
|
||||
/**
|
||||
@@ -21,8 +20,6 @@ OpModules.JSBN = {
|
||||
"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;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import OpModules from "./Default.js";
|
||||
import BSONModule from "./BSON.js";
|
||||
import CharEncModule from "./CharEnc.js";
|
||||
import CipherModule from "./Ciphers.js";
|
||||
import CodeModule from "./Code.js";
|
||||
@@ -18,11 +19,13 @@ import HTTPModule from "./HTTP.js";
|
||||
import ImageModule from "./Image.js";
|
||||
import JSBNModule from "./JSBN.js";
|
||||
import PublicKeyModule from "./PublicKey.js";
|
||||
import RegexModule from "./Regex.js";
|
||||
import ShellcodeModule from "./Shellcode.js";
|
||||
import URLModule from "./URL.js";
|
||||
|
||||
Object.assign(
|
||||
OpModules,
|
||||
BSONModule,
|
||||
CharEncModule,
|
||||
CipherModule,
|
||||
CodeModule,
|
||||
@@ -34,6 +37,7 @@ Object.assign(
|
||||
ImageModule,
|
||||
JSBNModule,
|
||||
PublicKeyModule,
|
||||
RegexModule,
|
||||
ShellcodeModule,
|
||||
URLModule
|
||||
);
|
||||
|
||||
25
src/core/config/modules/PGP.js
Normal file
25
src/core/config/modules/PGP.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import PGP from "../../operations/PGP.js";
|
||||
|
||||
|
||||
/**
|
||||
* PGP module.
|
||||
*
|
||||
* Libraries:
|
||||
* - kbpgp
|
||||
*
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @author Matt C [matt@artemisbot.uk]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
|
||||
|
||||
OpModules.PGP = {
|
||||
"Generate PGP Key Pair": PGP.runGenerateKeyPair,
|
||||
"PGP Encrypt": PGP.runEncrypt,
|
||||
"PGP Decrypt": PGP.runDecrypt,
|
||||
"PGP Encrypt and Sign": PGP.runSign,
|
||||
"PGP Decrypt and Verify": PGP.runVerify,
|
||||
};
|
||||
|
||||
export default OpModules;
|
||||
30
src/core/config/modules/Regex.js
Normal file
30
src/core/config/modules/Regex.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import Extract from "../../operations/Extract.js";
|
||||
import Regex from "../../operations/Regex.js";
|
||||
|
||||
|
||||
/**
|
||||
* Regex module.
|
||||
*
|
||||
* Libraries:
|
||||
* - XRegExp
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
|
||||
|
||||
OpModules.Regex = {
|
||||
"Regular expression": Regex.runRegex,
|
||||
"Find / Replace": Regex.runFindReplace,
|
||||
"Strings": Extract.runStrings,
|
||||
"Extract IP addresses": Extract.runIp,
|
||||
"Extract email addresses": Extract.runEmail,
|
||||
"Extract MAC addresses": Extract.runMac,
|
||||
"Extract URLs": Extract.runUrls,
|
||||
"Extract domains": Extract.runDomains,
|
||||
"Extract file paths": Extract.runFilePaths,
|
||||
"Extract dates": Extract.runDates,
|
||||
};
|
||||
|
||||
export default OpModules;
|
||||
@@ -1443,8 +1443,8 @@ const Operands = [
|
||||
------------------------------------------------------------------------------------------------------------------------*/
|
||||
"10000004","10000004","10000004","10000004",
|
||||
"16000C00","170E0C00","0C001600","0C00170E",
|
||||
"10020008",
|
||||
"10020008",
|
||||
"110E0008",
|
||||
"110E0008",
|
||||
"0D060C01", //JMP Ap (w:z).
|
||||
"100000040004",
|
||||
"16001A01","170E1A01",
|
||||
@@ -5703,7 +5703,6 @@ function LDisassemble()
|
||||
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
253
src/core/operations/Arithmetic.js
Normal file
253
src/core/operations/Arithmetic.js
Normal file
@@ -0,0 +1,253 @@
|
||||
import Utils from "../Utils.js";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
|
||||
/**
|
||||
* Math operations on numbers.
|
||||
*
|
||||
* @author bwhitn [brian.m.whitney@outlook.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
const Arithmetic = {
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
DELIM_OPTIONS: ["Line feed", "Space", "Comma", "Semi-colon", "Colon", "CRLF"],
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string based on a delimiter and calculates the sum of numbers.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runSum: function(input, args) {
|
||||
const val = Arithmetic._sum(Arithmetic._createNumArray(input, args[0]));
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string based on a delimiter and subtracts all the numbers.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runSub: function(input, args) {
|
||||
let val = Arithmetic._sub(Arithmetic._createNumArray(input, args[0]));
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string based on a delimiter and multiplies the numbers.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runMulti: function(input, args) {
|
||||
let val = Arithmetic._multi(Arithmetic._createNumArray(input, args[0]));
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string based on a delimiter and divides the numbers.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runDiv: function(input, args) {
|
||||
let val = Arithmetic._div(Arithmetic._createNumArray(input, args[0]));
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string based on a delimiter and computes the mean (average).
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runMean: function(input, args) {
|
||||
let val = Arithmetic._mean(Arithmetic._createNumArray(input, args[0]));
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string based on a delimiter and finds the median.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runMedian: function(input, args) {
|
||||
let val = Arithmetic._median(Arithmetic._createNumArray(input, args[0]));
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* splits a string based on a delimiter and computes the standard deviation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runStdDev: function(input, args) {
|
||||
let val = Arithmetic._stdDev(Arithmetic._createNumArray(input, args[0]));
|
||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Converts a string array to a number array.
|
||||
*
|
||||
* @private
|
||||
* @param {string[]} input
|
||||
* @param {string} delim
|
||||
* @returns {BigNumber[]}
|
||||
*/
|
||||
_createNumArray: function(input, delim) {
|
||||
delim = Utils.charRep[delim || "Space"];
|
||||
let splitNumbers = input.split(delim),
|
||||
numbers = [],
|
||||
num;
|
||||
|
||||
for (let i = 0; i < splitNumbers.length; i++) {
|
||||
try {
|
||||
num = BigNumber(splitNumbers[i].trim());
|
||||
if (!num.isNaN()) {
|
||||
numbers.push(num);
|
||||
}
|
||||
} catch (err) {
|
||||
// This line is not a valid number
|
||||
}
|
||||
}
|
||||
return numbers;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Adds an array of numbers and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_sum: function(data) {
|
||||
if (data.length > 0) {
|
||||
return data.reduce((acc, curr) => acc.plus(curr));
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Subtracts an array of numbers and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_sub: function(data) {
|
||||
if (data.length > 0) {
|
||||
return data.reduce((acc, curr) => acc.minus(curr));
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Multiplies an array of numbers and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_multi: function(data) {
|
||||
if (data.length > 0) {
|
||||
return data.reduce((acc, curr) => acc.times(curr));
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Divides an array of numbers and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_div: function(data) {
|
||||
if (data.length > 0) {
|
||||
return data.reduce((acc, curr) => acc.div(curr));
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Computes mean of a number array and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_mean: function(data) {
|
||||
if (data.length > 0) {
|
||||
return Arithmetic._sum(data).div(data.length);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Computes median of a number array and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_median: function (data) {
|
||||
if ((data.length % 2) === 0 && data.length > 0) {
|
||||
let first, second;
|
||||
data.sort(function(a, b){
|
||||
return a.minus(b);
|
||||
});
|
||||
first = data[Math.floor(data.length / 2)];
|
||||
second = data[Math.floor(data.length / 2) - 1];
|
||||
return Arithmetic._mean([first, second]);
|
||||
} else {
|
||||
return data[Math.floor(data.length / 2)];
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Computes standard deviation of a number array and returns the value.
|
||||
*
|
||||
* @private
|
||||
* @param {BigNumber[]} data
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
_stdDev: function (data) {
|
||||
if (data.length > 0) {
|
||||
let avg = Arithmetic._mean(data);
|
||||
let devSum = new BigNumber(0);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
devSum = devSum.plus(data[i].minus(avg).pow(2));
|
||||
}
|
||||
return devSum.div(data.length).sqrt();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default Arithmetic;
|
||||
@@ -1,4 +1,5 @@
|
||||
import Utils from "../Utils.js";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
|
||||
/**
|
||||
@@ -61,14 +62,14 @@ const BCD = {
|
||||
/**
|
||||
* To BCD operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runToBCD: function(input, args) {
|
||||
if (isNaN(input))
|
||||
if (input.isNaN())
|
||||
return "Invalid input";
|
||||
if (Math.floor(input) !== input)
|
||||
if (!input.integerValue(BigNumber.ROUND_DOWN).isEqualTo(input))
|
||||
return "Fractional values are not supported by BCD";
|
||||
|
||||
const encoding = BCD.ENCODING_LOOKUP[args[0]],
|
||||
@@ -77,7 +78,7 @@ const BCD = {
|
||||
outputFormat = args[3];
|
||||
|
||||
// Split input number up into separate digits
|
||||
const digits = input.toString().split("");
|
||||
const digits = input.toFixed().split("");
|
||||
|
||||
if (digits[0] === "-" || digits[0] === "+") {
|
||||
digits.shift();
|
||||
@@ -134,11 +135,11 @@ const BCD = {
|
||||
switch (outputFormat) {
|
||||
case "Nibbles":
|
||||
return nibbles.map(n => {
|
||||
return Utils.padLeft(n.toString(2), 4);
|
||||
return n.toString(2).padStart(4, "0");
|
||||
}).join(" ");
|
||||
case "Bytes":
|
||||
return bytes.map(b => {
|
||||
return Utils.padLeft(b.toString(2), 8);
|
||||
return b.toString(2).padStart(8, "0");
|
||||
}).join(" ");
|
||||
case "Raw":
|
||||
default:
|
||||
@@ -152,7 +153,7 @@ const BCD = {
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runFromBCD: function(input, args) {
|
||||
const encoding = BCD.ENCODING_LOOKUP[args[0]],
|
||||
@@ -206,7 +207,7 @@ const BCD = {
|
||||
output += val.toString();
|
||||
});
|
||||
|
||||
return parseInt(output, 10);
|
||||
return new BigNumber(output);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
59
src/core/operations/BSON.js
Normal file
59
src/core/operations/BSON.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import bsonjs from "bson";
|
||||
import {Buffer} from "buffer";
|
||||
|
||||
|
||||
/**
|
||||
* BSON operations.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
const BSON = {
|
||||
|
||||
/**
|
||||
* BSON serialise operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
runBSONSerialise(input, args) {
|
||||
if (!input) return new ArrayBuffer();
|
||||
|
||||
const bson = new bsonjs();
|
||||
|
||||
try {
|
||||
const data = JSON.parse(input);
|
||||
return bson.serialize(data).buffer;
|
||||
} catch (err) {
|
||||
throw err.toString();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* BSON deserialise operation.
|
||||
*
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
*/
|
||||
runBSONDeserialise(input, args) {
|
||||
if (!input.byteLength) return "";
|
||||
|
||||
const bson = new bsonjs();
|
||||
|
||||
try {
|
||||
const data = bson.deserialize(new Buffer(input));
|
||||
return JSON.stringify(data, null, 2);
|
||||
} catch (err) {
|
||||
return err.toString();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default BSON;
|
||||
@@ -1,3 +1,5 @@
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
/**
|
||||
* Numerical base operations.
|
||||
*
|
||||
@@ -18,7 +20,7 @@ const Base = {
|
||||
/**
|
||||
* To Base operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -39,7 +41,7 @@ const Base = {
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runFrom: function(input, args) {
|
||||
const radix = args[0] || Base.DEFAULT_RADIX;
|
||||
@@ -48,14 +50,14 @@ const Base = {
|
||||
}
|
||||
|
||||
let number = input.replace(/\s/g, "").split("."),
|
||||
result = parseInt(number[0], radix) || 0;
|
||||
result = new BigNumber(number[0], radix) || 0;
|
||||
|
||||
if (number.length === 1) return result;
|
||||
|
||||
// Fractional part
|
||||
for (let i = 0; i < number[1].length; i++) {
|
||||
const digit = parseInt(number[1][i], radix);
|
||||
result += digit / Math.pow(radix, i+1);
|
||||
const digit = new BigNumber(number[1][i], radix);
|
||||
result += digit.div(Math.pow(radix, i+1));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -40,13 +40,13 @@ const Base64 = {
|
||||
/**
|
||||
* To Base64 operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runTo: function(input, args) {
|
||||
const alphabet = args[0] || Base64.ALPHABET;
|
||||
return Utils.toBase64(input, alphabet);
|
||||
return Utils.toBase64(new Uint8Array(input), alphabet);
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ const BitwiseOp = {
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
KEY_FORMAT: ["Hex", "Base64", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1"],
|
||||
KEY_FORMAT: ["Hex", "Base64", "UTF8", "Latin1"],
|
||||
|
||||
/**
|
||||
* XOR operation.
|
||||
@@ -77,12 +77,10 @@ const BitwiseOp = {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runXor: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || ""),
|
||||
const key = Utils.convertToByteArray(args[0].string || "", args[0].option),
|
||||
scheme = args[1],
|
||||
nullPreserving = args[2];
|
||||
|
||||
key = Utils.wordArrayToByteArray(key);
|
||||
|
||||
return BitwiseOp._bitOp(input, key, BitwiseOp._xor, nullPreserving, scheme);
|
||||
},
|
||||
|
||||
@@ -200,8 +198,7 @@ const BitwiseOp = {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runAnd: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || "");
|
||||
key = Utils.wordArrayToByteArray(key);
|
||||
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
|
||||
|
||||
return BitwiseOp._bitOp(input, key, BitwiseOp._and);
|
||||
},
|
||||
@@ -215,8 +212,7 @@ const BitwiseOp = {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runOr: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || "");
|
||||
key = Utils.wordArrayToByteArray(key);
|
||||
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
|
||||
|
||||
return BitwiseOp._bitOp(input, key, BitwiseOp._or);
|
||||
},
|
||||
@@ -230,8 +226,7 @@ const BitwiseOp = {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runAdd: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || "");
|
||||
key = Utils.wordArrayToByteArray(key);
|
||||
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
|
||||
|
||||
return BitwiseOp._bitOp(input, key, BitwiseOp._add);
|
||||
},
|
||||
@@ -245,8 +240,7 @@ const BitwiseOp = {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runSub: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || "");
|
||||
key = Utils.wordArrayToByteArray(key);
|
||||
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
|
||||
|
||||
return BitwiseOp._bitOp(input, key, BitwiseOp._sub);
|
||||
},
|
||||
|
||||
@@ -21,7 +21,12 @@ const ByteRepr = {
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
HEX_DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"],
|
||||
TO_HEX_DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
FROM_HEX_DELIM_OPTIONS: ["Auto", "Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
@@ -31,13 +36,13 @@ const ByteRepr = {
|
||||
/**
|
||||
* To Hex operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runToHex: function(input, args) {
|
||||
const delim = Utils.charRep[args[0] || "Space"];
|
||||
return Utils.toHex(input, delim, 2);
|
||||
return Utils.toHex(new Uint8Array(input), delim, 2);
|
||||
},
|
||||
|
||||
|
||||
@@ -148,6 +153,10 @@ const ByteRepr = {
|
||||
throw "Error: Base argument must be between 2 and 36";
|
||||
}
|
||||
|
||||
if (input.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (base !== 16 && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
|
||||
|
||||
// Split into groups of 2 if the whole string is concatenated and
|
||||
@@ -186,7 +195,7 @@ const ByteRepr = {
|
||||
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
|
||||
if (delim === "0x" || delim === "\\x") {
|
||||
pos[0].start += 2;
|
||||
pos[0].end += 2;
|
||||
pos[0].end += 2;
|
||||
}
|
||||
return pos;
|
||||
},
|
||||
@@ -266,7 +275,7 @@ const ByteRepr = {
|
||||
padding = 8;
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
output += Utils.pad(input[i].toString(2), padding) + delim;
|
||||
output += input[i].toString(2).padStart(padding, "0") + delim;
|
||||
}
|
||||
|
||||
if (delim.length) {
|
||||
|
||||
@@ -120,7 +120,7 @@ const Checksum = {
|
||||
/**
|
||||
* CRC-32 Checksum operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -132,7 +132,7 @@ const Checksum = {
|
||||
/**
|
||||
* CRC-16 Checksum operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import Utils from "../Utils.js";
|
||||
import CryptoJS from "crypto-js";
|
||||
import forge from "imports-loader?jQuery=>null!node-forge/dist/forge.min.js";
|
||||
import {blowfish as Blowfish} from "sladex-blowfish";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
|
||||
/**
|
||||
@@ -18,132 +20,27 @@ const Cipher = {
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
IO_FORMAT1: ["Hex", "Base64", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1"],
|
||||
IO_FORMAT1: ["Hex", "UTF8", "Latin1", "Base64"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
IO_FORMAT2: ["UTF8", "Latin1", "Hex", "Base64"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
IO_FORMAT3: ["Raw", "Hex"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
IO_FORMAT4: ["Hex", "Raw"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
IO_FORMAT2: ["UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1", "Hex", "Base64"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
IO_FORMAT3: ["Hex", "Base64", "UTF16", "UTF16LE", "UTF16BE", "Latin1"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
IO_FORMAT4: ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
MODES: ["CBC", "CFB", "CTR", "OFB", "ECB"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
PADDING: ["Pkcs7", "Iso97971", "AnsiX923", "Iso10126", "ZeroPadding", "NoPadding"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
RESULT_TYPE: ["Show all", "Ciphertext", "Key", "IV", "Salt"],
|
||||
|
||||
|
||||
/**
|
||||
* Runs encryption operations using the CryptoJS framework.
|
||||
*
|
||||
* @private
|
||||
* @param {function} algo - The CryptoJS algorithm to use
|
||||
* @param {byteArray} input
|
||||
* @param {function} args
|
||||
* @returns {string}
|
||||
*/
|
||||
_enc: function (algo, input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || ""),
|
||||
iv = Utils.format[args[1].option].parse(args[1].string || ""),
|
||||
salt = Utils.format[args[2].option].parse(args[2].string || ""),
|
||||
mode = CryptoJS.mode[args[3]],
|
||||
padding = CryptoJS.pad[args[4]],
|
||||
resultOption = args[5].toLowerCase(),
|
||||
outputFormat = args[6];
|
||||
|
||||
if (iv.sigBytes === 0) {
|
||||
// Use passphrase rather than key. Need to convert it to a string.
|
||||
key = key.toString(CryptoJS.enc.Latin1);
|
||||
}
|
||||
|
||||
const encrypted = algo.encrypt(input, key, {
|
||||
salt: salt.sigBytes > 0 ? salt : false,
|
||||
iv: iv.sigBytes > 0 ? iv : null,
|
||||
mode: mode,
|
||||
padding: padding
|
||||
});
|
||||
|
||||
let result = "";
|
||||
if (resultOption === "show all") {
|
||||
result += "Key: " + encrypted.key.toString(Utils.format[outputFormat]);
|
||||
result += "\nIV: " + encrypted.iv.toString(Utils.format[outputFormat]);
|
||||
if (encrypted.salt) result += "\nSalt: " + encrypted.salt.toString(Utils.format[outputFormat]);
|
||||
result += "\n\nCiphertext: " + encrypted.ciphertext.toString(Utils.format[outputFormat]);
|
||||
} else {
|
||||
result = encrypted[resultOption].toString(Utils.format[outputFormat]);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Runs decryption operations using the CryptoJS framework.
|
||||
*
|
||||
* @private
|
||||
* @param {function} algo - The CryptoJS algorithm to use
|
||||
* @param {byteArray} input
|
||||
* @param {function} args
|
||||
* @returns {string}
|
||||
*/
|
||||
_dec: function (algo, input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string || ""),
|
||||
iv = Utils.format[args[1].option].parse(args[1].string || ""),
|
||||
salt = Utils.format[args[2].option].parse(args[2].string || ""),
|
||||
mode = CryptoJS.mode[args[3]],
|
||||
padding = CryptoJS.pad[args[4]],
|
||||
inputFormat = args[5],
|
||||
outputFormat = args[6];
|
||||
|
||||
// The ZeroPadding option causes a crash when the input length is 0
|
||||
if (!input.length) {
|
||||
return "No input";
|
||||
}
|
||||
|
||||
const ciphertext = Utils.format[inputFormat].parse(input);
|
||||
|
||||
if (iv.sigBytes === 0) {
|
||||
// Use passphrase rather than key. Need to convert it to a string.
|
||||
key = key.toString(CryptoJS.enc.Latin1);
|
||||
}
|
||||
|
||||
const decrypted = algo.decrypt({
|
||||
ciphertext: ciphertext,
|
||||
salt: salt.sigBytes > 0 ? salt : false
|
||||
}, key, {
|
||||
iv: iv.sigBytes > 0 ? iv : null,
|
||||
mode: mode,
|
||||
padding: padding
|
||||
});
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = decrypted.toString(Utils.format[outputFormat]);
|
||||
} catch (err) {
|
||||
result = "Decrypt error: " + err.message;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
AES_MODES: ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"],
|
||||
|
||||
/**
|
||||
* AES Encrypt operation.
|
||||
@@ -153,7 +50,41 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runAesEnc: function (input, args) {
|
||||
return Cipher._enc(CryptoJS.AES, input, args);
|
||||
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4];
|
||||
|
||||
if ([16, 24, 32].indexOf(key.length) < 0) {
|
||||
return `Invalid key length: ${key.length} bytes
|
||||
|
||||
The following algorithms will be used based on the size of the key:
|
||||
16 bytes = AES-128
|
||||
24 bytes = AES-192
|
||||
32 bytes = AES-256`;
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
const cipher = forge.cipher.createCipher("AES-" + mode, key);
|
||||
cipher.start({iv: iv});
|
||||
cipher.update(forge.util.createBuffer(input));
|
||||
cipher.finish();
|
||||
|
||||
if (outputType === "Hex") {
|
||||
if (mode === "GCM") {
|
||||
return cipher.output.toHex() + "\n\n" +
|
||||
"Tag: " + cipher.mode.tag.toHex();
|
||||
}
|
||||
return cipher.output.toHex();
|
||||
} else {
|
||||
if (mode === "GCM") {
|
||||
return cipher.output.getBytes() + "\n\n" +
|
||||
"Tag: " + cipher.mode.tag.getBytes();
|
||||
}
|
||||
return cipher.output.getBytes();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -165,10 +96,46 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runAesDec: function (input, args) {
|
||||
return Cipher._dec(CryptoJS.AES, input, args);
|
||||
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4],
|
||||
gcmTag = Utils.convertToByteString(args[5].string, args[5].option);
|
||||
|
||||
if ([16, 24, 32].indexOf(key.length) < 0) {
|
||||
return `Invalid key length: ${key.length} bytes
|
||||
|
||||
The following algorithms will be used based on the size of the key:
|
||||
16 bytes = AES-128
|
||||
24 bytes = AES-192
|
||||
32 bytes = AES-256`;
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
const decipher = forge.cipher.createDecipher("AES-" + mode, key);
|
||||
decipher.start({
|
||||
iv: iv,
|
||||
tag: gcmTag
|
||||
});
|
||||
decipher.update(forge.util.createBuffer(input));
|
||||
const result = decipher.finish();
|
||||
|
||||
if (result) {
|
||||
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
|
||||
} else {
|
||||
return "Unable to decrypt input with these parameters.";
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
DES_MODES: ["CBC", "CFB", "OFB", "CTR", "ECB"],
|
||||
|
||||
/**
|
||||
* DES Encrypt operation.
|
||||
*
|
||||
@@ -177,7 +144,27 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runDesEnc: function (input, args) {
|
||||
return Cipher._enc(CryptoJS.DES, input, args);
|
||||
const key = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4];
|
||||
|
||||
if (key.length !== 8) {
|
||||
return `Invalid key length: ${key.length} bytes
|
||||
|
||||
DES uses a key length of 8 bytes (64 bits).
|
||||
Triple DES uses a key length of 24 bytes (192 bits).`;
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
const cipher = forge.cipher.createCipher("DES-" + mode, key);
|
||||
cipher.start({iv: iv});
|
||||
cipher.update(forge.util.createBuffer(input));
|
||||
cipher.finish();
|
||||
|
||||
return outputType === "Hex" ? cipher.output.toHex() : cipher.output.getBytes();
|
||||
},
|
||||
|
||||
|
||||
@@ -189,7 +176,31 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runDesDec: function (input, args) {
|
||||
return Cipher._dec(CryptoJS.DES, input, args);
|
||||
const key = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4];
|
||||
|
||||
if (key.length !== 8) {
|
||||
return `Invalid key length: ${key.length} bytes
|
||||
|
||||
DES uses a key length of 8 bytes (64 bits).
|
||||
Triple DES uses a key length of 24 bytes (192 bits).`;
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
const decipher = forge.cipher.createDecipher("DES-" + mode, key);
|
||||
decipher.start({iv: iv});
|
||||
decipher.update(forge.util.createBuffer(input));
|
||||
const result = decipher.finish();
|
||||
|
||||
if (result) {
|
||||
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
|
||||
} else {
|
||||
return "Unable to decrypt input with these parameters.";
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -201,7 +212,27 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runTripleDesEnc: function (input, args) {
|
||||
return Cipher._enc(CryptoJS.TripleDES, input, args);
|
||||
const key = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4];
|
||||
|
||||
if (key.length !== 24) {
|
||||
return `Invalid key length: ${key.length} bytes
|
||||
|
||||
Triple DES uses a key length of 24 bytes (192 bits).
|
||||
DES uses a key length of 8 bytes (64 bits).`;
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
const cipher = forge.cipher.createCipher("3DES-" + mode, key);
|
||||
cipher.start({iv: iv});
|
||||
cipher.update(forge.util.createBuffer(input));
|
||||
cipher.finish();
|
||||
|
||||
return outputType === "Hex" ? cipher.output.toHex() : cipher.output.getBytes();
|
||||
},
|
||||
|
||||
|
||||
@@ -213,31 +244,79 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runTripleDesDec: function (input, args) {
|
||||
return Cipher._dec(CryptoJS.TripleDES, input, args);
|
||||
const key = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4];
|
||||
|
||||
if (key.length !== 24) {
|
||||
return `Invalid key length: ${key.length} bytes
|
||||
|
||||
Triple DES uses a key length of 24 bytes (192 bits).
|
||||
DES uses a key length of 8 bytes (64 bits).`;
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
const decipher = forge.cipher.createDecipher("3DES-" + mode, key);
|
||||
decipher.start({iv: iv});
|
||||
decipher.update(forge.util.createBuffer(input));
|
||||
const result = decipher.finish();
|
||||
|
||||
if (result) {
|
||||
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
|
||||
} else {
|
||||
return "Unable to decrypt input with these parameters.";
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Rabbit Encrypt operation.
|
||||
* RC2 Encrypt operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runRabbitEnc: function (input, args) {
|
||||
return Cipher._enc(CryptoJS.Rabbit, input, args);
|
||||
runRc2Enc: function (input, args) {
|
||||
const key = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteString(args[1].string, args[1].option),
|
||||
inputType = args[2],
|
||||
outputType = args[3],
|
||||
cipher = forge.rc2.createEncryptionCipher(key);
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
cipher.start(iv || null);
|
||||
cipher.update(forge.util.createBuffer(input));
|
||||
cipher.finish();
|
||||
|
||||
return outputType === "Hex" ? cipher.output.toHex() : cipher.output.getBytes();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Rabbit Decrypt operation.
|
||||
* RC2 Decrypt operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runRabbitDec: function (input, args) {
|
||||
return Cipher._dec(CryptoJS.Rabbit, input, args);
|
||||
runRc2Dec: function (input, args) {
|
||||
const key = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteString(args[1].string, args[1].option),
|
||||
inputType = args[2],
|
||||
outputType = args[3],
|
||||
decipher = forge.rc2.createDecryptionCipher(key);
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
decipher.start(iv || null);
|
||||
decipher.update(forge.util.createBuffer(input));
|
||||
decipher.finish();
|
||||
|
||||
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
|
||||
},
|
||||
|
||||
|
||||
@@ -245,12 +324,29 @@ const Cipher = {
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
BLOWFISH_MODES: ["ECB", "CBC", "PCBC", "CFB", "OFB", "CTR"],
|
||||
BLOWFISH_MODES: ["CBC", "PCBC", "CFB", "OFB", "CTR", "ECB"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
BLOWFISH_OUTPUT_TYPES: ["Base64", "Hex", "String", "Raw"],
|
||||
BLOWFISH_OUTPUT_TYPES: ["Hex", "Base64", "Raw"],
|
||||
|
||||
/**
|
||||
* Lookup table for Blowfish output types.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_BLOWFISH_OUTPUT_TYPE_LOOKUP: {
|
||||
Base64: 0, Hex: 1, String: 2, Raw: 3
|
||||
},
|
||||
/**
|
||||
* Lookup table for Blowfish modes.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_BLOWFISH_MODE_LOOKUP: {
|
||||
ECB: 0, CBC: 1, PCBC: 2, CFB: 3, OFB: 4, CTR: 5
|
||||
},
|
||||
|
||||
/**
|
||||
* Blowfish Encrypt operation.
|
||||
@@ -260,19 +356,24 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runBlowfishEnc: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1),
|
||||
mode = args[1],
|
||||
outputFormat = args[2];
|
||||
const key = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4];
|
||||
|
||||
if (key.length === 0) return "Enter a key";
|
||||
|
||||
let encHex = Blowfish.encrypt(input, key, {
|
||||
outputType: 1,
|
||||
cipherMode: Cipher.BLOWFISH_MODES.indexOf(mode)
|
||||
}),
|
||||
enc = CryptoJS.enc.Hex.parse(encHex);
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
return enc.toString(Utils.format[outputFormat]);
|
||||
Blowfish.setIV(Utils.toBase64(iv), 0);
|
||||
|
||||
const enc = Blowfish.encrypt(input, key, {
|
||||
outputType: Cipher._BLOWFISH_OUTPUT_TYPE_LOOKUP[outputType],
|
||||
cipherMode: Cipher._BLOWFISH_MODE_LOOKUP[mode]
|
||||
});
|
||||
|
||||
return outputType === "Raw" ? Utils.byteArrayToChars(enc) : enc ;
|
||||
},
|
||||
|
||||
|
||||
@@ -284,18 +385,24 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runBlowfishDec: function (input, args) {
|
||||
let key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1),
|
||||
mode = args[1],
|
||||
inputFormat = args[2];
|
||||
const key = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4];
|
||||
|
||||
if (key.length === 0) return "Enter a key";
|
||||
|
||||
input = Utils.format[inputFormat].parse(input);
|
||||
input = inputType === "Raw" ? Utils.strToByteArray(input) : input;
|
||||
|
||||
return Blowfish.decrypt(input.toString(CryptoJS.enc.Base64), key, {
|
||||
outputType: 0, // This actually means inputType. The library is weird.
|
||||
cipherMode: Cipher.BLOWFISH_MODES.indexOf(mode)
|
||||
Blowfish.setIV(Utils.toBase64(iv), 0);
|
||||
|
||||
const result = Blowfish.decrypt(input, key, {
|
||||
outputType: Cipher._BLOWFISH_OUTPUT_TYPE_LOOKUP[inputType], // This actually means inputType. The library is weird.
|
||||
cipherMode: Cipher._BLOWFISH_MODE_LOOKUP[mode]
|
||||
});
|
||||
|
||||
return outputType === "Hex" ? Utils.toHexFast(Utils.strToByteArray(result)) : result;
|
||||
},
|
||||
|
||||
|
||||
@@ -303,7 +410,7 @@ const Cipher = {
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
KDF_KEY_SIZE: 256,
|
||||
KDF_KEY_SIZE: 128,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
@@ -313,7 +420,7 @@ const Cipher = {
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
HASHERS: ["MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "RIPEMD160"],
|
||||
HASHERS: ["SHA1", "SHA256", "SHA384", "SHA512", "MD5"],
|
||||
|
||||
/**
|
||||
* Derive PBKDF2 key operation.
|
||||
@@ -323,20 +430,15 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runPbkdf2: function (input, args) {
|
||||
let keySize = args[0] / 32,
|
||||
iterations = args[1],
|
||||
hasher = args[2],
|
||||
salt = CryptoJS.enc.Hex.parse(args[3] || ""),
|
||||
inputFormat = args[4],
|
||||
outputFormat = args[5],
|
||||
passphrase = Utils.format[inputFormat].parse(input),
|
||||
key = CryptoJS.PBKDF2(passphrase, salt, {
|
||||
keySize: keySize,
|
||||
hasher: CryptoJS.algo[hasher],
|
||||
iterations: iterations,
|
||||
});
|
||||
const passphrase = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
keySize = args[1],
|
||||
iterations = args[2],
|
||||
hasher = args[3],
|
||||
salt = Utils.convertToByteString(args[4].string, args[4].option) ||
|
||||
forge.random.getBytesSync(keySize),
|
||||
derivedKey = forge.pkcs5.pbkdf2(passphrase, salt, iterations, keySize / 8, hasher.toLowerCase());
|
||||
|
||||
return key.toString(Utils.format[outputFormat]);
|
||||
return forge.util.bytesToHex(derivedKey);
|
||||
},
|
||||
|
||||
|
||||
@@ -348,23 +450,33 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runEvpkdf: function (input, args) {
|
||||
let keySize = args[0] / 32,
|
||||
iterations = args[1],
|
||||
hasher = args[2],
|
||||
salt = CryptoJS.enc.Hex.parse(args[3] || ""),
|
||||
inputFormat = args[4],
|
||||
outputFormat = args[5],
|
||||
passphrase = Utils.format[inputFormat].parse(input),
|
||||
const passphrase = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
keySize = args[1] / 32,
|
||||
iterations = args[2],
|
||||
hasher = args[3],
|
||||
salt = Utils.convertToByteString(args[4].string, args[4].option),
|
||||
key = CryptoJS.EvpKDF(passphrase, salt, {
|
||||
keySize: keySize,
|
||||
hasher: CryptoJS.algo[hasher],
|
||||
iterations: iterations,
|
||||
});
|
||||
|
||||
return key.toString(Utils.format[outputFormat]);
|
||||
return key.toString(CryptoJS.enc.Hex);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
RC4_KEY_FORMAT: ["UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1", "Hex", "Base64"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
CJS_IO_FORMAT: ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"],
|
||||
|
||||
|
||||
/**
|
||||
* RC4 operation.
|
||||
*
|
||||
@@ -373,11 +485,11 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runRc4: function (input, args) {
|
||||
let message = Utils.format[args[1]].parse(input),
|
||||
passphrase = Utils.format[args[0].option].parse(args[0].string),
|
||||
let message = Cipher._format[args[1]].parse(input),
|
||||
passphrase = Cipher._format[args[0].option].parse(args[0].string),
|
||||
encrypted = CryptoJS.RC4.encrypt(message, passphrase);
|
||||
|
||||
return encrypted.ciphertext.toString(Utils.format[args[2]]);
|
||||
return encrypted.ciphertext.toString(Cipher._format[args[2]]);
|
||||
},
|
||||
|
||||
|
||||
@@ -395,12 +507,63 @@ const Cipher = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runRc4drop: function (input, args) {
|
||||
let message = Utils.format[args[1]].parse(input),
|
||||
passphrase = Utils.format[args[0].option].parse(args[0].string),
|
||||
let message = Cipher._format[args[1]].parse(input),
|
||||
passphrase = Cipher._format[args[0].option].parse(args[0].string),
|
||||
drop = args[3],
|
||||
encrypted = CryptoJS.RC4Drop.encrypt(message, passphrase, { drop: drop });
|
||||
|
||||
return encrypted.ciphertext.toString(Utils.format[args[2]]);
|
||||
return encrypted.ciphertext.toString(Cipher._format[args[2]]);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
PRNG_BYTES: 32,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
PRNG_OUTPUT: ["Hex", "Integer", "Byte array", "Raw"],
|
||||
|
||||
/**
|
||||
* Pseudo-Random Number Generator operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runPRNG: function(input, args) {
|
||||
const numBytes = args[0],
|
||||
outputAs = args[1];
|
||||
|
||||
let bytes;
|
||||
|
||||
if (ENVIRONMENT_IS_WORKER() && self.crypto) {
|
||||
bytes = self.crypto.getRandomValues(new Uint8Array(numBytes));
|
||||
bytes = Utils.arrayBufferToStr(bytes.buffer);
|
||||
} else {
|
||||
bytes = forge.random.getBytesSync(numBytes);
|
||||
}
|
||||
|
||||
let value = new BigNumber(0),
|
||||
i;
|
||||
|
||||
switch (outputAs) {
|
||||
case "Hex":
|
||||
return forge.util.bytesToHex(bytes);
|
||||
case "Integer":
|
||||
for (i = bytes.length - 1; i >= 0; i--) {
|
||||
value = value.times(256).plus(bytes.charCodeAt(i));
|
||||
}
|
||||
return value.toFixed();
|
||||
case "Byte array":
|
||||
return JSON.stringify(Utils.strToCharcode(bytes));
|
||||
case "Raw":
|
||||
default:
|
||||
return bytes;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -783,6 +946,23 @@ const Cipher = {
|
||||
return output;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* A mapping of string formats to their classes in the CryptoJS library.
|
||||
*
|
||||
* @private
|
||||
* @constant
|
||||
*/
|
||||
_format: {
|
||||
"Hex": CryptoJS.enc.Hex,
|
||||
"Base64": CryptoJS.enc.Base64,
|
||||
"UTF8": CryptoJS.enc.Utf8,
|
||||
"UTF16": CryptoJS.enc.Utf16,
|
||||
"UTF16LE": CryptoJS.enc.Utf16LE,
|
||||
"UTF16BE": CryptoJS.enc.Utf16BE,
|
||||
"Latin1": CryptoJS.enc.Latin1,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default Cipher;
|
||||
@@ -827,3 +1007,27 @@ CryptoJS.kdf.OpenSSL.execute = function (password, keySize, ivSize, salt) {
|
||||
// Return params
|
||||
return CryptoJS.lib.CipherParams.create({ key: key, iv: iv, salt: salt });
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Override for the CryptoJS Hex encoding parser to remove whitespace before attempting to parse
|
||||
* the hex string.
|
||||
*
|
||||
* @param {string} hexStr
|
||||
* @returns {CryptoJS.lib.WordArray}
|
||||
*/
|
||||
CryptoJS.enc.Hex.parse = function (hexStr) {
|
||||
// Remove whitespace
|
||||
hexStr = hexStr.replace(/\s/g, "");
|
||||
|
||||
// Shortcut
|
||||
const hexStrLength = hexStr.length;
|
||||
|
||||
// Convert
|
||||
const words = [];
|
||||
for (let i = 0; i < hexStrLength; i += 2) {
|
||||
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
|
||||
}
|
||||
|
||||
return new CryptoJS.lib.WordArray.init(words, hexStrLength / 2);
|
||||
};
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import {camelCase, kebabCase, snakeCase} from "lodash";
|
||||
|
||||
import Utils from "../Utils.js";
|
||||
import vkbeautify from "vkbeautify";
|
||||
import {DOMParser as dom} from "xmldom";
|
||||
import {DOMParser} from "xmldom";
|
||||
import xpath from "xpath";
|
||||
import jpath from "jsonpath";
|
||||
import prettyPrintOne from "imports-loader?window=>global!exports-loader?prettyPrintOne!google-code-prettify/bin/prettify.min.js";
|
||||
import nwmatcher from "nwmatcher";
|
||||
import hljs from "highlight.js";
|
||||
|
||||
|
||||
/**
|
||||
@@ -23,12 +22,7 @@ const Code = {
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
LANGUAGES: ["default-code", "default-markup", "bash", "bsh", "c", "cc", "coffee", "cpp", "cs", "csh", "cv", "cxx", "cyc", "htm", "html", "in.tag", "java", "javascript", "js", "json", "m", "mxml", "perl", "pl", "pm", "py", "python", "rb", "rc", "rs", "ruby", "rust", "sh", "uq.val", "xhtml", "xml", "xsl"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
LINE_NUMS: false,
|
||||
LANGUAGES: ["auto detect"].concat(hljs.listLanguages()),
|
||||
|
||||
/**
|
||||
* Syntax highlighter operation.
|
||||
@@ -38,9 +32,13 @@ const Code = {
|
||||
* @returns {html}
|
||||
*/
|
||||
runSyntaxHighlight: function(input, args) {
|
||||
let language = args[0],
|
||||
lineNums = args[1];
|
||||
return "<code class='prettyprint'>" + prettyPrintOne(Utils.escapeHtml(input), language, lineNums) + "</code>";
|
||||
const language = args[0];
|
||||
|
||||
if (language === "auto detect") {
|
||||
return hljs.highlightAuto(input).value;
|
||||
}
|
||||
|
||||
return hljs.highlight(language, input, true).value;
|
||||
},
|
||||
|
||||
|
||||
@@ -336,7 +334,7 @@ const Code = {
|
||||
|
||||
let doc;
|
||||
try {
|
||||
doc = new dom().parseFromString(input);
|
||||
doc = new DOMParser().parseFromString(input, "application/xml");
|
||||
} catch (err) {
|
||||
return "Invalid input XML.";
|
||||
}
|
||||
@@ -423,7 +421,7 @@ const Code = {
|
||||
let query = args[0],
|
||||
delimiter = args[1],
|
||||
parser = new DOMParser(),
|
||||
html,
|
||||
dom,
|
||||
result;
|
||||
|
||||
if (!query.length || !input.length) {
|
||||
@@ -431,32 +429,32 @@ const Code = {
|
||||
}
|
||||
|
||||
try {
|
||||
html = parser.parseFromString(input, "text/html");
|
||||
dom = parser.parseFromString(input);
|
||||
} catch (err) {
|
||||
return "Invalid input HTML.";
|
||||
}
|
||||
|
||||
try {
|
||||
result = html.querySelectorAll(query);
|
||||
const matcher = nwmatcher({document: dom});
|
||||
result = matcher.select(query, dom);
|
||||
} catch (err) {
|
||||
return "Invalid CSS Selector. Details:\n" + err.message;
|
||||
}
|
||||
|
||||
const nodeToString = function(node) {
|
||||
return node.toString();
|
||||
/* xmldom does not return the outerHTML value.
|
||||
switch (node.nodeType) {
|
||||
case Node.ELEMENT_NODE: return node.outerHTML;
|
||||
case Node.ATTRIBUTE_NODE: return node.value;
|
||||
case Node.COMMENT_NODE: return node.data;
|
||||
case Node.TEXT_NODE: return node.wholeText;
|
||||
case Node.DOCUMENT_NODE: return node.outerHTML;
|
||||
case node.ELEMENT_NODE: return node.outerHTML;
|
||||
case node.ATTRIBUTE_NODE: return node.value;
|
||||
case node.TEXT_NODE: return node.wholeText;
|
||||
case node.COMMENT_NODE: return node.data;
|
||||
case node.DOCUMENT_NODE: return node.outerHTML;
|
||||
default: throw new Error("Unknown Node Type: " + node.nodeType);
|
||||
}
|
||||
}*/
|
||||
};
|
||||
|
||||
return Array.apply(null, Array(result.length))
|
||||
.map(function(_, i) {
|
||||
return result[i];
|
||||
})
|
||||
return result
|
||||
.map(nodeToString)
|
||||
.join(delimiter);
|
||||
},
|
||||
@@ -485,12 +483,11 @@ const Code = {
|
||||
|
||||
|
||||
/**
|
||||
* Converts to snake_case.
|
||||
* To Snake Case operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
*/
|
||||
runToSnakeCase(input, args) {
|
||||
const smart = args[0];
|
||||
@@ -504,12 +501,11 @@ const Code = {
|
||||
|
||||
|
||||
/**
|
||||
* Converts to camelCase.
|
||||
* To Camel Case operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
*/
|
||||
runToCamelCase(input, args) {
|
||||
const smart = args[0];
|
||||
@@ -523,12 +519,11 @@ const Code = {
|
||||
|
||||
|
||||
/**
|
||||
* Converts to kebab-case.
|
||||
* To Kebab Case operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
*/
|
||||
runToKebabCase(input, args) {
|
||||
const smart = args[0];
|
||||
@@ -539,6 +534,7 @@ const Code = {
|
||||
return kebabCase(input);
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default Code;
|
||||
|
||||
@@ -418,9 +418,9 @@ const Compress = {
|
||||
}
|
||||
};
|
||||
|
||||
const fileSize = Utils.padLeft(input.length.toString(8), 11, "0");
|
||||
const fileSize = input.length.toString(8).padStart(11, "0");
|
||||
const currentUnixTimestamp = Math.floor(Date.now() / 1000);
|
||||
const lastModTime = Utils.padLeft(currentUnixTimestamp.toString(8), 11, "0");
|
||||
const lastModTime = currentUnixTimestamp.toString(8).padStart(11, "0");
|
||||
|
||||
const file = {
|
||||
fileName: Utils.padBytesRight(args[0], 100),
|
||||
@@ -452,7 +452,7 @@ const Compress = {
|
||||
}
|
||||
});
|
||||
}
|
||||
checksum = Utils.padBytesRight(Utils.padLeft(checksum.toString(8), 7, "0"), 8);
|
||||
checksum = Utils.padBytesRight(checksum.toString(8).padStart(7, "0"), 8);
|
||||
file.checksum = checksum;
|
||||
|
||||
const tarball = new Tarball();
|
||||
|
||||
@@ -60,17 +60,16 @@ const Convert = {
|
||||
/**
|
||||
* Convert distance operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runDistance: function (input, args) {
|
||||
let inputUnits = args[0],
|
||||
outputUnits = args[1];
|
||||
|
||||
input = input * Convert.DISTANCE_FACTOR[inputUnits];
|
||||
return input / Convert.DISTANCE_FACTOR[outputUnits];
|
||||
// TODO Remove rounding errors (e.g. 1.000000000001)
|
||||
input = input.times(Convert.DISTANCE_FACTOR[inputUnits]);
|
||||
return input.div(Convert.DISTANCE_FACTOR[outputUnits]);
|
||||
},
|
||||
|
||||
|
||||
@@ -141,16 +140,16 @@ const Convert = {
|
||||
/**
|
||||
* Convert data units operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runDataSize: function (input, args) {
|
||||
let inputUnits = args[0],
|
||||
outputUnits = args[1];
|
||||
|
||||
input = input * Convert.DATA_FACTOR[inputUnits];
|
||||
return input / Convert.DATA_FACTOR[outputUnits];
|
||||
input = input.times(Convert.DATA_FACTOR[inputUnits]);
|
||||
return input.div(Convert.DATA_FACTOR[outputUnits]);
|
||||
},
|
||||
|
||||
|
||||
@@ -221,16 +220,16 @@ const Convert = {
|
||||
/**
|
||||
* Convert area operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runArea: function (input, args) {
|
||||
let inputUnits = args[0],
|
||||
outputUnits = args[1];
|
||||
|
||||
input = input * Convert.AREA_FACTOR[inputUnits];
|
||||
return input / Convert.AREA_FACTOR[outputUnits];
|
||||
input = input.times(Convert.AREA_FACTOR[inputUnits]);
|
||||
return input.div(Convert.AREA_FACTOR[outputUnits]);
|
||||
},
|
||||
|
||||
|
||||
@@ -332,16 +331,16 @@ const Convert = {
|
||||
/**
|
||||
* Convert mass operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runMass: function (input, args) {
|
||||
let inputUnits = args[0],
|
||||
outputUnits = args[1];
|
||||
|
||||
input = input * Convert.MASS_FACTOR[inputUnits];
|
||||
return input / Convert.MASS_FACTOR[outputUnits];
|
||||
input = input.times(Convert.MASS_FACTOR[inputUnits]);
|
||||
return input.div(Convert.MASS_FACTOR[outputUnits]);
|
||||
},
|
||||
|
||||
|
||||
@@ -397,16 +396,16 @@ const Convert = {
|
||||
/**
|
||||
* Convert speed operation.
|
||||
*
|
||||
* @param {number} input
|
||||
* @param {BigNumber} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {BigNumber}
|
||||
*/
|
||||
runSpeed: function (input, args) {
|
||||
let inputUnits = args[0],
|
||||
outputUnits = args[1];
|
||||
|
||||
input = input * Convert.SPEED_FACTOR[inputUnits];
|
||||
return input / Convert.SPEED_FACTOR[outputUnits];
|
||||
input = input.times(Convert.SPEED_FACTOR[inputUnits]);
|
||||
return input.div(Convert.SPEED_FACTOR[outputUnits]);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import moment from "moment-timezone";
|
||||
|
||||
|
||||
/**
|
||||
* Date and time operations.
|
||||
*
|
||||
@@ -57,24 +60,29 @@ const DateTime = {
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
* @returns {string}
|
||||
*/
|
||||
runToUnixTimestamp: function(input, args) {
|
||||
let units = args[0],
|
||||
const units = args[0],
|
||||
treatAsUTC = args[1],
|
||||
showDateTime = args[2],
|
||||
d = treatAsUTC ? moment.utc(input) : moment(input);
|
||||
|
||||
let result = "";
|
||||
|
||||
if (units === "Seconds (s)") {
|
||||
return d.unix();
|
||||
result = d.unix();
|
||||
} else if (units === "Milliseconds (ms)") {
|
||||
return d.valueOf();
|
||||
result = d.valueOf();
|
||||
} else if (units === "Microseconds (μs)") {
|
||||
return d.valueOf() * 1000;
|
||||
result = d.valueOf() * 1000;
|
||||
} else if (units === "Nanoseconds (ns)") {
|
||||
return d.valueOf() * 1000000;
|
||||
result = d.valueOf() * 1000000;
|
||||
} else {
|
||||
throw "Unrecognised unit";
|
||||
}
|
||||
|
||||
return showDateTime ? `${result} (${d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss")} UTC)` : result.toString();
|
||||
},
|
||||
|
||||
|
||||
@@ -189,6 +197,20 @@ const DateTime = {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Sleep operation.
|
||||
*
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
runSleep: async function(input, args) {
|
||||
const ms = args[0];
|
||||
await new Promise(r => setTimeout(r, ms));
|
||||
return input;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
*/
|
||||
|
||||
@@ -81,22 +81,23 @@ const Entropy = {
|
||||
/**
|
||||
* Frequency distribution operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
runFreqDistrib: function (input, args) {
|
||||
if (!input.length) return "No data";
|
||||
const data = new Uint8Array(input);
|
||||
if (!data.length) return "No data";
|
||||
|
||||
let distrib = new Array(256).fill(0),
|
||||
percentages = new Array(256),
|
||||
len = input.length,
|
||||
len = data.length,
|
||||
showZeroes = args[0],
|
||||
i;
|
||||
|
||||
// Count bytes
|
||||
for (i = 0; i < len; i++) {
|
||||
distrib[input[i]]++;
|
||||
distrib[data[i]]++;
|
||||
}
|
||||
|
||||
// Calculate percentages
|
||||
@@ -126,7 +127,7 @@ const Entropy = {
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (distrib[i] || showZeroes) {
|
||||
output += " " + Utils.hex(i, 2) + " (" +
|
||||
Utils.padRight(percentages[i].toFixed(2).replace(".00", "") + "%)", 8) +
|
||||
(percentages[i].toFixed(2).replace(".00", "") + "%)").padEnd(8, " ") +
|
||||
Array(Math.ceil(percentages[i])+1).join("|") + "\n";
|
||||
}
|
||||
}
|
||||
@@ -135,6 +136,32 @@ const Entropy = {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Chi Square operation.
|
||||
*
|
||||
* @param {ArrayBuffer} data
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
runChiSq: function(input, args) {
|
||||
const data = new Uint8Array(input);
|
||||
let distArray = new Array(256).fill(0),
|
||||
total = 0;
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
distArray[data[i]]++;
|
||||
}
|
||||
|
||||
for (let i = 0; i < distArray.length; i++) {
|
||||
if (distArray[i] > 0) {
|
||||
total += Math.pow(distArray[i] - data.length / 256, 2) / (data.length / 256);
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the Shannon entropy for a given chunk of data.
|
||||
*
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import XRegExp from "xregexp";
|
||||
|
||||
|
||||
/**
|
||||
* Identifier extraction operations.
|
||||
*
|
||||
@@ -26,6 +29,11 @@ const Extract = {
|
||||
match;
|
||||
|
||||
while ((match = searchRegex.exec(input))) {
|
||||
// Moves pointer when an empty string is matched (prevents infinite loop)
|
||||
if (match.index === searchRegex.lastIndex) {
|
||||
searchRegex.lastIndex++;
|
||||
}
|
||||
|
||||
if (removeRegex && removeRegex.test(match[0]))
|
||||
continue;
|
||||
total++;
|
||||
@@ -43,7 +51,20 @@ const Extract = {
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
MIN_STRING_LEN: 3,
|
||||
MIN_STRING_LEN: 4,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
STRING_MATCH_TYPE: [
|
||||
"[ASCII]", "Alphanumeric + punctuation (A)", "All printable chars (A)", "Null-terminated strings (A)",
|
||||
"[Unicode]", "Alphanumeric + punctuation (U)", "All printable chars (U)", "Null-terminated strings (U)"
|
||||
],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
ENCODING_LIST: ["Single byte", "16-bit littleendian", "16-bit bigendian", "All"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
@@ -58,10 +79,59 @@ const Extract = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runStrings: function(input, args) {
|
||||
let minLen = args[0] || Extract.MIN_STRING_LEN,
|
||||
displayTotal = args[1],
|
||||
strings = "[A-Z\\d/\\-:.,_$%'\"()<>= !\\[\\]{}@]",
|
||||
regex = new RegExp(strings + "{" + minLen + ",}", "ig");
|
||||
const encoding = args[0],
|
||||
minLen = args[1],
|
||||
matchType = args[2],
|
||||
displayTotal = args[3],
|
||||
alphanumeric = "A-Z\\d",
|
||||
punctuation = "/\\-:.,_$%'\"()<>= !\\[\\]{}@",
|
||||
printable = "\x20-\x7e",
|
||||
uniAlphanumeric = "\\pL\\pN",
|
||||
uniPunctuation = "\\pP\\pZ",
|
||||
uniPrintable = "\\pL\\pM\\pZ\\pS\\pN\\pP";
|
||||
|
||||
let strings = "";
|
||||
|
||||
switch (matchType) {
|
||||
case "Alphanumeric + punctuation (A)":
|
||||
strings = `[${alphanumeric + punctuation}]`;
|
||||
break;
|
||||
case "All printable chars (A)":
|
||||
case "Null-terminated strings (A)":
|
||||
strings = `[${printable}]`;
|
||||
break;
|
||||
case "Alphanumeric + punctuation (U)":
|
||||
strings = `[${uniAlphanumeric + uniPunctuation}]`;
|
||||
break;
|
||||
case "All printable chars (U)":
|
||||
case "Null-terminated strings (U)":
|
||||
strings = `[${uniPrintable}]`;
|
||||
break;
|
||||
}
|
||||
|
||||
// UTF-16 support is hacked in by allowing null bytes on either side of the matched chars
|
||||
switch (encoding) {
|
||||
case "All":
|
||||
strings = `(\x00?${strings}\x00?)`;
|
||||
break;
|
||||
case "16-bit littleendian":
|
||||
strings = `(${strings}\x00)`;
|
||||
break;
|
||||
case "16-bit bigendian":
|
||||
strings = `(\x00${strings})`;
|
||||
break;
|
||||
case "Single byte":
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
strings = `${strings}{${minLen},}`;
|
||||
|
||||
if (matchType.includes("Null-terminated")) {
|
||||
strings += "\x00";
|
||||
}
|
||||
|
||||
const regex = new XRegExp(strings, "ig");
|
||||
|
||||
return Extract._search(input, regex, null, displayTotal);
|
||||
},
|
||||
@@ -137,7 +207,7 @@ const Extract = {
|
||||
*/
|
||||
runEmail: function(input, args) {
|
||||
let displayTotal = args[0],
|
||||
regex = /\w[-.\w]*@[-\w]+(?:\.[-\w]+)*\.[A-Z]{2,4}/ig;
|
||||
regex = /\b\w[-.\w]*@[-\w]+(?:\.[-\w]+)*\.[A-Z]{2,4}\b/ig;
|
||||
|
||||
return Extract._search(input, regex, null, displayTotal);
|
||||
},
|
||||
@@ -217,7 +287,7 @@ const Extract = {
|
||||
includeUnixPath = args[1],
|
||||
displayTotal = args[2],
|
||||
winDrive = "[A-Z]:\\\\",
|
||||
winName = "[A-Z\\d][A-Z\\d\\- '_\\(\\)]{0,61}",
|
||||
winName = "[A-Z\\d][A-Z\\d\\- '_\\(\\)~]{0,61}",
|
||||
winExt = "[A-Z\\d]{1,6}",
|
||||
winPath = winDrive + "(?:" + winName + "\\\\?)*" + winName +
|
||||
"(?:\\." + winExt + ")?",
|
||||
|
||||
@@ -15,12 +15,13 @@ const FileType = {
|
||||
/**
|
||||
* Detect File Type operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runDetect: function(input, args) {
|
||||
const type = FileType.magicType(input);
|
||||
const data = new Uint8Array(input),
|
||||
type = FileType.magicType(data);
|
||||
|
||||
if (!type) {
|
||||
return "Unknown file type. Have you tried checking the entropy of this data to determine whether it might be encrypted or compressed?";
|
||||
@@ -46,20 +47,21 @@ const FileType = {
|
||||
/**
|
||||
* Scan for Embedded Files operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runScanForEmbeddedFiles: function(input, args) {
|
||||
let output = "Scanning data for 'magic bytes' which may indicate embedded files. The following results may be false positives and should not be treat as reliable. Any suffiently long file is likely to contain these magic bytes coincidentally.\n",
|
||||
type,
|
||||
ignoreCommon = args[0],
|
||||
commonExts = ["ico", "ttf", ""],
|
||||
numFound = 0,
|
||||
numCommonFound = 0;
|
||||
const ignoreCommon = args[0],
|
||||
commonExts = ["ico", "ttf", ""],
|
||||
data = new Uint8Array(input);
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
type = FileType.magicType(input.slice(i));
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
type = FileType.magicType(data.slice(i));
|
||||
if (type) {
|
||||
if (ignoreCommon && commonExts.indexOf(type.ext) > -1) {
|
||||
numCommonFound++;
|
||||
@@ -96,7 +98,7 @@ const FileType = {
|
||||
* Given a buffer, detects magic byte sequences at specific positions and returns the
|
||||
* extension and mime type.
|
||||
*
|
||||
* @param {byteArray} buf
|
||||
* @param {Uint8Array} buf
|
||||
* @returns {Object} type
|
||||
* @returns {string} type.ext - File extension
|
||||
* @returns {string} type.mime - Mime type
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {BigInteger} from "jsbn";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
/**
|
||||
* Windows Filetime operations.
|
||||
@@ -35,27 +35,29 @@ const Filetime = {
|
||||
let units = args[0],
|
||||
format = args[1];
|
||||
|
||||
if (!input) return "";
|
||||
|
||||
if (format === "Hex") {
|
||||
input = new BigInteger(input, 16);
|
||||
input = new BigNumber(input, 16);
|
||||
} else {
|
||||
input = new BigInteger(input);
|
||||
input = new BigNumber(input);
|
||||
}
|
||||
|
||||
input = input.subtract(new BigInteger("116444736000000000"));
|
||||
input = input.minus(new BigNumber("116444736000000000"));
|
||||
|
||||
if (units === "Seconds (s)"){
|
||||
input = input.divide(new BigInteger("10000000"));
|
||||
input = input.dividedBy(new BigNumber("10000000"));
|
||||
} else if (units === "Milliseconds (ms)") {
|
||||
input = input.divide(new BigInteger("10000"));
|
||||
input = input.dividedBy(new BigNumber("10000"));
|
||||
} else if (units === "Microseconds (μs)") {
|
||||
input = input.divide(new BigInteger("10"));
|
||||
input = input.dividedBy(new BigNumber("10"));
|
||||
} else if (units === "Nanoseconds (ns)") {
|
||||
input = input.multiply(new BigInteger("100"));
|
||||
input = input.multipliedBy(new BigNumber("100"));
|
||||
} else {
|
||||
throw "Unrecognised unit";
|
||||
}
|
||||
|
||||
return input.toString();
|
||||
return input.toFixed();
|
||||
},
|
||||
|
||||
|
||||
@@ -71,26 +73,28 @@ const Filetime = {
|
||||
let units = args[0],
|
||||
format = args[1];
|
||||
|
||||
input = new BigInteger(input);
|
||||
if (!input) return "";
|
||||
|
||||
input = new BigNumber(input);
|
||||
|
||||
if (units === "Seconds (s)"){
|
||||
input = input.multiply(new BigInteger("10000000"));
|
||||
input = input.multipliedBy(new BigNumber("10000000"));
|
||||
} else if (units === "Milliseconds (ms)") {
|
||||
input = input.multiply(new BigInteger("10000"));
|
||||
input = input.multipliedBy(new BigNumber("10000"));
|
||||
} else if (units === "Microseconds (μs)") {
|
||||
input = input.multiply(new BigInteger("10"));
|
||||
input = input.multiplyiedBy(new BigNumber("10"));
|
||||
} else if (units === "Nanoseconds (ns)") {
|
||||
input = input.divide(new BigInteger("100"));
|
||||
input = input.dividedBy(new BigNumber("100"));
|
||||
} else {
|
||||
throw "Unrecognised unit";
|
||||
}
|
||||
|
||||
input = input.add(new BigInteger("116444736000000000"));
|
||||
input = input.plus(new BigNumber("116444736000000000"));
|
||||
|
||||
if (format === "Hex"){
|
||||
return input.toString(16);
|
||||
} else {
|
||||
return input.toString();
|
||||
return input.toFixed();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -215,9 +215,9 @@ const HTML = {
|
||||
k = k.toFixed(2);
|
||||
|
||||
let hex = "#" +
|
||||
Utils.padLeft(Math.round(r).toString(16), 2) +
|
||||
Utils.padLeft(Math.round(g).toString(16), 2) +
|
||||
Utils.padLeft(Math.round(b).toString(16), 2),
|
||||
Math.round(r).toString(16).padStart(2, "0") +
|
||||
Math.round(g).toString(16).padStart(2, "0") +
|
||||
Math.round(b).toString(16).padStart(2, "0"),
|
||||
rgb = "rgb(" + r + ", " + g + ", " + b + ")",
|
||||
rgba = "rgba(" + r + ", " + g + ", " + b + ", " + a + ")",
|
||||
hsl = "hsl(" + h + ", " + s + "%, " + l + "%)",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {UAS_parser as UAParser} from "../lib/uas_parser.js";
|
||||
import UAParser from "ua-parser-js";
|
||||
|
||||
|
||||
/**
|
||||
@@ -46,20 +46,22 @@ const HTTP = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runParseUserAgent: function(input, args) {
|
||||
const ua = UAParser.parse(input);
|
||||
|
||||
return "Type: " + ua.type + "\n" +
|
||||
"Family: " + ua.uaFamily + "\n" +
|
||||
"Name: " + ua.uaName + "\n" +
|
||||
"URL: " + ua.uaUrl + "\n" +
|
||||
"Company: " + ua.uaCompany + "\n" +
|
||||
"Company URL: " + ua.uaCompanyUrl + "\n\n" +
|
||||
"OS Family: " + ua.osFamily + "\n" +
|
||||
"OS Name: " + ua.osName + "\n" +
|
||||
"OS URL: " + ua.osUrl + "\n" +
|
||||
"OS Company: " + ua.osCompany + "\n" +
|
||||
"OS Company URL: " + ua.osCompanyUrl + "\n" +
|
||||
"Device Type: " + ua.deviceType + "\n";
|
||||
const ua = UAParser(input);
|
||||
return `Browser
|
||||
Name: ${ua.browser.name || "unknown"}
|
||||
Version: ${ua.browser.version || "unknown"}
|
||||
Device
|
||||
Model: ${ua.device.model || "unknown"}
|
||||
Type: ${ua.device.type || "unknown"}
|
||||
Vendor: ${ua.device.vendor || "unknown"}
|
||||
Engine
|
||||
Name: ${ua.engine.name || "unknown"}
|
||||
Version: ${ua.engine.version || "unknown"}
|
||||
OS
|
||||
Name: ${ua.os.name || "unknown"}
|
||||
Version: ${ua.os.version || "unknown"}
|
||||
CPU
|
||||
Architecture: ${ua.cpu.architecture || "unknown"}`;
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import Utils from "../Utils.js";
|
||||
import CryptoApi from "crypto-api";
|
||||
import CryptoApi from "babel-loader!crypto-api";
|
||||
import MD6 from "node-md6";
|
||||
import * as SHA3 from "js-sha3";
|
||||
import Checksum from "./Checksum.js";
|
||||
import ctph from "ctph.js";
|
||||
import ssdeep from "ssdeep.js";
|
||||
import bcrypt from "bcryptjs";
|
||||
import scrypt from "scryptsy";
|
||||
|
||||
|
||||
/**
|
||||
@@ -20,22 +24,22 @@ const Hash = {
|
||||
* Generic hash function.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object} [options={}]
|
||||
* @returns {string}
|
||||
*/
|
||||
runHash: function(name, input) {
|
||||
const hasher = CryptoApi.hasher(name);
|
||||
hasher.state.message = input;
|
||||
hasher.state.length += input.length;
|
||||
hasher.process();
|
||||
return hasher.finalize().stringify("hex");
|
||||
runHash: function(name, input, options={}) {
|
||||
const msg = Utils.arrayBufferToStr(input, false),
|
||||
hasher = CryptoApi.getHasher(name, options);
|
||||
hasher.update(msg);
|
||||
return CryptoApi.encoder.toHex(hasher.finalize());
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* MD2 operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -47,7 +51,7 @@ const Hash = {
|
||||
/**
|
||||
* MD4 operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -59,7 +63,7 @@ const Hash = {
|
||||
/**
|
||||
* MD5 operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -103,7 +107,7 @@ const Hash = {
|
||||
/**
|
||||
* SHA0 operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -115,7 +119,7 @@ const Hash = {
|
||||
/**
|
||||
* SHA1 operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -133,7 +137,7 @@ const Hash = {
|
||||
/**
|
||||
* SHA2 operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -152,7 +156,7 @@ const Hash = {
|
||||
/**
|
||||
* SHA3 operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -190,7 +194,7 @@ const Hash = {
|
||||
/**
|
||||
* Keccak operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -233,7 +237,7 @@ const Hash = {
|
||||
/**
|
||||
* Shake operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -269,7 +273,7 @@ const Hash = {
|
||||
/**
|
||||
* RIPEMD operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -282,7 +286,7 @@ const Hash = {
|
||||
/**
|
||||
* HAS-160 operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -300,7 +304,7 @@ const Hash = {
|
||||
/**
|
||||
* Whirlpool operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -324,14 +328,73 @@ const Hash = {
|
||||
/**
|
||||
* Snefru operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runSnefru: function (input, args) {
|
||||
const rounds = args[0],
|
||||
size = args[1];
|
||||
return Hash.runHash(`snefru-${rounds}-${size}`, input);
|
||||
return Hash.runHash("snefru", input, {
|
||||
rounds: args[0],
|
||||
length: args[1]
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* CTPH operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runCTPH: function (input, args) {
|
||||
return ctph.digest(input);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* SSDEEP operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runSSDEEP: function (input, args) {
|
||||
return ssdeep.digest(input);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
DELIM_OPTIONS: ["Line feed", "CRLF", "Space", "Comma"],
|
||||
|
||||
/**
|
||||
* Compare CTPH hashes operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {Number}
|
||||
*/
|
||||
runCompareCTPH: function (input, args) {
|
||||
const samples = input.split(Utils.charRep[args[0]]);
|
||||
if (samples.length !== 2) throw "Incorrect number of samples.";
|
||||
return ctph.similarity(samples[0], samples[1]);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Compare SSDEEP hashes operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {Number}
|
||||
*/
|
||||
runCompareSSDEEP: function (input, args) {
|
||||
const samples = input.split(Utils.charRep[args[0]]);
|
||||
if (samples.length !== 2) throw "Incorrect number of samples.";
|
||||
return ssdeep.similarity(samples[0], samples[1]);
|
||||
},
|
||||
|
||||
|
||||
@@ -358,71 +421,206 @@ const Hash = {
|
||||
"HAS160",
|
||||
"Whirlpool",
|
||||
"Whirlpool-0",
|
||||
"Whirlpool-T"
|
||||
"Whirlpool-T",
|
||||
"Snefru"
|
||||
],
|
||||
|
||||
/**
|
||||
* HMAC operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runHMAC: function (input, args) {
|
||||
const password = args[0],
|
||||
const key = args[0],
|
||||
hashFunc = args[1].toLowerCase(),
|
||||
hmac = CryptoApi.mac("hmac", password, hashFunc, {});
|
||||
msg = Utils.arrayBufferToStr(input, false),
|
||||
hasher = CryptoApi.getHasher(hashFunc);
|
||||
|
||||
hmac.update(input);
|
||||
return hmac.finalize().stringify("hex");
|
||||
// Horrible shim to fix constructor bug. Reported in nf404/crypto-api#8
|
||||
hasher.reset = () => {
|
||||
hasher.state = {};
|
||||
const tmp = new hasher.constructor();
|
||||
hasher.state = tmp.state;
|
||||
};
|
||||
|
||||
const mac = CryptoApi.getHmac(CryptoApi.encoder.fromUtf(key), hasher);
|
||||
mac.update(msg);
|
||||
return CryptoApi.encoder.toHex(mac.finalize());
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
BCRYPT_ROUNDS: 10,
|
||||
|
||||
/**
|
||||
* Bcrypt operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runBcrypt: async function (input, args) {
|
||||
const rounds = args[0];
|
||||
const salt = await bcrypt.genSalt(rounds);
|
||||
|
||||
return await bcrypt.hash(input, salt, null, p => {
|
||||
// Progress callback
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Bcrypt compare operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runBcryptCompare: async function (input, args) {
|
||||
const hash = args[0];
|
||||
|
||||
const match = await bcrypt.compare(input, hash, null, p => {
|
||||
// Progress callback
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
|
||||
});
|
||||
|
||||
return match ? "Match: " + input : "No match";
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Bcrypt parse operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runBcryptParse: async function (input, args) {
|
||||
try {
|
||||
return `Rounds: ${bcrypt.getRounds(input)}
|
||||
Salt: ${bcrypt.getSalt(input)}
|
||||
Password hash: ${input.split(bcrypt.getSalt(input))[1]}
|
||||
Full hash: ${input}`;
|
||||
} catch (err) {
|
||||
return "Error: " + err.toString();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
KEY_FORMAT: ["Hex", "Base64", "UTF8", "Latin1"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
SCRYPT_ITERATIONS: 16384,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
SCRYPT_MEM_FACTOR: 8,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
SCRYPT_PARALLEL_FACTOR: 1,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
SCRYPT_KEY_LENGTH: 64,
|
||||
|
||||
/**
|
||||
* Scrypt operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runScrypt: function (input, args) {
|
||||
const salt = Utils.convertToByteString(args[0].string || "", args[0].option),
|
||||
iterations = args[1],
|
||||
memFactor = args[2],
|
||||
parallelFactor = args[3],
|
||||
keyLength = args[4];
|
||||
|
||||
try {
|
||||
const data = scrypt(
|
||||
input, salt, iterations, memFactor, parallelFactor, keyLength,
|
||||
p => {
|
||||
// Progress callback
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage(`Progress: ${p.percent.toFixed(0)}%`);
|
||||
}
|
||||
);
|
||||
|
||||
return data.toString("hex");
|
||||
} catch (err) {
|
||||
return "Error: " + err.toString();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Generate all hashes operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runAll: function (input, args) {
|
||||
let byteArray = Utils.strToByteArray(input),
|
||||
output = "MD2: " + Hash.runMD2(input, []) +
|
||||
"\nMD4: " + Hash.runMD4(input, []) +
|
||||
"\nMD5: " + Hash.runMD5(input, []) +
|
||||
"\nMD6: " + Hash.runMD6(input, []) +
|
||||
"\nSHA0: " + Hash.runSHA0(input, []) +
|
||||
"\nSHA1: " + Hash.runSHA1(input, []) +
|
||||
"\nSHA2 224: " + Hash.runSHA2(input, ["224"]) +
|
||||
"\nSHA2 256: " + Hash.runSHA2(input, ["256"]) +
|
||||
"\nSHA2 384: " + Hash.runSHA2(input, ["384"]) +
|
||||
"\nSHA2 512: " + Hash.runSHA2(input, ["512"]) +
|
||||
"\nSHA3 224: " + Hash.runSHA3(input, ["224"]) +
|
||||
"\nSHA3 256: " + Hash.runSHA3(input, ["256"]) +
|
||||
"\nSHA3 384: " + Hash.runSHA3(input, ["384"]) +
|
||||
"\nSHA3 512: " + Hash.runSHA3(input, ["512"]) +
|
||||
"\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"]) +
|
||||
const arrayBuffer = input,
|
||||
str = Utils.arrayBufferToStr(arrayBuffer, false),
|
||||
byteArray = new Uint8Array(arrayBuffer),
|
||||
output = "MD2: " + Hash.runMD2(arrayBuffer, []) +
|
||||
"\nMD4: " + Hash.runMD4(arrayBuffer, []) +
|
||||
"\nMD5: " + Hash.runMD5(arrayBuffer, []) +
|
||||
"\nMD6: " + Hash.runMD6(str, []) +
|
||||
"\nSHA0: " + Hash.runSHA0(arrayBuffer, []) +
|
||||
"\nSHA1: " + Hash.runSHA1(arrayBuffer, []) +
|
||||
"\nSHA2 224: " + Hash.runSHA2(arrayBuffer, ["224"]) +
|
||||
"\nSHA2 256: " + Hash.runSHA2(arrayBuffer, ["256"]) +
|
||||
"\nSHA2 384: " + Hash.runSHA2(arrayBuffer, ["384"]) +
|
||||
"\nSHA2 512: " + Hash.runSHA2(arrayBuffer, ["512"]) +
|
||||
"\nSHA3 224: " + Hash.runSHA3(arrayBuffer, ["224"]) +
|
||||
"\nSHA3 256: " + Hash.runSHA3(arrayBuffer, ["256"]) +
|
||||
"\nSHA3 384: " + Hash.runSHA3(arrayBuffer, ["384"]) +
|
||||
"\nSHA3 512: " + Hash.runSHA3(arrayBuffer, ["512"]) +
|
||||
"\nKeccak 224: " + Hash.runKeccak(arrayBuffer, ["224"]) +
|
||||
"\nKeccak 256: " + Hash.runKeccak(arrayBuffer, ["256"]) +
|
||||
"\nKeccak 384: " + Hash.runKeccak(arrayBuffer, ["384"]) +
|
||||
"\nKeccak 512: " + Hash.runKeccak(arrayBuffer, ["512"]) +
|
||||
"\nShake 128: " + Hash.runShake(arrayBuffer, ["128", 256]) +
|
||||
"\nShake 256: " + Hash.runShake(arrayBuffer, ["256", 512]) +
|
||||
"\nRIPEMD-128: " + Hash.runRIPEMD(arrayBuffer, ["128"]) +
|
||||
"\nRIPEMD-160: " + Hash.runRIPEMD(arrayBuffer, ["160"]) +
|
||||
"\nRIPEMD-256: " + Hash.runRIPEMD(arrayBuffer, ["256"]) +
|
||||
"\nRIPEMD-320: " + Hash.runRIPEMD(arrayBuffer, ["320"]) +
|
||||
"\nHAS-160: " + Hash.runHAS(arrayBuffer, []) +
|
||||
"\nWhirlpool-0: " + Hash.runWhirlpool(arrayBuffer, ["Whirlpool-0"]) +
|
||||
"\nWhirlpool-T: " + Hash.runWhirlpool(arrayBuffer, ["Whirlpool-T"]) +
|
||||
"\nWhirlpool: " + Hash.runWhirlpool(arrayBuffer, ["Whirlpool"]) +
|
||||
"\nSSDEEP: " + Hash.runSSDEEP(str) +
|
||||
"\nCTPH: " + Hash.runCTPH(str) +
|
||||
"\n\nChecksums:" +
|
||||
"\nFletcher-8: " + Checksum.runFletcher8(byteArray, []) +
|
||||
"\nFletcher-16: " + Checksum.runFletcher16(byteArray, []) +
|
||||
"\nFletcher-32: " + Checksum.runFletcher32(byteArray, []) +
|
||||
"\nFletcher-64: " + Checksum.runFletcher64(byteArray, []) +
|
||||
"\nAdler-32: " + Checksum.runAdler32(byteArray, []) +
|
||||
"\nCRC-16: " + Checksum.runCRC16(input, []) +
|
||||
"\nCRC-32: " + Checksum.runCRC32(input, []);
|
||||
"\nCRC-16: " + Checksum.runCRC16(str, []) +
|
||||
"\nCRC-32: " + Checksum.runCRC32(str, []);
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
@@ -31,18 +31,19 @@ const Hexdump = {
|
||||
/**
|
||||
* To Hexdump operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runTo: function(input, args) {
|
||||
const data = new Uint8Array(input);
|
||||
const length = args[0] || Hexdump.WIDTH;
|
||||
const upperCase = args[1];
|
||||
const includeFinalLength = args[2];
|
||||
|
||||
let output = "", padding = 2;
|
||||
for (let i = 0; i < input.length; i += length) {
|
||||
const buff = input.slice(i, i+length);
|
||||
for (let i = 0; i < data.length; i += length) {
|
||||
const buff = data.slice(i, i+length);
|
||||
let hexa = "";
|
||||
for (let j = 0; j < buff.length; j++) {
|
||||
hexa += Utils.hex(buff[j], padding) + " ";
|
||||
@@ -56,10 +57,10 @@ const Hexdump = {
|
||||
}
|
||||
|
||||
output += lineNo + " " +
|
||||
Utils.padRight(hexa, (length*(padding+1))) +
|
||||
" |" + Utils.padRight(Utils.printable(Utils.byteArrayToChars(buff)), buff.length) + "|\n";
|
||||
hexa.padEnd(length*(padding+1), " ") +
|
||||
" |" + Utils.printable(Utils.byteArrayToChars(buff)).padEnd(buff.length, " ") + "|\n";
|
||||
|
||||
if (includeFinalLength && i+buff.length === input.length) {
|
||||
if (includeFinalLength && i+buff.length === data.length) {
|
||||
output += Utils.hex(i+buff.length, 8) + "\n";
|
||||
}
|
||||
}
|
||||
@@ -77,7 +78,7 @@ const Hexdump = {
|
||||
*/
|
||||
runFrom: function(input, args) {
|
||||
let output = [],
|
||||
regex = /^\s*(?:[\dA-F]{4,16}:?)?\s*((?:[\dA-F]{2}\s){1,8}(?:\s|[\dA-F]{2}-)(?:[\dA-F]{2}\s){1,8}|(?:[\dA-F]{2}\s|[\dA-F]{4}\s)+)/igm,
|
||||
regex = /^\s*(?:[\dA-F]{4,16}h?:?)?\s*((?:[\dA-F]{2}\s){1,8}(?:\s|[\dA-F]{2}-)(?:[\dA-F]{2}\s){1,8}|(?:[\dA-F]{2}\s|[\dA-F]{4}\s)+)/igm,
|
||||
block, line;
|
||||
|
||||
while ((block = regex.exec(input))) {
|
||||
|
||||
@@ -20,14 +20,13 @@ const Image = {
|
||||
*
|
||||
* Extracts EXIF data from a byteArray, representing a JPG or a TIFF image.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runExtractEXIF(input, args) {
|
||||
try {
|
||||
const bytes = Uint8Array.from(input);
|
||||
const parser = ExifParser.create(bytes.buffer);
|
||||
const parser = ExifParser.create(input);
|
||||
const result = parser.parse();
|
||||
|
||||
let lines = [];
|
||||
@@ -53,7 +52,7 @@ const Image = {
|
||||
* @author David Moodie [davidmoodie12@gmail.com]
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runRemoveEXIF(input, args) {
|
||||
// Do nothing if input is empty
|
||||
|
||||
@@ -34,6 +34,11 @@ const MAC = {
|
||||
* @default
|
||||
*/
|
||||
CISCO_STYLE: false,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
IPV6_INTERFACE_ID: false,
|
||||
|
||||
/**
|
||||
* Format MAC addresses operation.
|
||||
@@ -50,6 +55,7 @@ const MAC = {
|
||||
dashDelim = args[2],
|
||||
colonDelim = args[3],
|
||||
ciscoStyle = args[4],
|
||||
ipv6IntID = args[5],
|
||||
outputList = [],
|
||||
macs = input.toLowerCase().split(/[,\s\r\n]+/);
|
||||
|
||||
@@ -57,23 +63,32 @@ const MAC = {
|
||||
let cleanMac = mac.replace(/[:.-]+/g, ""),
|
||||
macHyphen = cleanMac.replace(/(.{2}(?=.))/g, "$1-"),
|
||||
macColon = cleanMac.replace(/(.{2}(?=.))/g, "$1:"),
|
||||
macCisco = cleanMac.replace(/(.{4}(?=.))/g, "$1.");
|
||||
macCisco = cleanMac.replace(/(.{4}(?=.))/g, "$1."),
|
||||
macIPv6 = cleanMac.slice(0, 6) + "fffe" + cleanMac.slice(6);
|
||||
|
||||
macIPv6 = macIPv6.replace(/(.{4}(?=.))/g, "$1:");
|
||||
let bite = parseInt(macIPv6.slice(0, 2), 16) ^ 2;
|
||||
bite = bite.toString(16).padStart(2, "0");
|
||||
macIPv6 = bite + macIPv6.slice(2);
|
||||
|
||||
if (outputCase === "Lower only") {
|
||||
if (noDelim) outputList.push(cleanMac);
|
||||
if (dashDelim) outputList.push(macHyphen);
|
||||
if (colonDelim) outputList.push(macColon);
|
||||
if (ciscoStyle) outputList.push(macCisco);
|
||||
if (ipv6IntID) outputList.push(macIPv6);
|
||||
} else if (outputCase === "Upper only") {
|
||||
if (noDelim) outputList.push(cleanMac.toUpperCase());
|
||||
if (dashDelim) outputList.push(macHyphen.toUpperCase());
|
||||
if (colonDelim) outputList.push(macColon.toUpperCase());
|
||||
if (ciscoStyle) outputList.push(macCisco.toUpperCase());
|
||||
if (ipv6IntID) outputList.push(macIPv6.toUpperCase());
|
||||
} else {
|
||||
if (noDelim) outputList.push(cleanMac, cleanMac.toUpperCase());
|
||||
if (dashDelim) outputList.push(macHyphen, macHyphen.toUpperCase());
|
||||
if (colonDelim) outputList.push(macColon, macColon.toUpperCase());
|
||||
if (ciscoStyle) outputList.push(macCisco, macCisco.toUpperCase());
|
||||
if (ipv6IntID) outputList.push(macIPv6, macIPv6.toUpperCase());
|
||||
}
|
||||
|
||||
outputList.push(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Microsoft operations.
|
||||
* Microsoft operations.
|
||||
*
|
||||
* @author bmwhitn [brian.m.whitney@outlook.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
|
||||
@@ -14,16 +14,72 @@ const Numberwang = {
|
||||
* @returns {string}
|
||||
*/
|
||||
run: function(input, args) {
|
||||
if (!input) return "Let's play Wangernumb!";
|
||||
const match = input.match(/\d+/);
|
||||
if (match) {
|
||||
return match[0] + "! That's Numberwang!";
|
||||
let output;
|
||||
if (!input) {
|
||||
output = "Let's play Wangernumb!";
|
||||
} else {
|
||||
// That's a bad miss!
|
||||
return "Sorry, that's not Numberwang. Let's rotate the board!";
|
||||
const match = input.match(/(f0rty-s1x|shinty-six|filth-hundred and neeb|-?√?\d+(\.\d+)?i?([a-z]?)%?)/i);
|
||||
if (match) {
|
||||
if (match[3]) output = match[0] + "! That's AlphaNumericWang!";
|
||||
else output = match[0] + "! That's Numberwang!";
|
||||
} else {
|
||||
// That's a bad miss!
|
||||
output = "Sorry, that's not Numberwang. Let's rotate the board!";
|
||||
}
|
||||
}
|
||||
|
||||
const rand = Math.floor(Math.random() * Numberwang._didYouKnow.length);
|
||||
return output + "\n\nDid you know: " + Numberwang._didYouKnow[rand];
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Taken from http://numberwang.wikia.com/wiki/Numberwang_Wikia
|
||||
*
|
||||
* @private
|
||||
* @constant
|
||||
*/
|
||||
_didYouKnow: [
|
||||
"Numberwang, contrary to popular belief, is a fruit and not a vegetable.",
|
||||
"Robert Webb once got WordWang while presenting an episode of Numberwang.",
|
||||
"The 6705th digit of pi is Numberwang.",
|
||||
"Numberwang was invented on a Sevenday.",
|
||||
"Contrary to popular belief, Albert Einstein always got good grades in Numberwang at school. He once scored ^4$ on a test.",
|
||||
"680 asteroids have been named after Numberwang.",
|
||||
"Archimedes is most famous for proclaiming \"That's Numberwang!\" during an epiphany about water displacement he had while taking a bath.",
|
||||
"Numberwang Day is celebrated in Japan on every day of the year apart from June 6.",
|
||||
"Biologists recently discovered Numberwang within a strand of human DNA.",
|
||||
"Numbernot is a special type of non-Numberwang number. It is divisible by 3 and the letter \"y\".",
|
||||
"Julie once got 612.04 Numberwangs in a single episode of Emmerdale.",
|
||||
"In India, it is traditional to shout out \"Numberwang!\" instead of checkmate during games of chess.",
|
||||
"There is a rule on Countdown which states that if you get Numberwang in the numbers round, you automatically win. It has only ever been invoked twice.",
|
||||
"\"Numberwang\" was the third-most common baby name for a brief period in 1722.",
|
||||
"\"The Lion King\" was loosely based on Numberwang.",
|
||||
"\"A Numberwang a day keeps the doctor away\" is how Donny Cosy, the oldest man in the world, explained how he was in such good health at the age of 136.",
|
||||
"The \"number lock\" button on a keyboard is based on the popular round of the same name in \"Numberwang\".",
|
||||
"Cambridge became the first university to offer a course in Numberwang in 1567.",
|
||||
"Schrödinger's Numberwang is a number that has been confusing dentists for centuries.",
|
||||
"\"Harry Potter and the Numberwang of Numberwang\" was rejected by publishers -41 times before it became a bestseller.",
|
||||
"\"Numberwang\" is the longest-running British game show in history; it has aired 226 seasons, each containing 19 episodes, which makes a grand total of 132 episodes.",
|
||||
"The triple Numberwang bonus was discovered by archaeologist Thomas Jefferson in Somerset.",
|
||||
"Numberwang is illegal in parts of Czechoslovakia.",
|
||||
"Numberwang was discovered in India in the 12th century.",
|
||||
"Numberwang has the chemical formula Zn4SO2(HgEs)3.",
|
||||
"The first pack of cards ever created featured two \"Numberwang\" cards instead of jokers.",
|
||||
"Julius Caesar was killed by an overdose of Numberwang.",
|
||||
"The most Numberwang musical note is G#.",
|
||||
"In 1934, the forty-third Google Doodle promoted the upcoming television show \"Numberwang on Ice\".",
|
||||
"A recent psychology study found that toddlers were 17% faster at identifying numbers which were Numberwang.",
|
||||
"There are 700 ways to commit a foul in the television show \"Numberwang\". All 700 of these fouls were committed by Julie in one single episode in 1473.",
|
||||
"Astronomers suspect God is Numberwang.",
|
||||
"Numberwang is the official beverage of Canada.",
|
||||
"In the pilot episode of \"The Price is Right\", if a contestant got the value of an item exactly right they were told \"That's Numberwang!\" and immediately won ₹5.7032.",
|
||||
"The first person to get three Numberwangs in a row was Madonna.",
|
||||
"\"Numberwang\" has the code U+46402 in Unicode.",
|
||||
"The musical note \"Numberwang\" is between D# and E♮.",
|
||||
"Numberwang was first played on the moon in 1834.",
|
||||
],
|
||||
|
||||
};
|
||||
|
||||
export default Numberwang;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import otp from "otp";
|
||||
import Base64 from "./Base64.js";
|
||||
|
||||
|
||||
/**
|
||||
* One-Time Password operations.
|
||||
*
|
||||
|
||||
364
src/core/operations/PGP.js
Executable file
364
src/core/operations/PGP.js
Executable file
@@ -0,0 +1,364 @@
|
||||
import * as kbpgp from "kbpgp";
|
||||
import {promisify} from "es6-promisify";
|
||||
|
||||
|
||||
/**
|
||||
* PGP operations.
|
||||
*
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @author Matt C [matt@artemisbot.uk]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
const PGP = {
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
KEY_TYPES: ["RSA-1024", "RSA-2048", "RSA-4096", "ECC-256", "ECC-384"],
|
||||
|
||||
|
||||
/**
|
||||
* Get size of subkey
|
||||
*
|
||||
* @private
|
||||
* @param {number} keySize
|
||||
* @returns {number}
|
||||
*/
|
||||
_getSubkeySize(keySize) {
|
||||
return {
|
||||
1024: 1024,
|
||||
2048: 1024,
|
||||
4096: 2048,
|
||||
256: 256,
|
||||
384: 256,
|
||||
}[keySize];
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Progress callback
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_ASP: new kbpgp.ASP({
|
||||
"progress_hook": info => {
|
||||
let msg = "";
|
||||
|
||||
switch (info.what) {
|
||||
case "guess":
|
||||
msg = "Guessing a prime";
|
||||
break;
|
||||
case "fermat":
|
||||
msg = "Factoring prime using Fermat's factorization method";
|
||||
break;
|
||||
case "mr":
|
||||
msg = "Performing Miller-Rabin primality test";
|
||||
break;
|
||||
case "passed_mr":
|
||||
msg = "Passed Miller-Rabin primality test";
|
||||
break;
|
||||
case "failed_mr":
|
||||
msg = "Failed Miller-Rabin primality test";
|
||||
break;
|
||||
case "found":
|
||||
msg = "Prime found";
|
||||
break;
|
||||
default:
|
||||
msg = `Stage: ${info.what}`;
|
||||
}
|
||||
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage(msg);
|
||||
}
|
||||
}),
|
||||
|
||||
|
||||
/**
|
||||
* Import private key and unlock if necessary
|
||||
*
|
||||
* @private
|
||||
* @param {string} privateKey
|
||||
* @param {string} [passphrase]
|
||||
* @returns {Object}
|
||||
*/
|
||||
async _importPrivateKey(privateKey, passphrase) {
|
||||
try {
|
||||
const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
||||
armored: privateKey,
|
||||
opts: {
|
||||
"no_check_keys": true
|
||||
}
|
||||
});
|
||||
if (key.is_pgp_locked()) {
|
||||
if (passphrase) {
|
||||
await promisify(key.unlock_pgp.bind(key))({
|
||||
passphrase
|
||||
});
|
||||
} else {
|
||||
throw "Did not provide passphrase with locked private key.";
|
||||
}
|
||||
}
|
||||
return key;
|
||||
} catch (err) {
|
||||
throw `Could not import private key: ${err}`;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Import public key
|
||||
*
|
||||
* @private
|
||||
* @param {string} publicKey
|
||||
* @returns {Object}
|
||||
*/
|
||||
async _importPublicKey (publicKey) {
|
||||
try {
|
||||
const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
||||
armored: publicKey,
|
||||
opts: {
|
||||
"no_check_keys": true
|
||||
}
|
||||
});
|
||||
return key;
|
||||
} catch (err) {
|
||||
throw `Could not import public key: ${err}`;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Generate PGP Key Pair operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runGenerateKeyPair(input, args) {
|
||||
let [keyType, keySize] = args[0].split("-"),
|
||||
password = args[1],
|
||||
name = args[2],
|
||||
email = args[3],
|
||||
userIdentifier = "";
|
||||
|
||||
if (name) userIdentifier += name;
|
||||
if (email) userIdentifier += ` <${email}>`;
|
||||
|
||||
let flags = kbpgp.const.openpgp.certify_keys;
|
||||
flags |= kbpgp.const.openpgp.sign_data;
|
||||
flags |= kbpgp.const.openpgp.auth;
|
||||
flags |= kbpgp.const.openpgp.encrypt_comm;
|
||||
flags |= kbpgp.const.openpgp.encrypt_storage;
|
||||
|
||||
let keyGenerationOptions = {
|
||||
userid: userIdentifier,
|
||||
ecc: keyType === "ecc",
|
||||
primary: {
|
||||
"nbits": keySize,
|
||||
"flags": flags,
|
||||
"expire_in": 0
|
||||
},
|
||||
subkeys: [{
|
||||
"nbits": PGP._getSubkeySize(keySize),
|
||||
"flags": kbpgp.const.openpgp.sign_data,
|
||||
"expire_in": 86400 * 365 * 8
|
||||
}, {
|
||||
"nbits": PGP._getSubkeySize(keySize),
|
||||
"flags": kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage,
|
||||
"expire_in": 86400 * 365 * 2
|
||||
}],
|
||||
asp: PGP._ASP
|
||||
};
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const unsignedKey = await promisify(kbpgp.KeyManager.generate)(keyGenerationOptions);
|
||||
await promisify(unsignedKey.sign.bind(unsignedKey))({});
|
||||
let signedKey = unsignedKey;
|
||||
let privateKeyExportOptions = {};
|
||||
if (password) privateKeyExportOptions.passphrase = password;
|
||||
const privateKey = await promisify(signedKey.export_pgp_private.bind(signedKey))(privateKeyExportOptions);
|
||||
const publicKey = await promisify(signedKey.export_pgp_public.bind(signedKey))({});
|
||||
resolve(privateKey + "\n" + publicKey.trim());
|
||||
} catch (err) {
|
||||
reject(`Error whilst generating key pair: ${err}`);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* PGP Encrypt operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async runEncrypt(input, args) {
|
||||
let plaintextMessage = input,
|
||||
plainPubKey = args[0],
|
||||
key,
|
||||
encryptedMessage;
|
||||
|
||||
if (!plainPubKey) return "Enter the public key of the recipient.";
|
||||
|
||||
try {
|
||||
key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
||||
armored: plainPubKey,
|
||||
});
|
||||
} catch (err) {
|
||||
throw `Could not import public key: ${err}`;
|
||||
}
|
||||
|
||||
try {
|
||||
encryptedMessage = await promisify(kbpgp.box)({
|
||||
"msg": plaintextMessage,
|
||||
"encrypt_for": key,
|
||||
"asp": PGP._ASP
|
||||
});
|
||||
} catch (err) {
|
||||
throw `Couldn't encrypt message with provided public key: ${err}`;
|
||||
}
|
||||
|
||||
return encryptedMessage.toString();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* PGP Decrypt operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async runDecrypt(input, args) {
|
||||
let encryptedMessage = input,
|
||||
privateKey = args[0],
|
||||
passphrase = args[1],
|
||||
keyring = new kbpgp.keyring.KeyRing(),
|
||||
plaintextMessage;
|
||||
|
||||
if (!privateKey) return "Enter the private key of the recipient.";
|
||||
|
||||
const key = await PGP._importPrivateKey(privateKey, passphrase);
|
||||
keyring.add_key_manager(key);
|
||||
|
||||
try {
|
||||
plaintextMessage = await promisify(kbpgp.unbox)({
|
||||
armored: encryptedMessage,
|
||||
keyfetch: keyring,
|
||||
asp: PGP._ASP
|
||||
});
|
||||
} catch (err) {
|
||||
throw `Couldn't decrypt message with provided private key: ${err}`;
|
||||
}
|
||||
|
||||
return plaintextMessage.toString();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* PGP Sign Message operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async runSign(input, args) {
|
||||
let message = input,
|
||||
privateKey = args[0],
|
||||
passphrase = args[1],
|
||||
publicKey = args[2],
|
||||
signedMessage;
|
||||
|
||||
if (!privateKey) return "Enter the private key of the signer.";
|
||||
if (!publicKey) return "Enter the public key of the recipient.";
|
||||
const privKey = await PGP._importPrivateKey(privateKey, passphrase);
|
||||
const pubKey = await PGP._importPublicKey(publicKey);
|
||||
|
||||
try {
|
||||
signedMessage = await promisify(kbpgp.box)({
|
||||
"msg": message,
|
||||
"encrypt_for": pubKey,
|
||||
"sign_with": privKey,
|
||||
"asp": PGP._ASP
|
||||
});
|
||||
} catch (err) {
|
||||
throw `Couldn't sign message: ${err}`;
|
||||
}
|
||||
|
||||
return signedMessage;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* PGP Verify Message operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async runVerify(input, args) {
|
||||
let signedMessage = input,
|
||||
publicKey = args[0],
|
||||
privateKey = args[1],
|
||||
passphrase = args[2],
|
||||
keyring = new kbpgp.keyring.KeyRing(),
|
||||
unboxedLiterals;
|
||||
|
||||
if (!publicKey) return "Enter the public key of the signer.";
|
||||
if (!privateKey) return "Enter the private key of the recipient.";
|
||||
const privKey = await PGP._importPrivateKey(privateKey, passphrase);
|
||||
const pubKey = await PGP._importPublicKey(publicKey);
|
||||
keyring.add_key_manager(privKey);
|
||||
keyring.add_key_manager(pubKey);
|
||||
|
||||
try {
|
||||
unboxedLiterals = await promisify(kbpgp.unbox)({
|
||||
armored: signedMessage,
|
||||
keyfetch: keyring,
|
||||
asp: PGP._ASP
|
||||
});
|
||||
const ds = unboxedLiterals[0].get_data_signer();
|
||||
if (ds) {
|
||||
const km = ds.get_key_manager();
|
||||
if (km) {
|
||||
const signer = km.get_userids_mark_primary()[0].components;
|
||||
let text = "Signed by ";
|
||||
if (signer.email || signer.username || signer.comment) {
|
||||
if (signer.username) {
|
||||
text += `${signer.username} `;
|
||||
}
|
||||
if (signer.comment) {
|
||||
text += `${signer.comment} `;
|
||||
}
|
||||
if (signer.email) {
|
||||
text += `<${signer.email}>`;
|
||||
}
|
||||
text += "\n";
|
||||
}
|
||||
text += [
|
||||
`PGP fingerprint: ${km.get_pgp_fingerprint().toString("hex")}`,
|
||||
`Signed on ${new Date(ds.sig.hashed_subpackets[0].time * 1000).toUTCString()}`,
|
||||
"----------------------------------\n"
|
||||
].join("\n");
|
||||
text += unboxedLiterals.toString();
|
||||
return text.trim();
|
||||
} else {
|
||||
return "Could not identify a key manager.";
|
||||
}
|
||||
} else {
|
||||
return "The data does not appear to be signed.";
|
||||
}
|
||||
} catch (err) {
|
||||
return `Couldn't verify message: ${err}`;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default PGP;
|
||||
@@ -121,8 +121,7 @@ const PublicKey = {
|
||||
// Format Public Key fields
|
||||
for (let i = 0; i < pkFields.length; i++) {
|
||||
pkStr += " " + pkFields[i].key + ":" +
|
||||
Utils.padLeft(
|
||||
pkFields[i].value + "\n",
|
||||
(pkFields[i].value + "\n").padStart(
|
||||
18 - (pkFields[i].key.length + 3) + pkFields[i].value.length + 1,
|
||||
" "
|
||||
);
|
||||
@@ -286,9 +285,9 @@ ${extensions}`;
|
||||
|
||||
key = fields[i].split("=")[0];
|
||||
value = fields[i].split("=")[1];
|
||||
str = Utils.padRight(key, maxKeyLen) + " = " + value + "\n";
|
||||
str = key.padEnd(maxKeyLen, " ") + " = " + value + "\n";
|
||||
|
||||
output += Utils.padLeft(str, indent + str.length, " ");
|
||||
output += str.padStart(indent + str.length, " ");
|
||||
}
|
||||
|
||||
return output.slice(0, -1);
|
||||
@@ -314,7 +313,7 @@ ${extensions}`;
|
||||
if (i === 0) {
|
||||
output += str;
|
||||
} else {
|
||||
output += Utils.padLeft(str, indent + str.length, " ");
|
||||
output += str.padStart(indent + str.length, " ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
278
src/core/operations/Regex.js
Normal file
278
src/core/operations/Regex.js
Normal file
@@ -0,0 +1,278 @@
|
||||
import XRegExp from "xregexp";
|
||||
import Utils from "../Utils.js";
|
||||
|
||||
|
||||
/**
|
||||
* Regex operations.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
const Regex = {
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
REGEX_PRE_POPULATE: [
|
||||
{
|
||||
name: "User defined",
|
||||
value: ""
|
||||
},
|
||||
{
|
||||
name: "IPv4 address",
|
||||
value: "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?"
|
||||
},
|
||||
{
|
||||
name: "IPv6 address",
|
||||
value: "((?=.*::)(?!.*::.+::)(::)?([\\dA-Fa-f]{1,4}:(:|\\b)|){5}|([\\dA-Fa-f]{1,4}:){6})((([\\dA-Fa-f]{1,4}((?!\\3)::|:\\b|(?![\\dA-Fa-f])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})"
|
||||
},
|
||||
{
|
||||
name: "Email address",
|
||||
value: "\\b(\\w[-.\\w]*)@([-\\w]+(?:\\.[-\\w]+)*)\\.([A-Za-z]{2,4})\\b"
|
||||
},
|
||||
{
|
||||
name: "URL",
|
||||
value: "([A-Za-z]+://)([-\\w]+(?:\\.\\w[-\\w]*)+)(:\\d+)?(/[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]*(?:[.!,?]+[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]+)*)?"
|
||||
},
|
||||
{
|
||||
name: "Domain",
|
||||
value: "\\b((?=[a-z0-9-]{1,63}\\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,63}\\b"
|
||||
},
|
||||
{
|
||||
name: "Windows file path",
|
||||
value: "([A-Za-z]):\\\\((?:[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)~]{0,61}\\\\?)*[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)]{0,61})(\\.[A-Za-z\\d]{1,6})?"
|
||||
},
|
||||
{
|
||||
name: "UNIX file path",
|
||||
value: "(?:/[A-Za-z\\d.][A-Za-z\\d\\-.]{0,61})+"
|
||||
},
|
||||
{
|
||||
name: "MAC address",
|
||||
value: "[A-Fa-f\\d]{2}(?:[:-][A-Fa-f\\d]{2}){5}"
|
||||
},
|
||||
{
|
||||
name: "Date (yyyy-mm-dd)",
|
||||
value: "((?:19|20)\\d\\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])"
|
||||
},
|
||||
{
|
||||
name: "Date (dd/mm/yyyy)",
|
||||
value: "(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.]((?:19|20)\\d\\d)"
|
||||
},
|
||||
{
|
||||
name: "Date (mm/dd/yyyy)",
|
||||
value: "(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.]((?:19|20)\\d\\d)"
|
||||
},
|
||||
{
|
||||
name: "Strings",
|
||||
value: "[A-Za-z\\d/\\-:.,_$%\\x27\"()<>= !\\[\\]{}@]{4,}"
|
||||
},
|
||||
],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
OUTPUT_FORMAT: ["Highlight matches", "List matches", "List capture groups", "List matches with capture groups"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
DISPLAY_TOTAL: false,
|
||||
|
||||
/**
|
||||
* Regular expression operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
runRegex: function(input, args) {
|
||||
const userRegex = args[1],
|
||||
i = args[2],
|
||||
m = args[3],
|
||||
s = args[4],
|
||||
u = args[5],
|
||||
a = args[6],
|
||||
displayTotal = args[7],
|
||||
outputFormat = args[8];
|
||||
let modifiers = "g";
|
||||
|
||||
if (i) modifiers += "i";
|
||||
if (m) modifiers += "m";
|
||||
if (s) modifiers += "s";
|
||||
if (u) modifiers += "u";
|
||||
if (a) modifiers += "A";
|
||||
|
||||
if (userRegex && userRegex !== "^" && userRegex !== "$") {
|
||||
try {
|
||||
const regex = new XRegExp(userRegex, modifiers);
|
||||
|
||||
switch (outputFormat) {
|
||||
case "Highlight matches":
|
||||
return Regex._regexHighlight(input, regex, displayTotal);
|
||||
case "List matches":
|
||||
return Utils.escapeHtml(Regex._regexList(input, regex, displayTotal, true, false));
|
||||
case "List capture groups":
|
||||
return Utils.escapeHtml(Regex._regexList(input, regex, displayTotal, false, true));
|
||||
case "List matches with capture groups":
|
||||
return Utils.escapeHtml(Regex._regexList(input, regex, displayTotal, true, true));
|
||||
default:
|
||||
return "Error: Invalid output format";
|
||||
}
|
||||
} catch (err) {
|
||||
return "Invalid regex. Details: " + err.message;
|
||||
}
|
||||
} else {
|
||||
return Utils.escapeHtml(input);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
SEARCH_TYPE: ["Regex", "Extended (\\n, \\t, \\x...)", "Simple string"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
FIND_REPLACE_GLOBAL: true,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
FIND_REPLACE_CASE: false,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
FIND_REPLACE_MULTILINE: true,
|
||||
|
||||
/**
|
||||
* Find / Replace operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runFindReplace: function(input, args) {
|
||||
let find = args[0].string,
|
||||
type = args[0].option,
|
||||
replace = args[1],
|
||||
g = args[2],
|
||||
i = args[3],
|
||||
m = args[4],
|
||||
modifiers = "";
|
||||
|
||||
if (g) modifiers += "g";
|
||||
if (i) modifiers += "i";
|
||||
if (m) modifiers += "m";
|
||||
|
||||
if (type === "Regex") {
|
||||
find = new RegExp(find, modifiers);
|
||||
return input.replace(find, replace);
|
||||
}
|
||||
|
||||
if (type.indexOf("Extended") === 0) {
|
||||
find = Utils.parseEscapedChars(find);
|
||||
}
|
||||
|
||||
find = new RegExp(Utils.escapeRegex(find), modifiers);
|
||||
|
||||
return input.replace(find, replace);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Adds HTML highlights to matches within a string.
|
||||
*
|
||||
* @private
|
||||
* @param {string} input
|
||||
* @param {RegExp} regex
|
||||
* @param {boolean} displayTotal
|
||||
* @returns {string}
|
||||
*/
|
||||
_regexHighlight: function(input, regex, displayTotal) {
|
||||
let output = "",
|
||||
m,
|
||||
hl = 1,
|
||||
i = 0,
|
||||
total = 0;
|
||||
|
||||
while ((m = regex.exec(input))) {
|
||||
// Moves pointer when an empty string is matched (prevents infinite loop)
|
||||
if (m.index === regex.lastIndex) {
|
||||
regex.lastIndex++;
|
||||
}
|
||||
|
||||
// Add up to match
|
||||
output += Utils.escapeHtml(input.slice(i, m.index));
|
||||
|
||||
// Add match with highlighting
|
||||
output += "<span class='hl"+hl+"'>" + Utils.escapeHtml(m[0]) + "</span>";
|
||||
|
||||
// Switch highlight
|
||||
hl = hl === 1 ? 2 : 1;
|
||||
|
||||
i = regex.lastIndex;
|
||||
total++;
|
||||
}
|
||||
|
||||
// Add all after final match
|
||||
output += Utils.escapeHtml(input.slice(i, input.length));
|
||||
|
||||
if (displayTotal)
|
||||
output = "Total found: " + total + "\n\n" + output;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Creates a string listing the matches within a string.
|
||||
*
|
||||
* @private
|
||||
* @param {string} input
|
||||
* @param {RegExp} regex
|
||||
* @param {boolean} displayTotal
|
||||
* @param {boolean} matches - Display full match
|
||||
* @param {boolean} captureGroups - Display each of the capture groups separately
|
||||
* @returns {string}
|
||||
*/
|
||||
_regexList: function(input, regex, displayTotal, matches, captureGroups) {
|
||||
let output = "",
|
||||
total = 0,
|
||||
match;
|
||||
|
||||
while ((match = regex.exec(input))) {
|
||||
// Moves pointer when an empty string is matched (prevents infinite loop)
|
||||
if (match.index === regex.lastIndex) {
|
||||
regex.lastIndex++;
|
||||
}
|
||||
|
||||
total++;
|
||||
if (matches) {
|
||||
output += match[0] + "\n";
|
||||
}
|
||||
if (captureGroups) {
|
||||
for (let i = 1; i < match.length; i++) {
|
||||
if (matches) {
|
||||
output += " Group " + i + ": ";
|
||||
}
|
||||
output += match[i] + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displayTotal)
|
||||
output = "Total found: " + total + "\n\n" + output;
|
||||
|
||||
return output.slice(0, -1);
|
||||
},
|
||||
};
|
||||
|
||||
export default Regex;
|
||||
@@ -158,7 +158,7 @@ const SeqUtils = {
|
||||
width = lines.length.toString().length;
|
||||
|
||||
for (let n = 0; n < lines.length; n++) {
|
||||
output += Utils.pad((n+1).toString(), width, " ") + " " + lines[n] + "\n";
|
||||
output += (n+1).toString().padStart(width, " ") + " " + lines[n] + "\n";
|
||||
}
|
||||
return output.slice(0, output.length-1);
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Utils from "../Utils.js";
|
||||
import jsesc from "jsesc";
|
||||
|
||||
|
||||
/**
|
||||
@@ -12,128 +13,6 @@ import Utils from "../Utils.js";
|
||||
*/
|
||||
const StrUtils = {
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
REGEX_PRE_POPULATE: [
|
||||
{
|
||||
name: "User defined",
|
||||
value: ""
|
||||
},
|
||||
{
|
||||
name: "IPv4 address",
|
||||
value: "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?"
|
||||
},
|
||||
{
|
||||
name: "IPv6 address",
|
||||
value: "((?=.*::)(?!.*::.+::)(::)?([\\dA-Fa-f]{1,4}:(:|\\b)|){5}|([\\dA-Fa-f]{1,4}:){6})((([\\dA-Fa-f]{1,4}((?!\\3)::|:\\b|(?![\\dA-Fa-f])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})"
|
||||
},
|
||||
{
|
||||
name: "Email address",
|
||||
value: "(\\w[-.\\w]*)@([-\\w]+(?:\\.[-\\w]+)*)\\.([A-Za-z]{2,4})"
|
||||
},
|
||||
{
|
||||
name: "URL",
|
||||
value: "([A-Za-z]+://)([-\\w]+(?:\\.\\w[-\\w]*)+)(:\\d+)?(/[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]*(?:[.!,?]+[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]+)*)?"
|
||||
},
|
||||
{
|
||||
name: "Domain",
|
||||
value: "\\b((?=[a-z0-9-]{1,63}\\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,63}\\b"
|
||||
},
|
||||
{
|
||||
name: "Windows file path",
|
||||
value: "([A-Za-z]):\\\\((?:[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)]{0,61}\\\\?)*[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)]{0,61})(\\.[A-Za-z\\d]{1,6})?"
|
||||
},
|
||||
{
|
||||
name: "UNIX file path",
|
||||
value: "(?:/[A-Za-z\\d.][A-Za-z\\d\\-.]{0,61})+"
|
||||
},
|
||||
{
|
||||
name: "MAC address",
|
||||
value: "[A-Fa-f\\d]{2}(?:[:-][A-Fa-f\\d]{2}){5}"
|
||||
},
|
||||
{
|
||||
name: "Date (yyyy-mm-dd)",
|
||||
value: "((?:19|20)\\d\\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])"
|
||||
},
|
||||
{
|
||||
name: "Date (dd/mm/yyyy)",
|
||||
value: "(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.]((?:19|20)\\d\\d)"
|
||||
},
|
||||
{
|
||||
name: "Date (mm/dd/yyyy)",
|
||||
value: "(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.]((?:19|20)\\d\\d)"
|
||||
},
|
||||
{
|
||||
name: "Strings",
|
||||
value: "[A-Za-z\\d/\\-:.,_$%\\x27\"()<>= !\\[\\]{}@]{4,}"
|
||||
},
|
||||
],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
REGEX_CASE_INSENSITIVE: true,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
REGEX_MULTILINE_MATCHING: true,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
OUTPUT_FORMAT: ["Highlight matches", "List matches", "List capture groups", "List matches with capture groups"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
DISPLAY_TOTAL: false,
|
||||
|
||||
/**
|
||||
* Regular expression operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
runRegex: function(input, args) {
|
||||
let userRegex = args[1],
|
||||
i = args[2],
|
||||
m = args[3],
|
||||
displayTotal = args[4],
|
||||
outputFormat = args[5],
|
||||
modifiers = "g";
|
||||
|
||||
if (i) modifiers += "i";
|
||||
if (m) modifiers += "m";
|
||||
|
||||
if (userRegex && userRegex !== "^" && userRegex !== "$") {
|
||||
try {
|
||||
const regex = new RegExp(userRegex, modifiers);
|
||||
|
||||
switch (outputFormat) {
|
||||
case "Highlight matches":
|
||||
return StrUtils._regexHighlight(input, regex, displayTotal);
|
||||
case "List matches":
|
||||
return Utils.escapeHtml(StrUtils._regexList(input, regex, displayTotal, true, false));
|
||||
case "List capture groups":
|
||||
return Utils.escapeHtml(StrUtils._regexList(input, regex, displayTotal, false, true));
|
||||
case "List matches with capture groups":
|
||||
return Utils.escapeHtml(StrUtils._regexList(input, regex, displayTotal, true, true));
|
||||
default:
|
||||
return "Error: Invalid output format";
|
||||
}
|
||||
} catch (err) {
|
||||
return "Invalid regex. Details: " + err.message;
|
||||
}
|
||||
} else {
|
||||
return Utils.escapeHtml(input);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
@@ -187,68 +66,28 @@ const StrUtils = {
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
SEARCH_TYPE: ["Regex", "Extended (\\n, \\t, \\x...)", "Simple string"],
|
||||
SPLIT_DELIM_OPTIONS: [
|
||||
{name: "Comma", value: ","},
|
||||
{name: "Space", value: " "},
|
||||
{name: "Line feed", value: "\\n"},
|
||||
{name: "CRLF", value: "\\r\\n"},
|
||||
{name: "Semi-colon", value: ";"},
|
||||
{name: "Colon", value: ":"},
|
||||
{name: "Nothing (separate chars)", value: ""}
|
||||
],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
FIND_REPLACE_GLOBAL: true,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
FIND_REPLACE_CASE: false,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
FIND_REPLACE_MULTILINE: true,
|
||||
|
||||
/**
|
||||
* Find / Replace operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runFindReplace: function(input, args) {
|
||||
let find = args[0].string,
|
||||
type = args[0].option,
|
||||
replace = args[1],
|
||||
g = args[2],
|
||||
i = args[3],
|
||||
m = args[4],
|
||||
modifiers = "";
|
||||
|
||||
if (g) modifiers += "g";
|
||||
if (i) modifiers += "i";
|
||||
if (m) modifiers += "m";
|
||||
|
||||
if (type === "Regex") {
|
||||
find = new RegExp(find, modifiers);
|
||||
return input.replace(find, replace);
|
||||
}
|
||||
|
||||
if (type.indexOf("Extended") === 0) {
|
||||
find = Utils.parseEscapedChars(find);
|
||||
}
|
||||
|
||||
find = new RegExp(Utils.escapeRegex(find), modifiers);
|
||||
|
||||
return input.replace(find, replace);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
SPLIT_DELIM: ",",
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
DELIMITER_OPTIONS: ["Line feed", "CRLF", "Space", "Comma", "Semi-colon", "Colon", "Nothing (separate chars)"],
|
||||
JOIN_DELIM_OPTIONS: [
|
||||
{name: "Line feed", value: "\\n"},
|
||||
{name: "CRLF", value: "\\r\\n"},
|
||||
{name: "Space", value: " "},
|
||||
{name: "Comma", value: ","},
|
||||
{name: "Semi-colon", value: ";"},
|
||||
{name: "Colon", value: ":"},
|
||||
{name: "Nothing (join chars)", value: ""}
|
||||
],
|
||||
|
||||
/**
|
||||
* Split operation.
|
||||
@@ -258,14 +97,20 @@ const StrUtils = {
|
||||
* @returns {string}
|
||||
*/
|
||||
runSplit: function(input, args) {
|
||||
let splitDelim = args[0] || StrUtils.SPLIT_DELIM,
|
||||
joinDelim = Utils.charRep[args[1]],
|
||||
let splitDelim = args[0],
|
||||
joinDelim = args[1],
|
||||
sections = input.split(splitDelim);
|
||||
|
||||
return sections.join(joinDelim);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
DELIMITER_OPTIONS: ["Line feed", "CRLF", "Space", "Comma", "Semi-colon", "Colon", "Nothing (separate chars)"],
|
||||
|
||||
/**
|
||||
* Filter operation.
|
||||
*
|
||||
@@ -375,35 +220,45 @@ const StrUtils = {
|
||||
* @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"},
|
||||
],
|
||||
QUOTE_TYPES: ["Single", "Double", "Backtick"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
ESCAPE_LEVEL: ["Special chars", "Everything", "Minimal"],
|
||||
|
||||
/**
|
||||
* Escape string operation.
|
||||
*
|
||||
* @author Vel0x [dalemy@microsoft.com]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
* StrUtils.runUnescape("Don't do that", [])
|
||||
* StrUtils.runEscape("Don't do that", [])
|
||||
* > "Don\'t do that"
|
||||
* StrUtils.runUnescape(`Hello
|
||||
* StrUtils.runEscape(`Hello
|
||||
* World`, [])
|
||||
* > "Hello\nWorld"
|
||||
*/
|
||||
runEscape: function(input, args) {
|
||||
return StrUtils._replaceByKeys(input, "unescaped", "escaped");
|
||||
const level = args[0],
|
||||
quotes = args[1],
|
||||
jsonCompat = args[2],
|
||||
es6Compat = args[3],
|
||||
lowercaseHex = !args[4];
|
||||
|
||||
return jsesc(input, {
|
||||
quotes: quotes.toLowerCase(),
|
||||
es6: es6Compat,
|
||||
escapeEverything: level === "Everything",
|
||||
minimal: level === "Minimal",
|
||||
json: jsonCompat,
|
||||
lowercaseHex: lowercaseHex,
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -411,6 +266,7 @@ const StrUtils = {
|
||||
* Unescape string operation.
|
||||
*
|
||||
* @author Vel0x [dalemy@microsoft.com]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
@@ -424,32 +280,7 @@ const StrUtils = {
|
||||
* 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;
|
||||
return Utils.parseEscapedChars(input);
|
||||
},
|
||||
|
||||
|
||||
@@ -510,80 +341,71 @@ const StrUtils = {
|
||||
|
||||
|
||||
/**
|
||||
* Adds HTML highlights to matches within a string.
|
||||
*
|
||||
* @private
|
||||
* @param {string} input
|
||||
* @param {RegExp} regex
|
||||
* @param {boolean} displayTotal
|
||||
* @returns {string}
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
_regexHighlight: function(input, regex, displayTotal) {
|
||||
let output = "",
|
||||
m,
|
||||
hl = 1,
|
||||
i = 0,
|
||||
total = 0;
|
||||
|
||||
while ((m = regex.exec(input))) {
|
||||
// Add up to match
|
||||
output += Utils.escapeHtml(input.slice(i, m.index));
|
||||
|
||||
// Add match with highlighting
|
||||
output += "<span class='hl"+hl+"'>" + Utils.escapeHtml(m[0]) + "</span>";
|
||||
|
||||
// Switch highlight
|
||||
hl = hl === 1 ? 2 : 1;
|
||||
|
||||
i = regex.lastIndex;
|
||||
total++;
|
||||
}
|
||||
|
||||
// Add all after final match
|
||||
output += Utils.escapeHtml(input.slice(i, input.length));
|
||||
|
||||
if (displayTotal)
|
||||
output = "Total found: " + total + "\n\n" + output;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
HAMMING_DELIM: "\\n\\n",
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
HAMMING_INPUT_TYPE: ["Raw string", "Hex"],
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
HAMMING_UNIT: ["Byte", "Bit"],
|
||||
|
||||
/**
|
||||
* Creates a string listing the matches within a string.
|
||||
* Hamming Distance operation.
|
||||
*
|
||||
* @author GCHQ Contributor [2]
|
||||
*
|
||||
* @private
|
||||
* @param {string} input
|
||||
* @param {RegExp} regex
|
||||
* @param {boolean} displayTotal
|
||||
* @param {boolean} matches - Display full match
|
||||
* @param {boolean} captureGroups - Display each of the capture groups separately
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
_regexList: function(input, regex, displayTotal, matches, captureGroups) {
|
||||
let output = "",
|
||||
total = 0,
|
||||
match;
|
||||
runHamming: function(input, args) {
|
||||
const delim = args[0],
|
||||
byByte = args[1] === "Byte",
|
||||
inputType = args[2],
|
||||
samples = input.split(delim);
|
||||
|
||||
while ((match = regex.exec(input))) {
|
||||
total++;
|
||||
if (matches) {
|
||||
output += match[0] + "\n";
|
||||
}
|
||||
if (captureGroups) {
|
||||
for (let i = 1; i < match.length; i++) {
|
||||
if (matches) {
|
||||
output += " Group " + i + ": ";
|
||||
}
|
||||
output += match[i] + "\n";
|
||||
if (samples.length !== 2) {
|
||||
return "Error: You can only calculae the edit distance between 2 strings. Please ensure exactly two inputs are provided, separated by the specified delimiter.";
|
||||
}
|
||||
|
||||
if (samples[0].length !== samples[1].length) {
|
||||
return "Error: Both inputs must be of the same length.";
|
||||
}
|
||||
|
||||
if (inputType === "Hex") {
|
||||
samples[0] = Utils.fromHex(samples[0]);
|
||||
samples[1] = Utils.fromHex(samples[1]);
|
||||
} else {
|
||||
samples[0] = Utils.strToByteArray(samples[0]);
|
||||
samples[1] = Utils.strToByteArray(samples[1]);
|
||||
}
|
||||
|
||||
let dist = 0;
|
||||
|
||||
for (let i = 0; i < samples[0].length; i++) {
|
||||
const lhs = samples[0][i],
|
||||
rhs = samples[1][i];
|
||||
|
||||
if (byByte && lhs !== rhs) {
|
||||
dist++;
|
||||
} else if (!byByte) {
|
||||
let xord = lhs ^ rhs;
|
||||
|
||||
while (xord) {
|
||||
dist++;
|
||||
xord &= xord - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displayTotal)
|
||||
output = "Total found: " + total + "\n\n" + output;
|
||||
|
||||
return output;
|
||||
return dist.toString();
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import Utils from "../Utils.js";
|
||||
|
||||
|
||||
/**
|
||||
* Tidy operations.
|
||||
*
|
||||
@@ -104,32 +101,39 @@ const Tidy = {
|
||||
/**
|
||||
* Drop bytes operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
runDropBytes: function(input, args) {
|
||||
let start = args[0],
|
||||
const start = args[0],
|
||||
length = args[1],
|
||||
applyToEachLine = args[2];
|
||||
|
||||
if (start < 0 || length < 0)
|
||||
throw "Error: Invalid value";
|
||||
|
||||
if (!applyToEachLine)
|
||||
return input.slice(0, start).concat(input.slice(start+length, input.length));
|
||||
if (!applyToEachLine) {
|
||||
const left = input.slice(0, start),
|
||||
right = input.slice(start + length, input.byteLength);
|
||||
let result = new Uint8Array(left.byteLength + right.byteLength);
|
||||
result.set(new Uint8Array(left), 0);
|
||||
result.set(new Uint8Array(right), left.byteLength);
|
||||
return result.buffer;
|
||||
}
|
||||
|
||||
// Split input into lines
|
||||
const data = new Uint8Array(input);
|
||||
let lines = [],
|
||||
line = [],
|
||||
i;
|
||||
|
||||
for (i = 0; i < input.length; i++) {
|
||||
if (input[i] === 0x0a) {
|
||||
for (i = 0; i < data.length; i++) {
|
||||
if (data[i] === 0x0a) {
|
||||
lines.push(line);
|
||||
line = [];
|
||||
} else {
|
||||
line.push(input[i]);
|
||||
line.push(data[i]);
|
||||
}
|
||||
}
|
||||
lines.push(line);
|
||||
@@ -139,7 +143,7 @@ const Tidy = {
|
||||
output = output.concat(lines[i].slice(0, start).concat(lines[i].slice(start+length, lines[i].length)));
|
||||
output.push(0x0a);
|
||||
}
|
||||
return output.slice(0, output.length-1);
|
||||
return new Uint8Array(output.slice(0, output.length-1)).buffer;
|
||||
},
|
||||
|
||||
|
||||
@@ -157,12 +161,12 @@ const Tidy = {
|
||||
/**
|
||||
* Take bytes operation.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
runTakeBytes: function(input, args) {
|
||||
let start = args[0],
|
||||
const start = args[0],
|
||||
length = args[1],
|
||||
applyToEachLine = args[2];
|
||||
|
||||
@@ -173,16 +177,17 @@ const Tidy = {
|
||||
return input.slice(start, start+length);
|
||||
|
||||
// Split input into lines
|
||||
const data = new Uint8Array(input);
|
||||
let lines = [],
|
||||
line = [];
|
||||
let i;
|
||||
line = [],
|
||||
i;
|
||||
|
||||
for (i = 0; i < input.length; i++) {
|
||||
if (input[i] === 0x0a) {
|
||||
for (i = 0; i < data.length; i++) {
|
||||
if (data[i] === 0x0a) {
|
||||
lines.push(line);
|
||||
line = [];
|
||||
} else {
|
||||
line.push(input[i]);
|
||||
line.push(data[i]);
|
||||
}
|
||||
}
|
||||
lines.push(line);
|
||||
@@ -192,7 +197,7 @@ const Tidy = {
|
||||
output = output.concat(lines[i].slice(start, start+length));
|
||||
output.push(0x0a);
|
||||
}
|
||||
return output.slice(0, output.length-1);
|
||||
return new Uint8Array(output.slice(0, output.length-1)).buffer;
|
||||
},
|
||||
|
||||
|
||||
@@ -229,11 +234,11 @@ const Tidy = {
|
||||
|
||||
if (position === "Start") {
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
output += Utils.padLeft(lines[i], lines[i].length+len, chr) + "\n";
|
||||
output += lines[i].padStart(lines[i].length+len, chr) + "\n";
|
||||
}
|
||||
} else if (position === "End") {
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
output += Utils.padRight(lines[i], lines[i].length+len, chr) + "\n";
|
||||
output += lines[i].padEnd(lines[i].length+len, chr) + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
164
src/core/operations/ToTable.js
Executable file
164
src/core/operations/ToTable.js
Executable file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* ToTable operations.
|
||||
*
|
||||
* @author Mark Jones [github.com/justanothermark]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
import Utils from "../Utils.js";
|
||||
|
||||
const ToTable = {
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
FORMATS: [
|
||||
"ASCII",
|
||||
"HTML"
|
||||
],
|
||||
|
||||
|
||||
/**
|
||||
* To Table operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
runToTable: function (input, args) {
|
||||
const [cellDelims, rowDelims, firstRowHeader, format] = args;
|
||||
|
||||
// Process the input into a nested array of elements.
|
||||
const tableData = Utils.parseCSV(input, cellDelims.split(""), rowDelims.split(""));
|
||||
|
||||
if (!tableData.length) return "";
|
||||
|
||||
// Render the data in the requested format.
|
||||
switch (format) {
|
||||
case "ASCII":
|
||||
return asciiOutput(tableData);
|
||||
case "HTML":
|
||||
default:
|
||||
return htmlOutput(tableData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs an array of data as an ASCII table.
|
||||
*
|
||||
* @param {string[][]} tableData
|
||||
* @returns {string}
|
||||
*/
|
||||
function asciiOutput(tableData) {
|
||||
const horizontalBorder = "-";
|
||||
const verticalBorder = "|";
|
||||
const crossBorder = "+";
|
||||
|
||||
let output = "";
|
||||
let longestCells = [];
|
||||
|
||||
// Find longestCells value per column to pad cells equally.
|
||||
tableData.forEach(function(row, index) {
|
||||
row.forEach(function(cell, cellIndex) {
|
||||
if (longestCells[cellIndex] === undefined || cell.length > longestCells[cellIndex]) {
|
||||
longestCells[cellIndex] = cell.length;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Add the top border of the table to the output.
|
||||
output += outputHorizontalBorder(longestCells);
|
||||
|
||||
// If the first row is a header, remove the row from the data and
|
||||
// add it to the output with another horizontal border.
|
||||
if (firstRowHeader) {
|
||||
let row = tableData.shift();
|
||||
output += outputRow(row, longestCells);
|
||||
output += outputHorizontalBorder(longestCells);
|
||||
}
|
||||
|
||||
// Add the rest of the table rows.
|
||||
tableData.forEach(function(row, index) {
|
||||
output += outputRow(row, longestCells);
|
||||
});
|
||||
|
||||
// Close the table with a final horizontal border.
|
||||
output += outputHorizontalBorder(longestCells);
|
||||
|
||||
return output;
|
||||
|
||||
/**
|
||||
* Outputs a row of correctly padded cells.
|
||||
*/
|
||||
function outputRow(row, longestCells) {
|
||||
let rowOutput = verticalBorder;
|
||||
row.forEach(function(cell, index) {
|
||||
rowOutput += " " + cell + " ".repeat(longestCells[index] - cell.length) + " " + verticalBorder;
|
||||
});
|
||||
rowOutput += "\n";
|
||||
return rowOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a horizontal border with a different character where
|
||||
* the horizontal border meets a vertical border.
|
||||
*/
|
||||
function outputHorizontalBorder(longestCells) {
|
||||
let rowOutput = crossBorder;
|
||||
longestCells.forEach(function(cellLength) {
|
||||
rowOutput += horizontalBorder.repeat(cellLength + 2) + crossBorder;
|
||||
});
|
||||
rowOutput += "\n";
|
||||
return rowOutput;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a table of data as a HTML table.
|
||||
*
|
||||
* @param {string[][]} tableData
|
||||
* @returns {string}
|
||||
*/
|
||||
function htmlOutput(tableData) {
|
||||
// Start the HTML output with suitable classes for styling.
|
||||
let output = "<table class='table table-hover table-condensed table-bordered table-nonfluid'>";
|
||||
|
||||
// If the first row is a header then put it in <thead> with <th> cells.
|
||||
if (firstRowHeader) {
|
||||
let row = tableData.shift();
|
||||
output += "<thead>";
|
||||
output += outputRow(row, "th");
|
||||
output += "</thead>";
|
||||
}
|
||||
|
||||
// Output the rest of the rows in the <tbody>.
|
||||
output += "<tbody>";
|
||||
tableData.forEach(function(row, index) {
|
||||
output += outputRow(row, "td");
|
||||
});
|
||||
|
||||
// Close the body and table elements.
|
||||
output += "</tbody></table>";
|
||||
return output;
|
||||
|
||||
/**
|
||||
* Outputs a table row.
|
||||
*
|
||||
* @param {string[]} row
|
||||
* @param {string} cellType
|
||||
*/
|
||||
function outputRow(row, cellType) {
|
||||
let output = "<tr>";
|
||||
row.forEach(function(cell) {
|
||||
output += "<" + cellType + ">" + cell + "</" + cellType + ">";
|
||||
});
|
||||
output += "</tr>";
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default ToTable;
|
||||
@@ -1,5 +1,4 @@
|
||||
/* globals unescape */
|
||||
import Utils from "../Utils.js";
|
||||
import url from "url";
|
||||
|
||||
|
||||
@@ -78,7 +77,7 @@ const URL_ = {
|
||||
|
||||
output += "Arguments:\n";
|
||||
for (let key in uri.query) {
|
||||
output += "\t" + Utils.padRight(key, padding);
|
||||
output += "\t" + key.padEnd(padding, " ");
|
||||
if (uri.query[key].length) {
|
||||
output += " = " + uri.query[key] + "\n";
|
||||
} else {
|
||||
|
||||
@@ -27,7 +27,7 @@ const Unicode = {
|
||||
*/
|
||||
runUnescape: function(input, args) {
|
||||
let prefix = Unicode._prefixToRegex[args[0]],
|
||||
regex = new RegExp(prefix+"([a-f\\d]{4,6})", "ig"),
|
||||
regex = new RegExp(prefix+"([a-f\\d]{4})", "ig"),
|
||||
output = "",
|
||||
m,
|
||||
i = 0;
|
||||
@@ -50,6 +50,40 @@ const Unicode = {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Escape Unicode Characters operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runEscape: function(input, args) {
|
||||
const regexWhitelist = /[ -~]/i,
|
||||
prefix = args[0],
|
||||
encodeAll = args[1],
|
||||
padding = args[2],
|
||||
uppercaseHex = args[3];
|
||||
|
||||
let output = "",
|
||||
character = "";
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
character = input[i];
|
||||
if (!encodeAll && regexWhitelist.test(character)) {
|
||||
// It’s a printable ASCII character so don’t escape it.
|
||||
output += character;
|
||||
continue;
|
||||
}
|
||||
|
||||
let cp = character.codePointAt(0).toString(16);
|
||||
if (uppercaseHex) cp = cp.toUpperCase();
|
||||
output += prefix + cp.padStart(padding, "0");
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Lookup table to add prefixes to unicode delimiters so that they can be used in a regex.
|
||||
*
|
||||
|
||||
26
src/core/operations/XKCD.js
Executable file
26
src/core/operations/XKCD.js
Executable file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* XKCD operations.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
const XKCD = {
|
||||
|
||||
/**
|
||||
* XKCD Random Number operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
runRandomNumber: function(input, args) {
|
||||
return 4; // chosen by fair dice roll.
|
||||
// guaranteed to be random.
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default XKCD;
|
||||
@@ -49,9 +49,11 @@ App.prototype.setup = function() {
|
||||
this.manager.setup();
|
||||
this.resetLayout();
|
||||
this.setCompileMessage();
|
||||
this.loadURIParams();
|
||||
|
||||
log.debug("App loaded");
|
||||
this.appLoaded = true;
|
||||
|
||||
this.loadURIParams();
|
||||
this.loaded();
|
||||
};
|
||||
|
||||
@@ -80,6 +82,9 @@ App.prototype.loaded = function() {
|
||||
// Clear the loading message interval
|
||||
clearInterval(window.loadingMsgsInt);
|
||||
|
||||
// Remove the loading error handler
|
||||
window.removeEventListener("error", window.loadingErrorHandler);
|
||||
|
||||
document.dispatchEvent(this.manager.apploaded);
|
||||
};
|
||||
|
||||
@@ -91,7 +96,7 @@ App.prototype.loaded = function() {
|
||||
* @param {boolean} [logToConsole=false]
|
||||
*/
|
||||
App.prototype.handleError = function(err, logToConsole) {
|
||||
if (logToConsole) console.error(err);
|
||||
if (logToConsole) log.error(err);
|
||||
const msg = err.displayStr || err.toString();
|
||||
this.alert(msg, "danger", this.options.errorTimeout, !this.options.showErrors);
|
||||
};
|
||||
@@ -129,6 +134,7 @@ App.prototype.autoBake = function() {
|
||||
if (this.autoBakePause) return false;
|
||||
|
||||
if (this.autoBake_ && !this.baking) {
|
||||
log.debug("Auto-baking");
|
||||
this.bake();
|
||||
} else {
|
||||
this.manager.controls.showStaleIndicator();
|
||||
@@ -374,6 +380,7 @@ App.prototype.loadURIParams = function() {
|
||||
window.location.href.split("#")[1] ||
|
||||
window.location.hash;
|
||||
this.uriParams = Utils.parseURIParams(params);
|
||||
this.autoBakePause = true;
|
||||
|
||||
// Read in recipe from URI params
|
||||
if (this.uriParams.recipe) {
|
||||
@@ -384,35 +391,29 @@ App.prototype.loadURIParams = function() {
|
||||
} else if (this.uriParams.op) {
|
||||
// If there's no recipe, look for single operations
|
||||
this.manager.recipe.clearRecipe();
|
||||
try {
|
||||
this.manager.recipe.addOperation(this.uriParams.op);
|
||||
} catch (err) {
|
||||
// If no exact match, search for nearest match and add that
|
||||
const matchedOps = this.manager.ops.filterOperations(this.uriParams.op, false);
|
||||
if (matchedOps.length) {
|
||||
this.manager.recipe.addOperation(matchedOps[0].name);
|
||||
}
|
||||
|
||||
// Populate search with the string
|
||||
const search = document.getElementById("search");
|
||||
|
||||
search.value = this.uriParams.op;
|
||||
search.dispatchEvent(new Event("search"));
|
||||
// Search for nearest match and add it
|
||||
const matchedOps = this.manager.ops.filterOperations(this.uriParams.op, false);
|
||||
if (matchedOps.length) {
|
||||
this.manager.recipe.addOperation(matchedOps[0].name);
|
||||
}
|
||||
|
||||
// Populate search with the string
|
||||
const search = document.getElementById("search");
|
||||
|
||||
search.value = this.uriParams.op;
|
||||
search.dispatchEvent(new Event("search"));
|
||||
}
|
||||
|
||||
// Read in input data from URI params
|
||||
if (this.uriParams.input) {
|
||||
this.autoBakePause = true;
|
||||
try {
|
||||
const inputData = Utils.fromBase64(this.uriParams.input);
|
||||
this.setInput(inputData);
|
||||
} catch (err) {
|
||||
} finally {
|
||||
this.autoBakePause = false;
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
this.autoBakePause = false;
|
||||
this.autoBake();
|
||||
};
|
||||
|
||||
@@ -440,6 +441,7 @@ App.prototype.getRecipeConfig = function() {
|
||||
/**
|
||||
* Given a recipe configuration, sets the recipe to that configuration.
|
||||
*
|
||||
* @fires Manager#statechange
|
||||
* @param {Object[]} recipeConfig - The recipe configuration
|
||||
*/
|
||||
App.prototype.setRecipeConfig = function(recipeConfig) {
|
||||
@@ -529,7 +531,7 @@ App.prototype.setCompileMessage = function() {
|
||||
|
||||
/**
|
||||
* Determines whether the browser supports Local Storage and if it is accessible.
|
||||
*
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
App.prototype.isLocalStorageAvailable = function() {
|
||||
@@ -569,7 +571,7 @@ App.prototype.isLocalStorageAvailable = function() {
|
||||
App.prototype.alert = function(str, style, timeout, silent) {
|
||||
const time = new Date();
|
||||
|
||||
console.log("[" + time.toLocaleString() + "] " + str);
|
||||
log.info("[" + time.toLocaleString() + "] " + str);
|
||||
if (silent) return;
|
||||
|
||||
style = style || "danger";
|
||||
|
||||
@@ -382,10 +382,13 @@ ControlsWaiter.prototype.supportButtonClick = function(e) {
|
||||
const saveLink = this.generateStateUrl(true, true, null, "https://gchq.github.io/CyberChef/");
|
||||
|
||||
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";
|
||||
reportBugInfo.innerHTML = `* Version: ${PKG_VERSION + (typeof INLINE === "undefined" ? "" : "s")}
|
||||
* Compile time: ${COMPILE_TIME}
|
||||
* User-Agent:
|
||||
${navigator.userAgent}
|
||||
* [Link to reproduce](${saveLink})
|
||||
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -110,6 +110,15 @@ HTMLOperation.prototype.highlightSearchString = function(searchStr, namePos, des
|
||||
}
|
||||
|
||||
if (this.description && descPos >= 0) {
|
||||
// Find HTML tag offsets
|
||||
const re = /<[^>]+>/g;
|
||||
let match;
|
||||
while ((match = re.exec(this.description))) {
|
||||
// If the search string occurs within an HTML tag, return without highlighting it.
|
||||
if (descPos >= match.index && descPos <= (match.index + match[0].length))
|
||||
return;
|
||||
}
|
||||
|
||||
this.description = this.description.slice(0, descPos) + "<b><u>" +
|
||||
this.description.slice(descPos, descPos + searchStr.length) + "</u></b>" +
|
||||
this.description.slice(descPos + searchStr.length);
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import Utils from "../core/Utils.js";
|
||||
|
||||
|
||||
/**
|
||||
* Waiter to handle events related to highlighting in CyberChef.
|
||||
*
|
||||
@@ -312,9 +309,9 @@ HighlighterWaiter.prototype.outputHtmlMousemove = function(e) {
|
||||
HighlighterWaiter.prototype.selectionInfo = function(start, end) {
|
||||
const len = end.toString().length;
|
||||
const width = len < 2 ? 2 : len;
|
||||
const startStr = Utils.pad(start.toString(), width, " ").replace(/ /g, " ");
|
||||
const endStr = Utils.pad(end.toString(), width, " ").replace(/ /g, " ");
|
||||
const lenStr = Utils.pad((end-start).toString(), width, " ").replace(/ /g, " ");
|
||||
const startStr = start.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
const endStr = end.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
const lenStr = (end-start).toString().padStart(width, " ").replace(/ /g, " ");
|
||||
|
||||
return "start: " + startStr + "<br>end: " + endStr + "<br>length: " + lenStr;
|
||||
};
|
||||
@@ -402,7 +399,7 @@ HighlighterWaiter.prototype.highlight = function(textarea, highlighter, pos) {
|
||||
|
||||
// Check if there is a carriage return in the output dish as this will not
|
||||
// be displayed by the HTML textarea and will mess up highlighting offsets.
|
||||
if (!this.app.dishStr || this.app.dishStr.indexOf("\r") >= 0) return false;
|
||||
if (this.manager.output.containsCR()) return false;
|
||||
|
||||
const startPlaceholder = "[startHighlight]";
|
||||
const startPlaceholderRegex = /\[startHighlight\]/g;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import LoaderWorker from "worker-loader?inline&fallback=false!./LoaderWorker.js";
|
||||
import Utils from "../core/Utils.js";
|
||||
|
||||
|
||||
@@ -33,6 +34,9 @@ const InputWaiter = function(app, manager) {
|
||||
144, //Num
|
||||
145, //Scroll
|
||||
];
|
||||
|
||||
this.loaderWorker = null;
|
||||
this.fileBuffer = null;
|
||||
};
|
||||
|
||||
|
||||
@@ -42,20 +46,53 @@ const InputWaiter = function(app, manager) {
|
||||
* @returns {string}
|
||||
*/
|
||||
InputWaiter.prototype.get = function() {
|
||||
return document.getElementById("input-text").value;
|
||||
return this.fileBuffer || document.getElementById("input-text").value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the input in the input textarea.
|
||||
* Sets the input in the input area.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {string|File} input
|
||||
*
|
||||
* @fires Manager#statechange
|
||||
*/
|
||||
InputWaiter.prototype.set = function(input) {
|
||||
document.getElementById("input-text").value = input;
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
const inputText = document.getElementById("input-text");
|
||||
if (input instanceof File) {
|
||||
this.setFile(input);
|
||||
inputText.value = "";
|
||||
this.setInputInfo(input.size, null);
|
||||
} else {
|
||||
inputText.value = input;
|
||||
this.closeFile();
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
const lines = input.length < (this.app.options.ioDisplayThreshold * 1024) ?
|
||||
input.count("\n") + 1 : null;
|
||||
this.setInputInfo(input.length, lines);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shows file details.
|
||||
*
|
||||
* @param {File} file
|
||||
*/
|
||||
InputWaiter.prototype.setFile = function(file) {
|
||||
// Display file overlay in input area with details
|
||||
const fileOverlay = document.getElementById("input-file"),
|
||||
fileName = document.getElementById("input-file-name"),
|
||||
fileSize = document.getElementById("input-file-size"),
|
||||
fileType = document.getElementById("input-file-type"),
|
||||
fileLoaded = document.getElementById("input-file-loaded");
|
||||
|
||||
this.fileBuffer = new ArrayBuffer();
|
||||
fileOverlay.style.display = "block";
|
||||
fileName.textContent = file.name;
|
||||
fileSize.textContent = file.size.toLocaleString() + " bytes";
|
||||
fileType.textContent = file.type || "unknown";
|
||||
fileLoaded.textContent = "0%";
|
||||
};
|
||||
|
||||
|
||||
@@ -69,22 +106,29 @@ InputWaiter.prototype.setInputInfo = function(length, lines) {
|
||||
let width = length.toString().length;
|
||||
width = width < 2 ? 2 : width;
|
||||
|
||||
const lengthStr = Utils.pad(length.toString(), width, " ").replace(/ /g, " ");
|
||||
const linesStr = Utils.pad(lines.toString(), width, " ").replace(/ /g, " ");
|
||||
const lengthStr = length.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
let msg = "length: " + lengthStr;
|
||||
|
||||
document.getElementById("input-info").innerHTML = "length: " + lengthStr + "<br>lines: " + linesStr;
|
||||
if (typeof lines === "number") {
|
||||
const linesStr = lines.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
msg += "<br>lines: " + linesStr;
|
||||
}
|
||||
|
||||
document.getElementById("input-info").innerHTML = msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for input scroll events.
|
||||
* Scrolls the highlighter pane to match the input textarea position and updates history state.
|
||||
* Handler for input change events.
|
||||
*
|
||||
* @param {event} e
|
||||
*
|
||||
* @fires Manager#statechange
|
||||
*/
|
||||
InputWaiter.prototype.inputChange = function(e) {
|
||||
// Ignore this function if the input is a File
|
||||
if (this.fileBuffer) return;
|
||||
|
||||
// Remove highlighting from input and output panes as the offsets might be different now
|
||||
this.manager.highlighter.removeHighlights();
|
||||
|
||||
@@ -93,18 +137,47 @@ InputWaiter.prototype.inputChange = function(e) {
|
||||
|
||||
// Update the input metadata info
|
||||
const inputText = this.get();
|
||||
const lines = inputText.count("\n") + 1;
|
||||
const lines = inputText.length < (this.app.options.ioDisplayThreshold * 1024) ?
|
||||
inputText.count("\n") + 1 : null;
|
||||
|
||||
this.setInputInfo(inputText.length, lines);
|
||||
|
||||
|
||||
if (this.badKeys.indexOf(e.keyCode) < 0) {
|
||||
if (e && this.badKeys.indexOf(e.keyCode) < 0) {
|
||||
// Fire the statechange event as the input has been modified
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for input paste events.
|
||||
* Checks that the size of the input is below the display limit, otherwise treats it as a file/blob.
|
||||
*
|
||||
* @param {event} e
|
||||
*/
|
||||
InputWaiter.prototype.inputPaste = function(e) {
|
||||
const pastedData = e.clipboardData.getData("Text");
|
||||
|
||||
if (pastedData.length < (this.app.options.ioDisplayThreshold * 1024)) {
|
||||
this.inputChange(e);
|
||||
} else {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const file = new File([pastedData], "PastedData", {
|
||||
type: "text/plain",
|
||||
lastModified: Date.now()
|
||||
});
|
||||
|
||||
this.loaderWorker = new LoaderWorker();
|
||||
this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this));
|
||||
this.loaderWorker.postMessage({"file": file});
|
||||
this.set(file);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for input dragover events.
|
||||
* Gives the user a visual cue to show that items can be dropped here.
|
||||
@@ -118,7 +191,7 @@ InputWaiter.prototype.inputDragover = function(e) {
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
e.target.classList.add("dropping-file");
|
||||
e.target.closest("#input-text,#input-file").classList.add("dropping-file");
|
||||
};
|
||||
|
||||
|
||||
@@ -131,7 +204,8 @@ InputWaiter.prototype.inputDragover = function(e) {
|
||||
InputWaiter.prototype.inputDragleave = function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
e.target.classList.remove("dropping-file");
|
||||
document.getElementById("input-text").classList.remove("dropping-file");
|
||||
document.getElementById("input-file").classList.remove("dropping-file");
|
||||
};
|
||||
|
||||
|
||||
@@ -149,55 +223,82 @@ InputWaiter.prototype.inputDrop = function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
const el = e.target;
|
||||
const file = e.dataTransfer.files[0];
|
||||
const text = e.dataTransfer.getData("Text");
|
||||
const reader = new FileReader();
|
||||
let inputCharcode = "";
|
||||
let offset = 0;
|
||||
const CHUNK_SIZE = 20480; // 20KB
|
||||
|
||||
const setInput = function() {
|
||||
const recipeConfig = this.app.getRecipeConfig();
|
||||
if (!recipeConfig[0] || recipeConfig[0].op !== "From Hex") {
|
||||
recipeConfig.unshift({op: "From Hex", args: ["Space"]});
|
||||
this.app.setRecipeConfig(recipeConfig);
|
||||
}
|
||||
document.getElementById("input-text").classList.remove("dropping-file");
|
||||
document.getElementById("input-file").classList.remove("dropping-file");
|
||||
|
||||
this.set(inputCharcode);
|
||||
|
||||
el.classList.remove("loadingFile");
|
||||
}.bind(this);
|
||||
|
||||
const seek = function() {
|
||||
if (offset >= file.size) {
|
||||
setInput();
|
||||
return;
|
||||
}
|
||||
el.value = "Processing... " + Math.round(offset / file.size * 100) + "%";
|
||||
const slice = file.slice(offset, offset + CHUNK_SIZE);
|
||||
reader.readAsArrayBuffer(slice);
|
||||
};
|
||||
|
||||
reader.onload = function(e) {
|
||||
const data = new Uint8Array(reader.result);
|
||||
inputCharcode += Utils.toHexFast(data);
|
||||
offset += CHUNK_SIZE;
|
||||
seek();
|
||||
};
|
||||
|
||||
|
||||
el.classList.remove("dropping-file");
|
||||
if (text) {
|
||||
this.closeFile();
|
||||
this.set(text);
|
||||
return;
|
||||
}
|
||||
|
||||
if (file) {
|
||||
el.classList.add("loadingFile");
|
||||
seek();
|
||||
} else if (text) {
|
||||
this.set(text);
|
||||
this.closeFile();
|
||||
this.loaderWorker = new LoaderWorker();
|
||||
this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this));
|
||||
this.loaderWorker.postMessage({"file": file});
|
||||
this.set(file);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for messages sent back by the LoaderWorker.
|
||||
*
|
||||
* @param {MessageEvent} e
|
||||
*/
|
||||
InputWaiter.prototype.handleLoaderMessage = function(e) {
|
||||
const r = e.data;
|
||||
if (r.hasOwnProperty("progress")) {
|
||||
const fileLoaded = document.getElementById("input-file-loaded");
|
||||
fileLoaded.textContent = r.progress + "%";
|
||||
}
|
||||
|
||||
if (r.hasOwnProperty("error")) {
|
||||
this.app.alert(r.error, "danger", 10000);
|
||||
}
|
||||
|
||||
if (r.hasOwnProperty("fileBuffer")) {
|
||||
log.debug("Input file loaded");
|
||||
this.fileBuffer = r.fileBuffer;
|
||||
this.displayFilePreview();
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shows a chunk of the file in the input behind the file overlay.
|
||||
*/
|
||||
InputWaiter.prototype.displayFilePreview = function() {
|
||||
const inputText = document.getElementById("input-text"),
|
||||
fileSlice = this.fileBuffer.slice(0, 4096);
|
||||
|
||||
inputText.style.overflow = "hidden";
|
||||
inputText.classList.add("blur");
|
||||
inputText.value = Utils.printable(Utils.arrayBufferToStr(fileSlice));
|
||||
if (this.fileBuffer.byteLength > 4096) {
|
||||
inputText.value += "[truncated]...";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for file close events.
|
||||
*/
|
||||
InputWaiter.prototype.closeFile = function() {
|
||||
if (this.loaderWorker) this.loaderWorker.terminate();
|
||||
this.fileBuffer = null;
|
||||
document.getElementById("input-file").style.display = "none";
|
||||
const inputText = document.getElementById("input-text");
|
||||
inputText.style.overflow = "auto";
|
||||
inputText.classList.remove("blur");
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for clear IO events.
|
||||
* Resets the input, output and info areas.
|
||||
@@ -205,6 +306,8 @@ InputWaiter.prototype.inputDrop = function(e) {
|
||||
* @fires Manager#statechange
|
||||
*/
|
||||
InputWaiter.prototype.clearIoClick = function() {
|
||||
this.closeFile();
|
||||
this.manager.output.closeFile();
|
||||
this.manager.highlighter.removeHighlights();
|
||||
document.getElementById("input-text").value = "";
|
||||
document.getElementById("output-text").value = "";
|
||||
|
||||
54
src/web/LoaderWorker.js
Normal file
54
src/web/LoaderWorker.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Web Worker to load large amounts of data without locking up the UI.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Respond to message from parent thread.
|
||||
*/
|
||||
self.addEventListener("message", function(e) {
|
||||
const r = e.data;
|
||||
if (r.hasOwnProperty("file")) {
|
||||
self.loadFile(r.file);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Loads a file object into an ArrayBuffer, then transfers it back to the parent thread.
|
||||
*
|
||||
* @param {File} file
|
||||
*/
|
||||
self.loadFile = function(file) {
|
||||
const reader = new FileReader();
|
||||
let data = new Uint8Array(file.size);
|
||||
let offset = 0;
|
||||
const CHUNK_SIZE = 10485760; // 10MiB
|
||||
|
||||
const seek = function() {
|
||||
if (offset >= file.size) {
|
||||
self.postMessage({"progress": 100});
|
||||
self.postMessage({"fileBuffer": data.buffer}, [data.buffer]);
|
||||
return;
|
||||
}
|
||||
self.postMessage({"progress": Math.round(offset / file.size * 100)});
|
||||
const slice = file.slice(offset, offset + CHUNK_SIZE);
|
||||
reader.readAsArrayBuffer(slice);
|
||||
};
|
||||
|
||||
reader.onload = function(e) {
|
||||
data.set(new Uint8Array(reader.result), offset);
|
||||
offset += CHUNK_SIZE;
|
||||
seek();
|
||||
};
|
||||
|
||||
reader.onerror = function(e) {
|
||||
self.postMessage({"error": reader.error.message});
|
||||
};
|
||||
|
||||
seek();
|
||||
};
|
||||
@@ -58,7 +58,7 @@ const Manager = function(app) {
|
||||
this.ops = new OperationsWaiter(this.app, this);
|
||||
this.input = new InputWaiter(this.app, this);
|
||||
this.output = new OutputWaiter(this.app, this);
|
||||
this.options = new OptionsWaiter(this.app);
|
||||
this.options = new OptionsWaiter(this.app, this);
|
||||
this.highlighter = new HighlighterWaiter(this.app, this);
|
||||
this.seasonal = new SeasonalWaiter(this.app, this);
|
||||
this.bindings = new BindingsWaiter(this.app, this);
|
||||
@@ -119,7 +119,7 @@ Manager.prototype.initialiseEventListeners = function() {
|
||||
this.addDynamicListener(".op-list .op-icon", "mouseover", this.ops.opIconMouseover, this.ops);
|
||||
this.addDynamicListener(".op-list .op-icon", "mouseleave", this.ops.opIconMouseleave, this.ops);
|
||||
this.addDynamicListener(".op-list", "oplistcreate", this.ops.opListCreate, this.ops);
|
||||
this.addDynamicListener("li.operation", "operationadd", this.recipe.opAdd.bind(this.recipe));
|
||||
this.addDynamicListener("li.operation", "operationadd", this.recipe.opAdd, this.recipe);
|
||||
|
||||
// Recipe
|
||||
this.addDynamicListener(".arg:not(select)", "input", this.recipe.ingChange, this.recipe);
|
||||
@@ -132,16 +132,18 @@ Manager.prototype.initialiseEventListeners = function() {
|
||||
this.addDynamicListener("#rec-list", "operationremove", this.recipe.opRemove.bind(this.recipe));
|
||||
|
||||
// Input
|
||||
this.addMultiEventListener("#input-text", "keyup paste", this.input.inputChange, this.input);
|
||||
this.addMultiEventListener("#input-text", "keyup", this.input.inputChange, this.input);
|
||||
this.addMultiEventListener("#input-text", "paste", this.input.inputPaste, this.input);
|
||||
document.getElementById("reset-layout").addEventListener("click", this.app.resetLayout.bind(this.app));
|
||||
document.getElementById("clr-io").addEventListener("click", this.input.clearIoClick.bind(this.input));
|
||||
document.getElementById("input-text").addEventListener("dragover", this.input.inputDragover.bind(this.input));
|
||||
document.getElementById("input-text").addEventListener("dragleave", this.input.inputDragleave.bind(this.input));
|
||||
document.getElementById("input-text").addEventListener("drop", this.input.inputDrop.bind(this.input));
|
||||
this.addListeners("#input-text,#input-file", "dragover", this.input.inputDragover, this.input);
|
||||
this.addListeners("#input-text,#input-file", "dragleave", this.input.inputDragleave, this.input);
|
||||
this.addListeners("#input-text,#input-file", "drop", this.input.inputDrop, this.input);
|
||||
document.getElementById("input-text").addEventListener("scroll", this.highlighter.inputScroll.bind(this.highlighter));
|
||||
document.getElementById("input-text").addEventListener("mouseup", this.highlighter.inputMouseup.bind(this.highlighter));
|
||||
document.getElementById("input-text").addEventListener("mousemove", this.highlighter.inputMousemove.bind(this.highlighter));
|
||||
this.addMultiEventListener("#input-text", "mousedown dblclick select", this.highlighter.inputMousedown, this.highlighter);
|
||||
document.querySelector("#input-file .close").addEventListener("click", this.input.clearIoClick.bind(this.input));
|
||||
|
||||
// Output
|
||||
document.getElementById("save-to-file").addEventListener("click", this.output.saveClick.bind(this.output));
|
||||
@@ -157,6 +159,9 @@ Manager.prototype.initialiseEventListeners = function() {
|
||||
this.addMultiEventListener("#output-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter);
|
||||
this.addMultiEventListener("#output-html", "mousedown dblclick select", this.highlighter.outputHtmlMousedown, this.highlighter);
|
||||
this.addDynamicListener(".file-switch", "click", this.output.fileSwitch, this.output);
|
||||
this.addDynamicListener("#output-file-download", "click", this.output.downloadFile, this.output);
|
||||
this.addDynamicListener("#output-file-slice", "click", this.output.displayFileSlice, this.output);
|
||||
document.getElementById("show-file-overlay").addEventListener("click", this.output.showFileOverlayClick.bind(this.output));
|
||||
|
||||
// Options
|
||||
document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options));
|
||||
@@ -168,6 +173,7 @@ Manager.prototype.initialiseEventListeners = function() {
|
||||
this.addDynamicListener(".option-item input[type=number]", "change", this.options.numberChange, this.options);
|
||||
this.addDynamicListener(".option-item select", "change", this.options.selectChange, this.options);
|
||||
document.getElementById("theme").addEventListener("change", this.options.themeChange.bind(this.options));
|
||||
document.getElementById("logLevel").addEventListener("change", this.options.logLevelChange.bind(this.options));
|
||||
|
||||
// Misc
|
||||
window.addEventListener("keydown", this.bindings.parseInput.bind(this.bindings));
|
||||
|
||||
@@ -167,7 +167,8 @@ OperationsWaiter.prototype.opListCreate = function(e) {
|
||||
OperationsWaiter.prototype.enableOpsListPopovers = function(el) {
|
||||
$(el).find("[data-toggle=popover]").addBack("[data-toggle=popover]")
|
||||
.popover({trigger: "manual"})
|
||||
.on("mouseenter", function() {
|
||||
.on("mouseenter", function(e) {
|
||||
if (e.buttons > 0) return; // Mouse button held down - likely dragging an opertion
|
||||
const _this = this;
|
||||
$(this).popover("show");
|
||||
$(".popover").on("mouseleave", function () {
|
||||
@@ -178,7 +179,7 @@ OperationsWaiter.prototype.enableOpsListPopovers = function(el) {
|
||||
setTimeout(function() {
|
||||
// Determine if the popover associated with this element is being hovered over
|
||||
if ($(_this).data("bs.popover") &&
|
||||
!$(_this).data("bs.popover").$tip.is(":hover")) {
|
||||
($(_this).data("bs.popover").$tip && !$(_this).data("bs.popover").$tip.is(":hover"))) {
|
||||
$(_this).popover("hide");
|
||||
}
|
||||
}, 50);
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
* @constructor
|
||||
* @param {App} app - The main view object for CyberChef.
|
||||
*/
|
||||
const OptionsWaiter = function(app) {
|
||||
const OptionsWaiter = function(app, manager) {
|
||||
this.app = app;
|
||||
this.manager = manager;
|
||||
};
|
||||
|
||||
|
||||
@@ -86,6 +87,7 @@ OptionsWaiter.prototype.switchChange = function(e, state) {
|
||||
const el = e.target;
|
||||
const option = el.getAttribute("option");
|
||||
|
||||
log.debug(`Setting ${option} to ${state}`);
|
||||
this.app.options[option] = state;
|
||||
|
||||
if (this.app.isLocalStorageAvailable())
|
||||
@@ -102,8 +104,10 @@ OptionsWaiter.prototype.switchChange = function(e, state) {
|
||||
OptionsWaiter.prototype.numberChange = function(e) {
|
||||
const el = e.target;
|
||||
const option = el.getAttribute("option");
|
||||
const val = parseInt(el.value, 10);
|
||||
|
||||
this.app.options[option] = parseInt(el.value, 10);
|
||||
log.debug(`Setting ${option} to ${val}`);
|
||||
this.app.options[option] = val;
|
||||
|
||||
if (this.app.isLocalStorageAvailable())
|
||||
localStorage.setItem("options", JSON.stringify(this.app.options));
|
||||
@@ -120,6 +124,7 @@ OptionsWaiter.prototype.selectChange = function(e) {
|
||||
const el = e.target;
|
||||
const option = el.getAttribute("option");
|
||||
|
||||
log.debug(`Setting ${option} to ${el.value}`);
|
||||
this.app.options[option] = el.value;
|
||||
|
||||
if (this.app.isLocalStorageAvailable())
|
||||
@@ -149,6 +154,8 @@ OptionsWaiter.prototype.setWordWrap = function() {
|
||||
|
||||
/**
|
||||
* Changes the theme by setting the class of the <html> element.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
OptionsWaiter.prototype.themeChange = function (e) {
|
||||
const themeClass = e.target.value;
|
||||
@@ -156,4 +163,16 @@ OptionsWaiter.prototype.themeChange = function (e) {
|
||||
document.querySelector(":root").className = themeClass;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Changes the console logging level.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
OptionsWaiter.prototype.logLevelChange = function (e) {
|
||||
const level = e.target.value;
|
||||
log.setLevel(level, false);
|
||||
this.manager.worker.setLogLevel();
|
||||
};
|
||||
|
||||
export default OptionsWaiter;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Utils from "../core/Utils.js";
|
||||
import FileSaver from "file-saver";
|
||||
|
||||
|
||||
/**
|
||||
@@ -15,6 +16,9 @@ import Utils from "../core/Utils.js";
|
||||
const OutputWaiter = function(app, manager) {
|
||||
this.app = app;
|
||||
this.manager = manager;
|
||||
|
||||
this.dishBuffer = null;
|
||||
this.dishStr = null;
|
||||
};
|
||||
|
||||
|
||||
@@ -31,47 +35,161 @@ OutputWaiter.prototype.get = function() {
|
||||
/**
|
||||
* Sets the output in the output textarea.
|
||||
*
|
||||
* @param {string} dataStr - The output string/HTML
|
||||
* @param {string|ArrayBuffer} data - The output string/HTML/ArrayBuffer
|
||||
* @param {string} type - The data type of the output
|
||||
* @param {number} duration - The length of time (ms) it took to generate the output
|
||||
* @param {boolean} [preserveBuffer=false] - Whether to preserve the dishBuffer
|
||||
*/
|
||||
OutputWaiter.prototype.set = function(dataStr, type, duration) {
|
||||
OutputWaiter.prototype.set = function(data, type, duration, preserveBuffer) {
|
||||
log.debug("Output type: " + type);
|
||||
const outputText = document.getElementById("output-text");
|
||||
const outputHtml = document.getElementById("output-html");
|
||||
const outputFile = document.getElementById("output-file");
|
||||
const outputHighlighter = document.getElementById("output-highlighter");
|
||||
const inputHighlighter = document.getElementById("input-highlighter");
|
||||
let scriptElements, lines, length;
|
||||
|
||||
if (type === "html") {
|
||||
outputText.style.display = "none";
|
||||
outputHtml.style.display = "block";
|
||||
outputHighlighter.display = "none";
|
||||
inputHighlighter.display = "none";
|
||||
if (!preserveBuffer) {
|
||||
this.closeFile();
|
||||
document.getElementById("show-file-overlay").style.display = "none";
|
||||
}
|
||||
|
||||
outputText.value = "";
|
||||
outputHtml.innerHTML = dataStr;
|
||||
switch (type) {
|
||||
case "html":
|
||||
outputText.style.display = "none";
|
||||
outputHtml.style.display = "block";
|
||||
outputFile.style.display = "none";
|
||||
outputHighlighter.display = "none";
|
||||
inputHighlighter.display = "none";
|
||||
|
||||
// Execute script sections
|
||||
const scriptElements = outputHtml.querySelectorAll("script");
|
||||
for (let i = 0; i < scriptElements.length; i++) {
|
||||
try {
|
||||
eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
outputText.value = "";
|
||||
outputHtml.innerHTML = data;
|
||||
this.dishStr = Utils.unescapeHtml(Utils.stripHtmlTags(data, true));
|
||||
length = data.length;
|
||||
lines = this.dishStr.count("\n") + 1;
|
||||
|
||||
// Execute script sections
|
||||
scriptElements = outputHtml.querySelectorAll("script");
|
||||
for (let i = 0; i < scriptElements.length; i++) {
|
||||
try {
|
||||
eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval
|
||||
} catch (err) {
|
||||
log.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
outputText.style.display = "block";
|
||||
outputHtml.style.display = "none";
|
||||
outputHighlighter.display = "block";
|
||||
inputHighlighter.display = "block";
|
||||
break;
|
||||
case "ArrayBuffer":
|
||||
outputText.style.display = "block";
|
||||
outputHtml.style.display = "none";
|
||||
outputHighlighter.display = "none";
|
||||
inputHighlighter.display = "none";
|
||||
|
||||
outputText.value = Utils.printable(dataStr, true);
|
||||
outputHtml.innerHTML = "";
|
||||
outputText.value = "";
|
||||
outputHtml.innerHTML = "";
|
||||
this.dishStr = "";
|
||||
length = data.byteLength;
|
||||
|
||||
this.setFile(data);
|
||||
break;
|
||||
case "string":
|
||||
default:
|
||||
outputText.style.display = "block";
|
||||
outputHtml.style.display = "none";
|
||||
outputFile.style.display = "none";
|
||||
outputHighlighter.display = "block";
|
||||
inputHighlighter.display = "block";
|
||||
|
||||
outputText.value = Utils.printable(data, true);
|
||||
outputHtml.innerHTML = "";
|
||||
|
||||
lines = data.count("\n") + 1;
|
||||
length = data.length;
|
||||
this.dishStr = data;
|
||||
break;
|
||||
}
|
||||
|
||||
this.manager.highlighter.removeHighlights();
|
||||
const lines = dataStr.count("\n") + 1;
|
||||
this.setOutputInfo(dataStr.length, lines, duration);
|
||||
this.setOutputInfo(length, lines, duration);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shows file details.
|
||||
*
|
||||
* @param {ArrayBuffer} buf
|
||||
*/
|
||||
OutputWaiter.prototype.setFile = function(buf) {
|
||||
this.dishBuffer = buf;
|
||||
const file = new File([buf], "output.dat");
|
||||
|
||||
// Display file overlay in output area with details
|
||||
const fileOverlay = document.getElementById("output-file"),
|
||||
fileSize = document.getElementById("output-file-size");
|
||||
|
||||
fileOverlay.style.display = "block";
|
||||
fileSize.textContent = file.size.toLocaleString() + " bytes";
|
||||
|
||||
// Display preview slice in the background
|
||||
const outputText = document.getElementById("output-text"),
|
||||
fileSlice = this.dishBuffer.slice(0, 4096);
|
||||
|
||||
outputText.classList.add("blur");
|
||||
outputText.value = Utils.printable(Utils.arrayBufferToStr(fileSlice));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes the output file and nulls its memory.
|
||||
*/
|
||||
OutputWaiter.prototype.closeFile = function() {
|
||||
this.dishBuffer = null;
|
||||
document.getElementById("output-file").style.display = "none";
|
||||
document.getElementById("output-text").classList.remove("blur");
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for file download events.
|
||||
*/
|
||||
OutputWaiter.prototype.downloadFile = function() {
|
||||
this.filename = window.prompt("Please enter a filename:", this.filename || "download.dat");
|
||||
const file = new File([this.dishBuffer], this.filename);
|
||||
|
||||
if (this.filename) FileSaver.saveAs(file, this.filename, false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for file slice display events.
|
||||
*/
|
||||
OutputWaiter.prototype.displayFileSlice = function() {
|
||||
const startTime = new Date().getTime(),
|
||||
showFileOverlay = document.getElementById("show-file-overlay"),
|
||||
sliceFromEl = document.getElementById("output-file-slice-from"),
|
||||
sliceToEl = document.getElementById("output-file-slice-to"),
|
||||
sliceFrom = parseInt(sliceFromEl.value, 10),
|
||||
sliceTo = parseInt(sliceToEl.value, 10),
|
||||
str = Utils.arrayBufferToStr(this.dishBuffer.slice(sliceFrom, sliceTo));
|
||||
|
||||
document.getElementById("output-text").classList.remove("blur");
|
||||
showFileOverlay.style.display = "block";
|
||||
this.set(str, "string", new Date().getTime() - startTime, true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for show file overlay events.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
OutputWaiter.prototype.showFileOverlayClick = function(e) {
|
||||
const outputFile = document.getElementById("output-file"),
|
||||
showFileOverlay = e.target;
|
||||
|
||||
document.getElementById("output-text").classList.add("blur");
|
||||
outputFile.style.display = "block";
|
||||
showFileOverlay.style.display = "none";
|
||||
this.setOutputInfo(this.dishBuffer.byteLength, null, 0);
|
||||
};
|
||||
|
||||
|
||||
@@ -86,13 +204,17 @@ OutputWaiter.prototype.setOutputInfo = function(length, lines, duration) {
|
||||
let width = length.toString().length;
|
||||
width = width < 4 ? 4 : width;
|
||||
|
||||
const lengthStr = Utils.pad(length.toString(), width, " ").replace(/ /g, " ");
|
||||
const linesStr = Utils.pad(lines.toString(), width, " ").replace(/ /g, " ");
|
||||
const timeStr = Utils.pad(duration.toString() + "ms", width, " ").replace(/ /g, " ");
|
||||
const lengthStr = length.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
const timeStr = (duration.toString() + "ms").padStart(width, " ").replace(/ /g, " ");
|
||||
|
||||
document.getElementById("output-info").innerHTML = "time: " + timeStr +
|
||||
"<br>length: " + lengthStr +
|
||||
"<br>lines: " + linesStr;
|
||||
let msg = "time: " + timeStr + "<br>length: " + lengthStr;
|
||||
|
||||
if (typeof lines === "number") {
|
||||
const linesStr = lines.toString().padStart(width, " ").replace(/ /g, " ");
|
||||
msg += "<br>lines: " + linesStr;
|
||||
}
|
||||
|
||||
document.getElementById("output-info").innerHTML = msg;
|
||||
document.getElementById("input-selection-info").innerHTML = "";
|
||||
document.getElementById("output-selection-info").innerHTML = "";
|
||||
};
|
||||
@@ -129,24 +251,13 @@ OutputWaiter.prototype.adjustWidth = function() {
|
||||
|
||||
/**
|
||||
* Handler for save click events.
|
||||
* Saves the current output to a file, downloaded as a URL octet stream.
|
||||
* Saves the current output to a file.
|
||||
*/
|
||||
OutputWaiter.prototype.saveClick = function() {
|
||||
const data = Utils.toBase64(this.app.dishStr);
|
||||
const filename = window.prompt("Please enter a filename:", "download.dat");
|
||||
|
||||
if (filename) {
|
||||
const el = document.createElement("a");
|
||||
el.setAttribute("href", "data:application/octet-stream;base64;charset=utf-8," + data);
|
||||
el.setAttribute("download", filename);
|
||||
|
||||
// Firefox requires that the element be added to the DOM before it can be clicked
|
||||
el.style.display = "none";
|
||||
document.body.appendChild(el);
|
||||
|
||||
el.click();
|
||||
el.remove();
|
||||
if (!this.dishBuffer) {
|
||||
this.dishBuffer = new Uint8Array(Utils.strToCharcode(this.dishStr)).buffer;
|
||||
}
|
||||
this.downloadFile();
|
||||
};
|
||||
|
||||
|
||||
@@ -165,14 +276,14 @@ OutputWaiter.prototype.copyClick = function() {
|
||||
textarea.style.height = 0;
|
||||
textarea.style.border = "none";
|
||||
|
||||
textarea.value = this.app.dishStr;
|
||||
textarea.value = this.dishStr;
|
||||
document.body.appendChild(textarea);
|
||||
|
||||
// Select and copy the contents of this textarea
|
||||
let success = false;
|
||||
try {
|
||||
textarea.select();
|
||||
success = document.execCommand("copy");
|
||||
success = this.dishStr && document.execCommand("copy");
|
||||
} catch (err) {
|
||||
success = false;
|
||||
}
|
||||
@@ -195,7 +306,17 @@ OutputWaiter.prototype.copyClick = function() {
|
||||
OutputWaiter.prototype.switchClick = function() {
|
||||
this.switchOrigData = this.manager.input.get();
|
||||
document.getElementById("undo-switch").disabled = false;
|
||||
this.app.setInput(this.app.dishStr);
|
||||
if (this.dishBuffer) {
|
||||
this.manager.input.setFile(new File([this.dishBuffer], "output.dat"));
|
||||
this.manager.input.handleLoaderMessage({
|
||||
data: {
|
||||
progress: 100,
|
||||
fileBuffer: this.dishBuffer
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.app.setInput(this.dishStr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -210,7 +331,7 @@ OutputWaiter.prototype.undoSwitchClick = function() {
|
||||
|
||||
/**
|
||||
* Handler for file switch click events.
|
||||
* Moves a files data for items created via Utils.displayFilesAsHTML to the input.
|
||||
* Moves a file's data for items created via Utils.displayFilesAsHTML to the input.
|
||||
*/
|
||||
OutputWaiter.prototype.fileSwitch = function(e) {
|
||||
e.preventDefault();
|
||||
@@ -282,4 +403,14 @@ OutputWaiter.prototype.setStatusMsg = function(msg) {
|
||||
el.textContent = msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the output contains carriage returns
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
OutputWaiter.prototype.containsCR = function() {
|
||||
return this.dishStr.indexOf("\r") >= 0;
|
||||
};
|
||||
|
||||
export default OutputWaiter;
|
||||
|
||||
@@ -253,7 +253,7 @@ RecipeWaiter.prototype.breakpointClick = function(e) {
|
||||
*/
|
||||
RecipeWaiter.prototype.operationDblclick = function(e) {
|
||||
e.target.remove();
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
this.opRemove(e);
|
||||
};
|
||||
|
||||
|
||||
@@ -266,7 +266,7 @@ RecipeWaiter.prototype.operationDblclick = function(e) {
|
||||
*/
|
||||
RecipeWaiter.prototype.operationChildDblclick = function(e) {
|
||||
e.target.parentNode.remove();
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
this.opRemove(e);
|
||||
};
|
||||
|
||||
|
||||
@@ -421,6 +421,7 @@ RecipeWaiter.prototype.dropdownToggleClick = function(e) {
|
||||
* @param {event} e
|
||||
*/
|
||||
RecipeWaiter.prototype.opAdd = function(e) {
|
||||
log.debug(`'${e.target.querySelector(".arg-title").textContent}' added to recipe`);
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
};
|
||||
|
||||
@@ -433,6 +434,7 @@ RecipeWaiter.prototype.opAdd = function(e) {
|
||||
* @param {event} e
|
||||
*/
|
||||
RecipeWaiter.prototype.opRemove = function(e) {
|
||||
log.debug("Operation removed from recipe");
|
||||
window.dispatchEvent(this.manager.statechange);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Utils from "../core/Utils.js";
|
||||
import ChefWorker from "worker-loader?inline&fallback=false!../core/ChefWorker.js";
|
||||
|
||||
/**
|
||||
@@ -22,8 +21,10 @@ const WorkerWaiter = function(app, manager) {
|
||||
* Sets up the ChefWorker and associated listeners.
|
||||
*/
|
||||
WorkerWaiter.prototype.registerChefWorker = function() {
|
||||
log.debug("Registering new ChefWorker");
|
||||
this.chefWorker = new ChefWorker();
|
||||
this.chefWorker.addEventListener("message", this.handleChefMessage.bind(this));
|
||||
this.setLogLevel();
|
||||
|
||||
let docURL = document.location.href.split(/[#?]/)[0];
|
||||
const index = docURL.lastIndexOf("/");
|
||||
@@ -41,8 +42,10 @@ WorkerWaiter.prototype.registerChefWorker = function() {
|
||||
*/
|
||||
WorkerWaiter.prototype.handleChefMessage = function(e) {
|
||||
const r = e.data;
|
||||
log.debug("Receiving '" + r.action + "' from ChefWorker");
|
||||
|
||||
switch (r.action) {
|
||||
case "bakeSuccess":
|
||||
case "bakeComplete":
|
||||
this.bakingComplete(r.data);
|
||||
break;
|
||||
case "bakeError":
|
||||
@@ -53,12 +56,14 @@ WorkerWaiter.prototype.handleChefMessage = function(e) {
|
||||
break;
|
||||
case "workerLoaded":
|
||||
this.app.workerLoaded = true;
|
||||
log.debug("ChefWorker loaded");
|
||||
this.app.loaded();
|
||||
break;
|
||||
case "statusMessage":
|
||||
this.manager.output.setStatusMsg(r.data);
|
||||
break;
|
||||
case "optionUpdate":
|
||||
log.debug(`Setting ${r.data.option} to ${r.data.value}`);
|
||||
this.app.options[r.data.option] = r.data.value;
|
||||
break;
|
||||
case "setRegisters":
|
||||
@@ -68,7 +73,7 @@ WorkerWaiter.prototype.handleChefMessage = function(e) {
|
||||
this.manager.highlighter.displayHighlights(r.data.pos, r.data.direction);
|
||||
break;
|
||||
default:
|
||||
console.error("Unrecognised message from ChefWorker", e);
|
||||
log.error("Unrecognised message from ChefWorker", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
@@ -111,10 +116,10 @@ WorkerWaiter.prototype.bakingComplete = function(response) {
|
||||
this.app.handleError(response.error);
|
||||
}
|
||||
|
||||
this.app.dishStr = response.type === "html" ? Utils.stripHtmlTags(response.result, true) : response.result;
|
||||
this.app.progress = response.progress;
|
||||
this.manager.recipe.updateBreakpointIndicator(response.progress);
|
||||
this.manager.output.set(response.result, response.type, response.duration);
|
||||
log.debug("--- Bake complete ---");
|
||||
};
|
||||
|
||||
|
||||
@@ -147,7 +152,7 @@ WorkerWaiter.prototype.bake = function(input, recipeConfig, options, progress, s
|
||||
* Asks the ChefWorker to run a silent bake, forcing the browser to load and cache all the relevant
|
||||
* JavaScript code needed to do a real bake.
|
||||
*
|
||||
* @param {Objectp[]} [recipeConfig]
|
||||
* @param {Object[]} [recipeConfig]
|
||||
*/
|
||||
WorkerWaiter.prototype.silentBake = function(recipeConfig) {
|
||||
this.chefWorker.postMessage({
|
||||
@@ -180,4 +185,19 @@ WorkerWaiter.prototype.highlight = function(recipeConfig, direction, pos) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the console log level in the worker.
|
||||
*
|
||||
* @param {string} level
|
||||
*/
|
||||
WorkerWaiter.prototype.setLogLevel = function(level) {
|
||||
if (!this.chefWorker) return;
|
||||
|
||||
this.chefWorker.postMessage({
|
||||
action: "setLogLevel",
|
||||
data: log.getLevel()
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export default WorkerWaiter;
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
}
|
||||
|
||||
// Define loading messages
|
||||
const loadingMsgs = [
|
||||
var loadingMsgs = [
|
||||
"Proving P = NP...",
|
||||
"Computing 6 x 9...",
|
||||
"Mining bitcoin...",
|
||||
@@ -59,22 +59,25 @@
|
||||
"for i in range(additional): Pylon()",
|
||||
"(creating unresolved tension...",
|
||||
"Symlinking emacs and vim to ed...",
|
||||
"Training branch predictor...",
|
||||
"Timing cache hits...",
|
||||
"Speculatively executing recipes..."
|
||||
];
|
||||
|
||||
// Shuffle array using Durstenfeld algorithm
|
||||
for (let i = loadingMsgs.length - 1; i > 0; --i) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
const temp = loadingMsgs[i];
|
||||
var j = Math.floor(Math.random() * (i + 1));
|
||||
var 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();
|
||||
var msg = loadingMsgs.shift();
|
||||
loadingMsgs.push(msg);
|
||||
try {
|
||||
const el = document.getElementById("preloader-msg");
|
||||
var el = document.getElementById("preloader-msg");
|
||||
if (!el.classList.contains("loading"))
|
||||
el.classList.add("loading"); // Causes CSS transition on first message
|
||||
el.innerHTML = msg;
|
||||
@@ -83,8 +86,50 @@
|
||||
|
||||
changeLoadingMsg();
|
||||
window.loadingMsgsInt = setInterval(changeLoadingMsg, (Math.random() * 2000) + 1500);
|
||||
|
||||
// If any errors are thrown during loading, handle them here
|
||||
function loadingErrorHandler(e) {
|
||||
function escapeHtml(str) {
|
||||
var HTML_CHARS = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
'"': """,
|
||||
"'": "'", // ' not recommended because it's not in the HTML spec
|
||||
"/": "/", // forward slash is included as it helps end an HTML entity
|
||||
"`": "`"
|
||||
};
|
||||
|
||||
return str.replace(/[&<>"'/`]/g, function (match) {
|
||||
return HTML_CHARS[match];
|
||||
});
|
||||
}
|
||||
|
||||
var msg = e.message +
|
||||
(e.filename ? "\nFilename: " + e.filename : "") +
|
||||
(e.lineno ? "\nLine: " + e.lineno : "") +
|
||||
(e.colno ? "\nColumn: " + e.colno : "") +
|
||||
(e.error ? "\nError: " + e.error : "") +
|
||||
"\nUser-Agent: " + navigator.userAgent +
|
||||
"\nCyberChef version: <%= htmlWebpackPlugin.options.version %>";
|
||||
|
||||
clearInterval(window.loadingMsgsInt);
|
||||
document.getElementById("preloader").remove();
|
||||
document.getElementById("preloader-msg").remove();
|
||||
document.getElementById("preloader-error").innerHTML =
|
||||
"CyberChef encountered an error while loading.<br><br>" +
|
||||
"The following browser versions are supported:" +
|
||||
"<ul><li>Google Chrome 40+</li><li>Mozilla Firefox 35+</li><li>Microsoft Edge 14+</li></ul>" +
|
||||
"Your user agent is:<br>" + escapeHtml(navigator.userAgent) + "<br><br>" +
|
||||
"If your browser is supported, please <a href='https://github.com/gchq/CyberChef/issues/new'>" +
|
||||
"raise an issue</a> including the following details:<br><br>" +
|
||||
"<pre>" + escapeHtml(msg) + "</pre>";
|
||||
};
|
||||
window.addEventListener("error", loadingErrorHandler);
|
||||
</script>
|
||||
<% if (!htmlWebpackPlugin.options.inline) { %>
|
||||
<% if (htmlWebpackPlugin.options.inline) { %>
|
||||
<meta name="robots" content="noindex" />
|
||||
<% } else { %>
|
||||
<script type="application/ld+json">
|
||||
<% print(JSON.stringify(require("../static/structuredData.json"))); %>
|
||||
</script>
|
||||
@@ -95,6 +140,7 @@
|
||||
<div id="loader-wrapper">
|
||||
<div id="preloader" class="loader"></div>
|
||||
<div id="preloader-msg" class="loading-msg"></div>
|
||||
<div id="preloader-error" class="loading-error"></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>
|
||||
@@ -115,7 +161,7 @@
|
||||
<span id="notice">
|
||||
<script type="text/javascript">
|
||||
// Must be text/javascript rather than application/javascript otherwise IE won't recognise it...
|
||||
if (navigator.userAgent && navigator.userAgent.match(/MSIE \d\d?\./)) {
|
||||
if (navigator.userAgent && navigator.userAgent.match(/Trident/)) {
|
||||
document.write("Internet Explorer is not supported, please use Firefox or Chrome instead");
|
||||
alert("Internet Explorer is not supported, please use Firefox or Chrome instead");
|
||||
}
|
||||
@@ -180,7 +226,22 @@
|
||||
</div>
|
||||
<div class="textarea-wrapper no-select">
|
||||
<div id="input-highlighter" class="no-select"></div>
|
||||
<textarea id="input-text"></textarea>
|
||||
<textarea id="input-text" spellcheck="false"></textarea>
|
||||
<div id="input-file">
|
||||
<div class="file-overlay"></div>
|
||||
<div style="position: relative; height: 100%;">
|
||||
<div class="card">
|
||||
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon"/>
|
||||
<div class="card-body">
|
||||
<button type="button" class="close" id="input-file-close">×</button>
|
||||
Name: <span id="input-file-name"></span><br>
|
||||
Size: <span id="input-file-size"></span><br>
|
||||
Type: <span id="input-file-type"></span><br>
|
||||
Loaded: <span id="input-file-loaded"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -201,7 +262,28 @@
|
||||
<div class="textarea-wrapper">
|
||||
<div id="output-highlighter" class="no-select"></div>
|
||||
<div id="output-html"></div>
|
||||
<textarea id="output-text" readonly="readonly"></textarea>
|
||||
<textarea id="output-text" readonly="readonly" spellcheck="false"></textarea>
|
||||
<img id="show-file-overlay" aria-hidden="true" src="<%- require('../static/images/file-32x32.png') %>" alt="Show file overlay" title="Show file overlay"/>
|
||||
<div id="output-file">
|
||||
<div class="file-overlay"></div>
|
||||
<div style="position: relative; height: 100%;">
|
||||
<div class="card">
|
||||
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon"/>
|
||||
<div class="card-body">
|
||||
Size: <span id="output-file-size"></span><br>
|
||||
<button id="output-file-download" type="button" class="btn btn-primary">Download</button>
|
||||
<div class="input-group">
|
||||
<span class="input-group-btn">
|
||||
<button id="output-file-slice" type="button" class="btn btn-default" title="View slice">🔍</button>
|
||||
</span>
|
||||
<input type="number" class="form-control" id="output-file-slice-from" placeholder="From" value="0" step="1024" min="0">
|
||||
<div class="input-group-addon">to</div>
|
||||
<input type="number" class="form-control" id="output-file-slice-to" placeholder="To" value="2048" step="1024" min="0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="output-loader">
|
||||
<div class="loader"></div>
|
||||
<div class="loading-msg"></div>
|
||||
@@ -308,31 +390,46 @@
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="checkbox" option="updateUrl" id="updateUrl" checked />
|
||||
<label for="updateUrl"> Update the URL when the input or recipe changes </label>
|
||||
<label for="updateUrl"> Update the URL when the input or recipe changes</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="checkbox" option="showHighlighter" id="showHighlighter" checked />
|
||||
<label for="showHighlighter"> Highlight selected bytes in output and input (when possible) </label>
|
||||
<label for="showHighlighter"> Highlight selected bytes in output and input (when possible)</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="checkbox" option="treatAsUtf8" id="treatAsUtf8" checked />
|
||||
<label for="treatAsUtf8"> Treat output as UTF-8 if possible </label>
|
||||
<label for="treatAsUtf8"> Treat output as UTF-8 if possible</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="checkbox" option="wordWrap" id="wordWrap" checked />
|
||||
<label for="wordWrap"> Word wrap the input and output </label>
|
||||
<label for="wordWrap"> Word wrap the input and output</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="checkbox" option="showErrors" id="showErrors" checked />
|
||||
<label for="showErrors"> Operation error reporting (recommended) </label>
|
||||
<label for="showErrors"> Operation error reporting (recommended)</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="checkbox" option="useMetaKey" id="useMetaKey" />
|
||||
<label for="useMetaKey"> Use meta key for keybindings (Windows ⊞/Command ⌘) </label>
|
||||
<label for="useMetaKey"> Use meta key for keybindings (Windows ⊞/Command ⌘)</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="number" option="errorTimeout" id="errorTimeout" />
|
||||
<label for="errorTimeout"> Operation error timeout in ms (0 for never) </label>
|
||||
<label for="errorTimeout"> Operation error timeout in ms (0 for never)</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="number" option="ioDisplayThreshold" id="ioDisplayThreshold" />
|
||||
<label for="ioDisplayThreshold"> Size threshold for treating the input and output as a file (KiB)</label>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<select option="logLevel" id="logLevel">
|
||||
<option value="silent">Silent</option>
|
||||
<option value="error">Error</option>
|
||||
<option value="warn">Warn</option>
|
||||
<option value="info">Info</option>
|
||||
<option value="debug">Debug</option>
|
||||
<option value="trace">Trace</option>
|
||||
</select>
|
||||
<label for="logLevel"> Console logging level</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@@ -438,8 +535,9 @@
|
||||
</a>
|
||||
</blockquote>
|
||||
<div class="collapse" id="faq-load-files">
|
||||
<p>Yes! Just drag your file over the input box and drop it. The contents of the file will be converted into hexadecimal and the 'From Hex' operation will be added to the beginning of the recipe (if it's not already there). This is so that special characters like carriage returns aren't removed by your browser.</p>
|
||||
<p>Please note that loading large files is likely to cause a crash. There's not a lot that can be done about this - browsers just aren't very good at handling and displaying large amounts of data.</p>
|
||||
<p>Yes! Just drag your file over the input box and drop it.</p>
|
||||
<p>CyberChef can handle files up to around 500MB (depending on your browser), however some of the operations may take a very long time to run over this much data.</p>
|
||||
<p>If the output is larger than a certain threshold (default 1MiB), it will be presented to you as a file available for download. Slices of the file can be viewed in the output if you need to inspect them.</p>
|
||||
</div>
|
||||
<blockquote>
|
||||
<a data-toggle="collapse" data-target="#faq-fork">
|
||||
@@ -482,7 +580,7 @@
|
||||
|
||||
|
||||
<br>
|
||||
<p>There are around 150 useful operations in CyberChef for anyone working on anything vaguely Internet-related, whether you just want to convert a timestamp to a different format, decompress gzipped data, create a SHA3 hash, or parse an X.509 certificate to find out who issued it.</p>
|
||||
<p>There are around 200 useful operations in CyberChef for anyone working on anything vaguely Internet-related, whether you just want to convert a timestamp to a different format, decompress gzipped data, create a SHA3 hash, or parse an X.509 certificate to find out who issued it.</p>
|
||||
<p>It’s the Cyber Swiss Army Knife.</p>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="keybindings" style="padding: 20px;">
|
||||
|
||||
@@ -12,6 +12,7 @@ import "babel-polyfill";
|
||||
import "bootstrap";
|
||||
import "bootstrap-switch";
|
||||
import "bootstrap-colorpicker";
|
||||
import moment from "moment-timezone";
|
||||
import CanvasComponents from "../core/lib/canvascomponents.js";
|
||||
|
||||
// CyberChef
|
||||
@@ -38,15 +39,17 @@ function main() {
|
||||
];
|
||||
|
||||
const defaultOptions = {
|
||||
updateUrl: true,
|
||||
showHighlighter: true,
|
||||
treatAsUtf8: true,
|
||||
wordWrap: true,
|
||||
showErrors: true,
|
||||
errorTimeout: 4000,
|
||||
attemptHighlight: true,
|
||||
theme: "classic",
|
||||
useMetaKey: false
|
||||
updateUrl: true,
|
||||
showHighlighter: true,
|
||||
treatAsUtf8: true,
|
||||
wordWrap: true,
|
||||
showErrors: true,
|
||||
errorTimeout: 4000,
|
||||
attemptHighlight: true,
|
||||
theme: "classic",
|
||||
useMetaKey: false,
|
||||
ioDisplayThreshold: 512,
|
||||
logLevel: "info"
|
||||
};
|
||||
|
||||
document.removeEventListener("DOMContentLoaded", main, false);
|
||||
@@ -54,9 +57,6 @@ function main() {
|
||||
window.app.setup();
|
||||
}
|
||||
|
||||
// Fix issues with browsers that don't support console.log()
|
||||
window.console = console || {log: function() {}, error: function() {}};
|
||||
|
||||
window.compileTime = moment.tz(COMPILE_TIME, "DD/MM/YYYY HH:mm:ss z", "UTC").valueOf();
|
||||
window.compileMessage = COMPILE_MSG;
|
||||
|
||||
@@ -64,3 +64,4 @@ window.compileMessage = COMPILE_MSG;
|
||||
window.CanvasComponents = CanvasComponents;
|
||||
|
||||
document.addEventListener("DOMContentLoaded", main, false);
|
||||
|
||||
|
||||
BIN
src/web/static/images/file-128x128.png
Normal file
BIN
src/web/static/images/file-128x128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
src/web/static/images/file-32x32.png
Normal file
BIN
src/web/static/images/file-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
33
src/web/static/sitemap.js
Normal file
33
src/web/static/sitemap.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import sm from "sitemap";
|
||||
import OperationConfig from "../../core/config/MetaConfig.js";
|
||||
|
||||
|
||||
/**
|
||||
* Generates an XML sitemap for all CyberChef operations and a number of recipes.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
const sitemap = sm.createSitemap({
|
||||
hostname: "https://gchq.github.io/CyberChef",
|
||||
});
|
||||
|
||||
sitemap.add({
|
||||
url: "/",
|
||||
changefreq: "weekly",
|
||||
priority: 1.0
|
||||
});
|
||||
|
||||
for (let op in OperationConfig) {
|
||||
sitemap.add({
|
||||
url: `/?op=${encodeURIComponent(op)}`,
|
||||
changeFreq: "yearly",
|
||||
priority: 0.5
|
||||
});
|
||||
}
|
||||
|
||||
const xml = sitemap.toString();
|
||||
|
||||
console.log(xml); // eslint-disable-line no-console
|
||||
@@ -28,3 +28,56 @@
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
|
||||
transition: 0.3s;
|
||||
width: 400px;
|
||||
height: 150px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-family: var(--primary-font-family);
|
||||
color: var(--primary-font-colour);
|
||||
line-height: 30px;
|
||||
background-color: var(--primary-background-colour);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.card>img {
|
||||
float: left;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
margin-left: 10px;
|
||||
margin-top: 11px;
|
||||
}
|
||||
|
||||
.card-body .close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
float: left;
|
||||
padding: 16px;
|
||||
width: 250px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.card-body>.btn {
|
||||
margin-bottom: 15px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.card input[type=number] {
|
||||
padding-right: 6px;
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
/* Libraries */
|
||||
import "google-code-prettify/src/prettify.css";
|
||||
import "highlight.js/styles/vs.css";
|
||||
|
||||
/* Frameworks */
|
||||
import "./vendors/bootstrap.less";
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
}
|
||||
|
||||
.textarea-wrapper textarea,
|
||||
.textarea-wrapper div {
|
||||
.textarea-wrapper>div {
|
||||
font-family: var(--fixed-width-font-family);
|
||||
font-size: var(--fixed-width-font-size);
|
||||
color: var(--fixed-width-font-colour);
|
||||
@@ -77,6 +77,32 @@
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
#input-file,
|
||||
#output-file {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.file-overlay {
|
||||
position: absolute;
|
||||
opacity: 0.8;
|
||||
background-color: var(--title-background-colour);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#show-file-overlay {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 15px;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.io-btn-group {
|
||||
float: right;
|
||||
margin-top: -4px;
|
||||
@@ -92,6 +118,8 @@
|
||||
font-family: var(--fixed-width-font-family);
|
||||
font-weight: normal;
|
||||
font-size: 8pt;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#input-info {
|
||||
|
||||
@@ -74,6 +74,14 @@
|
||||
transition: all 0.1s ease-in;
|
||||
}
|
||||
|
||||
.loading-error {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 600px;
|
||||
left: calc(50% - 300px);
|
||||
top: 10%;
|
||||
}
|
||||
|
||||
|
||||
/* Loaded */
|
||||
.loaded .loading-msg {
|
||||
|
||||
@@ -64,7 +64,8 @@ a:focus {
|
||||
.alert,
|
||||
.modal-content,
|
||||
.tooltip-inner,
|
||||
.dropdown-menu {
|
||||
.dropdown-menu,
|
||||
.input-group-addon {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
@@ -187,6 +188,15 @@ optgroup {
|
||||
color: var(--primary-font-colour);
|
||||
}
|
||||
|
||||
.input-group-addon:not(:first-child):not(:last-child) {
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.input-group-btn:first-child>.btn {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Bootstrap-switch */
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint no-console: 0 */
|
||||
|
||||
/**
|
||||
* TestRunner.js
|
||||
*
|
||||
@@ -12,21 +14,27 @@ import "babel-polyfill";
|
||||
|
||||
import TestRegister from "./TestRegister.js";
|
||||
import "./tests/operations/Base58.js";
|
||||
import "./tests/operations/Base64.js";
|
||||
import "./tests/operations/BCD.js";
|
||||
import "./tests/operations/BitwiseOp.js";
|
||||
import "./tests/operations/BSON.js";
|
||||
import "./tests/operations/ByteRepr.js";
|
||||
import "./tests/operations/CharEnc.js";
|
||||
import "./tests/operations/Checksum.js";
|
||||
import "./tests/operations/Cipher.js";
|
||||
import "./tests/operations/Code.js";
|
||||
import "./tests/operations/Compress.js";
|
||||
import "./tests/operations/DateTime.js";
|
||||
import "./tests/operations/FlowControl.js";
|
||||
import "./tests/operations/Hash.js";
|
||||
import "./tests/operations/Hexdump.js";
|
||||
import "./tests/operations/Image.js";
|
||||
import "./tests/operations/MorseCode.js";
|
||||
import "./tests/operations/MS.js";
|
||||
import "./tests/operations/PHP.js";
|
||||
import "./tests/operations/NetBIOS.js";
|
||||
import "./tests/operations/OTP.js";
|
||||
import "./tests/operations/Regex.js";
|
||||
import "./tests/operations/StrUtils.js";
|
||||
import "./tests/operations/SeqUtils.js";
|
||||
|
||||
|
||||
56
test/tests/operations/BSON.js
Executable file
56
test/tests/operations/BSON.js
Executable file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* BSON tests.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister.js";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "BSON serialise: nothing",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "BSON serialise",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "BSON serialise: basic",
|
||||
input: "{\"hello\":\"world\"}",
|
||||
expectedOutput: "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "BSON serialise",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "BSON deserialise: nothing",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "BSON deserialise",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "BSON deserialise: basic",
|
||||
input: "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00",
|
||||
expectedOutput: "{\n \"hello\": \"world\"\n}",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "BSON deserialise",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
119
test/tests/operations/Base64.js
Normal file
119
test/tests/operations/Base64.js
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Base64 tests.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister.js";
|
||||
|
||||
const ALL_BYTES = [
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
|
||||
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
|
||||
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
|
||||
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
|
||||
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
|
||||
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
|
||||
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
|
||||
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
|
||||
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
|
||||
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
|
||||
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
|
||||
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
|
||||
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
|
||||
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
|
||||
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
|
||||
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
|
||||
].join("");
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "To Base64: nothing",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Base64",
|
||||
args: ["A-Za-z0-9+/="],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Base64: Hello, World!",
|
||||
input: "Hello, World!",
|
||||
expectedOutput: "SGVsbG8sIFdvcmxkIQ==",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Base64",
|
||||
args: ["A-Za-z0-9+/="],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Base64: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "4YOc4YOjIOGDnuGDkOGDnOGDmOGDmeGDkOGDoQ==",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Base64",
|
||||
args: ["A-Za-z0-9+/="],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Base64: All bytes",
|
||||
input: ALL_BYTES,
|
||||
expectedOutput: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Base64",
|
||||
args: ["A-Za-z0-9+/="],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "From Base64: nothing",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Base64",
|
||||
args: ["A-Za-z0-9+/=", true],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "From Base64: Hello, World!",
|
||||
input: "SGVsbG8sIFdvcmxkIQ==",
|
||||
expectedOutput: "Hello, World!",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Base64",
|
||||
args: ["A-Za-z0-9+/=", true],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "From Base64: UTF-8",
|
||||
input: "4YOc4YOjIOGDnuGDkOGDnOGDmOGDmeGDkOGDoQ==",
|
||||
expectedOutput: "ნუ პანიკას",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Base64",
|
||||
args: ["A-Za-z0-9+/=", true],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "From Base64: All bytes",
|
||||
input: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==",
|
||||
expectedOutput: ALL_BYTES,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Base64",
|
||||
args: ["A-Za-z0-9+/=", true],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
151
test/tests/operations/ByteRepr.js
Normal file → Executable file
151
test/tests/operations/ByteRepr.js
Normal file → Executable file
@@ -7,6 +7,25 @@
|
||||
*/
|
||||
import TestRegister from "../../TestRegister.js";
|
||||
|
||||
const ALL_BYTES = [
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
|
||||
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
|
||||
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
|
||||
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
|
||||
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
|
||||
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
|
||||
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
|
||||
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
|
||||
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
|
||||
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
|
||||
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
|
||||
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
|
||||
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
|
||||
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
|
||||
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
|
||||
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
|
||||
].join("");
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "To Octal: nothing",
|
||||
@@ -74,4 +93,136 @@ TestRegister.addTests([
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "To Hex: nothing",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Hex",
|
||||
args: ["Space"]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "To Hex: All bytes",
|
||||
input: ALL_BYTES,
|
||||
expectedOutput: "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Hex",
|
||||
args: ["Space"]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "To Hex: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "e1839ce183a320e1839ee18390e1839ce18398e18399e18390e183a1",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Hex",
|
||||
args: ["None"]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "From Hex: nothing",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hex",
|
||||
args: ["Space"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "From Hex: All bytes",
|
||||
input: "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff",
|
||||
expectedOutput: ALL_BYTES,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hex",
|
||||
args: ["Space"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "From Hex: UTF-8",
|
||||
input: "e1839ce183a320e1839ee18390e1839ce18398e18399e18390e183a1",
|
||||
expectedOutput: "ნუ პანიკას",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hex",
|
||||
args: ["None"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "To Charcode: nothing",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Charcode",
|
||||
args: ["Space", 16]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "To Charcode: All bytes",
|
||||
input: ALL_BYTES,
|
||||
expectedOutput: "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Charcode",
|
||||
args: ["Space", 16]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "To Charcode: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "10dc 10e3 20 10de 10d0 10dc 10d8 10d9 10d0 10e1",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Charcode",
|
||||
args: ["Space", 16]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "From Charcode: nothing",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Charcode",
|
||||
args: ["Space", 16]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "From Charcode: All bytes",
|
||||
input: "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff",
|
||||
expectedOutput: ALL_BYTES,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Charcode",
|
||||
args: ["Space", 16]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "From Charcode: UTF-8",
|
||||
input: "10dc 10e3 20 10de 10d0 10dc 10d8 10d9 10d0 10e1",
|
||||
expectedOutput: "ნუ პანიკას",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Charcode",
|
||||
args: ["Space", 16]
|
||||
}
|
||||
]
|
||||
},
|
||||
]);
|
||||
|
||||
120
test/tests/operations/Checksum.js
Normal file
120
test/tests/operations/Checksum.js
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Checksum tests.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister.js";
|
||||
|
||||
const BASIC_STRING = "The ships hung in the sky in much the same way that bricks don't.";
|
||||
const UTF8_STR = "ნუ პანიკას";
|
||||
const ALL_BYTES = [
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
|
||||
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
|
||||
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
|
||||
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
|
||||
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
|
||||
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
|
||||
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
|
||||
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
|
||||
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
|
||||
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
|
||||
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
|
||||
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
|
||||
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
|
||||
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
|
||||
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
|
||||
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
|
||||
].join("");
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "CRC-16: nothing",
|
||||
input: "",
|
||||
expectedOutput: "0000",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "CRC-16 Checksum",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "CRC-16: basic string",
|
||||
input: BASIC_STRING,
|
||||
expectedOutput: "0c70",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "CRC-16 Checksum",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "CRC-16: UTF-8",
|
||||
input: UTF8_STR,
|
||||
expectedOutput: "dcf6",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "CRC-16 Checksum",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "CRC-16: all bytes",
|
||||
input: ALL_BYTES,
|
||||
expectedOutput: "bad3",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "CRC-16 Checksum",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "CRC-32: nothing",
|
||||
input: "",
|
||||
expectedOutput: "00000000",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "CRC-32 Checksum",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "CRC-32: basic string",
|
||||
input: BASIC_STRING,
|
||||
expectedOutput: "bf4b739c",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "CRC-32 Checksum",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "CRC-32: UTF-8",
|
||||
input: UTF8_STR,
|
||||
expectedOutput: "87553290",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "CRC-32 Checksum",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "CRC-32: all bytes",
|
||||
input: ALL_BYTES,
|
||||
expectedOutput: "29058c73",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "CRC-32 Checksum",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
]);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -310,4 +310,26 @@ TestRegister.addTests([
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "CSS selector",
|
||||
input: '<div id="test">\n<p class="a">hello</p>\n<p>world</p>\n<p class="a">again</p>\n</div>',
|
||||
expectedOutput: '<p class="a">hello</p>\n<p class="a">again</p>',
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "CSS selector",
|
||||
"args": ["#test p.a", "\\n"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "XPath expression",
|
||||
input: '<div id="test">\n<p class="a">hello</p>\n<p>world</p>\n<p class="a">again</p>\n</div>',
|
||||
expectedOutput: '<p class="a">hello</p>\n<p class="a">again</p>',
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XPath expression",
|
||||
"args": ["/div/p[@class=\"a\"]", "\\n"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
106
test/tests/operations/FlowControl.js
Normal file → Executable file
106
test/tests/operations/FlowControl.js
Normal file → Executable file
@@ -8,6 +8,25 @@
|
||||
*/
|
||||
import TestRegister from "../../TestRegister.js";
|
||||
|
||||
const ALL_BYTES = [
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
|
||||
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
|
||||
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
|
||||
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
|
||||
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
|
||||
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
|
||||
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
|
||||
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
|
||||
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
|
||||
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
|
||||
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
|
||||
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
|
||||
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
|
||||
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
|
||||
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
|
||||
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
|
||||
].join("");
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Fork: nothing",
|
||||
@@ -37,7 +56,7 @@ TestRegister.addTests([
|
||||
},
|
||||
{
|
||||
name: "Fork, (expect) Error, Merge",
|
||||
input: "1\n2\na\n4",
|
||||
input: "1.1\n2.5\na\n3.4",
|
||||
expectedError: true,
|
||||
recipeConfig: [
|
||||
{
|
||||
@@ -45,8 +64,8 @@ TestRegister.addTests([
|
||||
args: ["\n", "\n", false],
|
||||
},
|
||||
{
|
||||
op: "To Base",
|
||||
args: [16],
|
||||
op: "Object Identifier to Hex",
|
||||
args: [],
|
||||
},
|
||||
{
|
||||
op: "Merge",
|
||||
@@ -251,7 +270,7 @@ TestRegister.addTests([
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Conditional Jump: Skips negatively",
|
||||
name: "Conditional Jump: Skips backwards",
|
||||
input: [
|
||||
"match",
|
||||
].join("\n"),
|
||||
@@ -290,4 +309,83 @@ TestRegister.addTests([
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Register: RC4 key",
|
||||
input: "http://malwarez.biz/beacon.php?key=0e932a5c&data=8db7d5ebe38663a54ecbb334e3db11",
|
||||
expectedOutput: "All the secrets",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Register",
|
||||
args: ["key=([\\da-f]*)", true, false]
|
||||
},
|
||||
{
|
||||
op: "Find / Replace",
|
||||
args: [
|
||||
{
|
||||
"option": "Regex",
|
||||
"string": ".*data=(.*)"
|
||||
}, "$1", true, false, true
|
||||
]
|
||||
},
|
||||
{
|
||||
op: "RC4",
|
||||
args: [
|
||||
{
|
||||
"option": "Hex",
|
||||
"string": "$R0"
|
||||
}, "Hex", "Latin1"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Register: AES key",
|
||||
input: "51e201d463698ef5f717f71f5b4712af20be674b3bff53d38546396ee61daac4908e319ca3fcf7089bfb6b38ea99e781d26e577ba9dd6f311a39420b8978e93014b042d44726caedf5436eaf652429c0df94b521676c7c2ce812097c277273c7c72cd89aec8d9fb4a27586ccf6aa0aee224c34ba3bfdf7aeb1ddd477622b91e72c9e709ab60f8daf731ec0cc85ce0f746ff1554a5a3ec291ca40f9e629a872592d988fdd834534aba79c1ad1676769a7c010bf04739ecdb65d95302371d629d9e37e7b4a361da468f1ed5358922d2ea752dd11c366f3017b14aa011d2af03c44f95579098a15e3cf9b4486f8ffe9c239f34de7151f6ca6500fe4b850c3f1c02e801caf3a24464614e42801615b8ffaa07ac8251493ffda7de5ddf3368880c2b95b030f41f8f15066add071a66cf60e5f46f3a230d397b652963a21a53f",
|
||||
expectedOutput: `"You know," said Arthur, "it's at times like this, when I'm trapped in a Vogon airlock with a man from Betelgeuse, and about to die of asphyxiation in deep space that I really wish I'd listened to what my mother told me when I was young."
|
||||
"Why, what did she tell you?"
|
||||
"I don't know, I didn't listen."`,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Register",
|
||||
args: ["(.{32})", true, false]
|
||||
},
|
||||
{
|
||||
op: "Drop bytes",
|
||||
args: [0, 32, false]
|
||||
},
|
||||
{
|
||||
op: "AES Decrypt",
|
||||
args: [
|
||||
{
|
||||
"option": "Hex",
|
||||
"string": "1748e7179bd56570d51fa4ba287cc3e5"
|
||||
},
|
||||
{
|
||||
"option": "Hex",
|
||||
"string": "$R0"
|
||||
},
|
||||
"CTR", "Hex", "Raw",
|
||||
{
|
||||
"option": "Hex",
|
||||
"string": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Label, Comment: Complex content",
|
||||
input: ALL_BYTES,
|
||||
expectedOutput: ALL_BYTES,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Label",
|
||||
args: [""]
|
||||
},
|
||||
{
|
||||
op: "Comment",
|
||||
args: [""]
|
||||
}
|
||||
]
|
||||
},
|
||||
]);
|
||||
|
||||
339
test/tests/operations/Hash.js
Normal file → Executable file
339
test/tests/operations/Hash.js
Normal file → Executable file
@@ -415,4 +415,343 @@ TestRegister.addTests([
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "MD5: Complex bytes",
|
||||
input: "10dc10e32010de10d010dc10d810d910d010e12e",
|
||||
expectedOutput: "4f4f02e2646545aa8fc42f613c9aa068",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "From Hex",
|
||||
"args": ["None"]
|
||||
},
|
||||
{
|
||||
"op": "MD5",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA1: Complex bytes",
|
||||
input: "10dc10e32010de10d010dc10d810d910d010e12e",
|
||||
expectedOutput: "2c5400aaee7e8ad4cad29bfbdf8d566924e5442c",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "From Hex",
|
||||
"args": ["None"]
|
||||
},
|
||||
{
|
||||
"op": "SHA1",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA2 224: Complex bytes",
|
||||
input: "10dc10e32010de10d010dc10d810d910d010e12e",
|
||||
expectedOutput: "66c166eba2529ecc44a7b7b218a64a8e3892f873c8d231e8e3c1ef3d",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "From Hex",
|
||||
"args": ["None"]
|
||||
},
|
||||
{
|
||||
"op": "SHA2",
|
||||
"args": ["224"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA2 256: Complex bytes",
|
||||
input: "10dc10e32010de10d010dc10d810d910d010e12e",
|
||||
expectedOutput: "186ffd22c3af83995afa4a0316023f81a7f8834fd16bd2ed358c7b1b8182ba41",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "From Hex",
|
||||
"args": ["None"]
|
||||
},
|
||||
{
|
||||
"op": "SHA2",
|
||||
"args": ["256"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA2 384: Complex bytes",
|
||||
input: "10dc10e32010de10d010dc10d810d910d010e12e",
|
||||
expectedOutput: "2a6369ffec550ea0bfb810b3b8246b7d6b7f060edfae88441f0f242b98b91549aa4ff407de38c6d03b5f377434ad2f36",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "From Hex",
|
||||
"args": ["None"]
|
||||
},
|
||||
{
|
||||
"op": "SHA2",
|
||||
"args": ["384"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA2 512: Complex bytes",
|
||||
input: "10dc10e32010de10d010dc10d810d910d010e12e",
|
||||
expectedOutput: "544ae686522c05b70d12b460b5b39ea0a758eb4027333edbded7e2b3f467aa605804f71f54db61a7bbe50e6e7898510635efd6721fd418a9ea4d05b286d12806",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "From Hex",
|
||||
"args": ["None"]
|
||||
},
|
||||
{
|
||||
"op": "SHA2",
|
||||
"args": ["512"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA3 224: Complex bytes",
|
||||
input: "10dc10e32010de10d010dc10d810d910d010e12e",
|
||||
expectedOutput: "e2c07562ee8c2d73e3dd309efea257159abd0948ebc14619bab9ffb3",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "From Hex",
|
||||
"args": ["None"]
|
||||
},
|
||||
{
|
||||
"op": "SHA3",
|
||||
"args": ["224"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA3 256: Complex bytes",
|
||||
input: "10dc10e32010de10d010dc10d810d910d010e12e",
|
||||
expectedOutput: "55a55275387586afd1ed64757c9ee7ad1d96ca81a5b7b742c40127856ee78a2d",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "From Hex",
|
||||
"args": ["None"]
|
||||
},
|
||||
{
|
||||
"op": "SHA3",
|
||||
"args": ["256"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA3 384: Complex bytes",
|
||||
input: "10dc10e32010de10d010dc10d810d910d010e12e",
|
||||
expectedOutput: "39f8796dd697dc39e5a943817833793f2c29dc0d1adc7037854c0fb51e135c6bd26b113240c4fb1e3fcc16ff8690c91a",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "From Hex",
|
||||
"args": ["None"]
|
||||
},
|
||||
{
|
||||
"op": "SHA3",
|
||||
"args": ["384"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA3 512: Complex bytes",
|
||||
input: "10dc10e32010de10d010dc10d810d910d010e12e",
|
||||
expectedOutput: "ee9061bed83b1ad1e2fc4a4bac72a5a65a23a0fa55193b808af0a3e2013b718a5a3e40474765b4f93d1b2747401058a5b58099cc890a159db92b2ea816287add",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "From Hex",
|
||||
"args": ["None"]
|
||||
},
|
||||
{
|
||||
"op": "SHA3",
|
||||
"args": ["512"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "MD5: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "2e93ee2b5b2a337ccb678c7db12eff1b",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "MD5",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA1: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "87f483b1515dce672be044bf183ae8103e3b2d4b",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "SHA1",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA2 224: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "563ca57b500157717961a5fa87ce42c6db76a488c98ea9c28d620770",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "SHA2",
|
||||
"args": ["224"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA2 256: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "36abbb4622ffff06aa3e3cea266765601b21457bb3755a0a2cf0a206422863c1",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "SHA2",
|
||||
"args": ["256"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA2 384: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "140b929391a66c9a943bcd60e6964f0d19526d3bc9ba020fbb29aae51cddb8e63a78784d8770f1d36335bf4efff8c131",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "SHA2",
|
||||
"args": ["384"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA2 512: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "04a7887c400bf647b7c67b9a0f1ada70d176348b5afdfebea184f7e62748849828669c7b5160be99455fdbf625589bd1689c003bc06ef60c39607d825a2f8838",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "SHA2",
|
||||
"args": ["512"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA3 224: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "b3ffc9620949f879cb561fb240452494e2566cb4e4f701a85715e14f",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "SHA3",
|
||||
"args": ["224"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA3 256: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "b5f247d725b46546c832502cd07bccb5d4de0c41a6665d3944ed2cc55cd9d156",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "SHA3",
|
||||
"args": ["256"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA3 384: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "93e87b9aa8c9c47eba146adac357c525b418b71677f6db01d1c760d87b058682e639c8d43a8bfe91529cecd9800700e3",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "SHA3",
|
||||
"args": ["384"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "SHA3 512: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "1fbc484b5184982561795162757717474eebc846ca9f10029a75a54cdd897a7b48d1db42f2478fa1d5d213a0dd7de71c809cb19c60581ba57e7289d29408fb36",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "SHA3",
|
||||
"args": ["512"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Bcrypt compare: dolphin",
|
||||
input: "dolphin",
|
||||
expectedOutput: "Match: dolphin",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Bcrypt compare",
|
||||
args: ["$2a$10$qyon0LQCmMxpFFjwWH6Qh.dDdhqntQh./IN0RXCc3XIMILuOYZKgK"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Scrypt: RFC test vector 1",
|
||||
input: "",
|
||||
expectedOutput: "77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Scrypt",
|
||||
args: [
|
||||
{
|
||||
"option": "Latin1",
|
||||
"string": ""
|
||||
},
|
||||
16, 1, 1, 64
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Scrypt: RFC test vector 2",
|
||||
input: "password",
|
||||
expectedOutput: "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Scrypt",
|
||||
args: [
|
||||
{
|
||||
"option": "Latin1",
|
||||
"string": "NaCl"
|
||||
},
|
||||
1024, 8, 16, 64
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Scrypt: RFC test vector 3",
|
||||
input: "pleaseletmein",
|
||||
expectedOutput: "7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Scrypt",
|
||||
args: [
|
||||
{
|
||||
"option": "Latin1",
|
||||
"string": "SodiumChloride"
|
||||
},
|
||||
16384, 8, 1, 64
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
/*{ // This takes a LONG time to run (over a minute usually).
|
||||
name: "Scrypt: RFC test vector 4",
|
||||
input: "pleaseletmein",
|
||||
expectedOutput: "2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Scrypt",
|
||||
args: [
|
||||
{
|
||||
"option": "Latin1",
|
||||
"string": "SodiumChloride"
|
||||
},
|
||||
1048576, 8, 1, 64
|
||||
]
|
||||
}
|
||||
]
|
||||
},*/
|
||||
]);
|
||||
|
||||
235
test/tests/operations/Hexdump.js
Executable file
235
test/tests/operations/Hexdump.js
Executable file
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* Hexdump tests.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister.js";
|
||||
|
||||
const ALL_BYTES = [
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
|
||||
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
|
||||
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
|
||||
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
|
||||
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
|
||||
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
|
||||
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
|
||||
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
|
||||
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
|
||||
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
|
||||
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
|
||||
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
|
||||
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
|
||||
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
|
||||
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
|
||||
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
|
||||
].join("");
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Hexdump: nothing",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Hexdump",
|
||||
args: [16, false, false]
|
||||
},
|
||||
{
|
||||
op: "From Hexdump",
|
||||
args: []
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Hexdump: Hello, World!",
|
||||
input: "Hello, World!",
|
||||
expectedOutput: "Hello, World!",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Hexdump",
|
||||
args: [16, false, false]
|
||||
},
|
||||
{
|
||||
op: "From Hexdump",
|
||||
args: []
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Hexdump: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "ნუ პანიკას",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Hexdump",
|
||||
args: [16, false, false]
|
||||
},
|
||||
{
|
||||
op: "From Hexdump",
|
||||
args: []
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Hexdump: All bytes",
|
||||
input: ALL_BYTES,
|
||||
expectedOutput: ALL_BYTES,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Hexdump",
|
||||
args: [16, false, false]
|
||||
},
|
||||
{
|
||||
op: "From Hexdump",
|
||||
args: []
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Hexdump: UTF-8",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: `00000000 e1 83 9c e1 83 a3 20 e1 83 9e e1 83 90 e1 83 9c |á..á.£ á..á..á..|
|
||||
00000010 e1 83 98 e1 83 99 e1 83 90 e1 83 a1 |á..á..á..á.¡|`,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Hexdump",
|
||||
args: [16, false, false]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "To Hexdump: All bytes",
|
||||
input: ALL_BYTES,
|
||||
expectedOutput: `00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|
||||
00000010 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f |................|
|
||||
00000020 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f | !"#$%&'()*+,-./|
|
||||
00000030 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f |0123456789:;<=>?|
|
||||
00000040 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f |@ABCDEFGHIJKLMNO|
|
||||
00000050 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f |PQRSTUVWXYZ[\\]^_|
|
||||
00000060 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f |\`abcdefghijklmno|
|
||||
00000070 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f |pqrstuvwxyz{|}~.|
|
||||
00000080 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f |................|
|
||||
00000090 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f |................|
|
||||
000000a0 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af |\xa0¡¢£¤¥¦§¨©ª«¬.®¯|
|
||||
000000b0 b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf |°±²³´µ¶·¸¹º»¼½¾¿|
|
||||
000000c0 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf |ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ|
|
||||
000000d0 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df |ÐÑÒÓÔÕÖרÙÚÛÜÝÞß|
|
||||
000000e0 e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef |àáâãäåæçèéêëìíîï|
|
||||
000000f0 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff |ðñòóôõö÷øùúûüýþÿ|`,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "To Hexdump",
|
||||
args: [16, false, false]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "From Hexdump: xxd",
|
||||
input: `00000000: 0001 0203 0405 0607 0809 0a0b 0c0d 0e0f ................
|
||||
00000010: 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f ................
|
||||
00000020: 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f !"#$%&'()*+,-./
|
||||
00000030: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0123456789:;<=>?
|
||||
00000040: 4041 4243 4445 4647 4849 4a4b 4c4d 4e4f @ABCDEFGHIJKLMNO
|
||||
00000050: 5051 5253 5455 5657 5859 5a5b 5c5d 5e5f PQRSTUVWXYZ[\\]^_
|
||||
00000060: 6061 6263 6465 6667 6869 6a6b 6c6d 6e6f \`abcdefghijklmno
|
||||
00000070: 7071 7273 7475 7677 7879 7a7b 7c7d 7e7f pqrstuvwxyz{|}~.
|
||||
00000080: 8081 8283 8485 8687 8889 8a8b 8c8d 8e8f ................
|
||||
00000090: 9091 9293 9495 9697 9899 9a9b 9c9d 9e9f ................
|
||||
000000a0: a0a1 a2a3 a4a5 a6a7 a8a9 aaab acad aeaf ................
|
||||
000000b0: b0b1 b2b3 b4b5 b6b7 b8b9 babb bcbd bebf ................
|
||||
000000c0: c0c1 c2c3 c4c5 c6c7 c8c9 cacb cccd cecf ................
|
||||
000000d0: d0d1 d2d3 d4d5 d6d7 d8d9 dadb dcdd dedf ................
|
||||
000000e0: e0e1 e2e3 e4e5 e6e7 e8e9 eaeb eced eeef ................
|
||||
000000f0: f0f1 f2f3 f4f5 f6f7 f8f9 fafb fcfd feff ................`,
|
||||
expectedOutput: ALL_BYTES,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hexdump",
|
||||
args: []
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "From Hexdump: Wireshark",
|
||||
input: `00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ........ ........
|
||||
00000010 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f ........ ........
|
||||
00000020 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f !"#$%&' ()*+,-./
|
||||
00000030 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 01234567 89:;<=>?
|
||||
00000040 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFG HIJKLMNO
|
||||
00000050 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f PQRSTUVW XYZ[\\]^_
|
||||
00000060 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f \`abcdefg hijklmno
|
||||
00000070 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f pqrstuvw xyz{|}~.
|
||||
00000080 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f ........ ........
|
||||
00000090 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f ........ ........
|
||||
000000A0 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af ........ ........
|
||||
000000B0 b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf ........ ........
|
||||
000000C0 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf ........ ........
|
||||
000000D0 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df ........ ........
|
||||
000000E0 e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef ........ ........
|
||||
000000F0 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff ........ ........
|
||||
`,
|
||||
expectedOutput: ALL_BYTES,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hexdump",
|
||||
args: []
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "From Hexdump: 010",
|
||||
input: `0000h: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ................
|
||||
0010h: 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F ................
|
||||
0020h: 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F !"#$%&'()*+,-./
|
||||
0030h: 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 0123456789:;<=>?
|
||||
0040h: 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F @ABCDEFGHIJKLMNO
|
||||
0050h: 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F PQRSTUVWXYZ[\\]^_
|
||||
0060h: 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F \`abcdefghijklmno
|
||||
0070h: 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F pqrstuvwxyz{|}~
|
||||
0080h: 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F €.‚ƒ„…†‡ˆ‰Š‹Œ...
|
||||
0090h: 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F .‘’“”•–—˜™š›œ.žŸ
|
||||
00A0h: A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF \xa0¡¢£¤¥¦§¨©ª«¬®¯
|
||||
00B0h: B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF °±²³´µ¶·¸¹º»¼½¾¿
|
||||
00C0h: C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ
|
||||
00D0h: D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF ÐÑÒÓÔÕÖרÙÚÛÜÝÞß
|
||||
00E0h: E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF àáâãäåæçèéêëìíîï
|
||||
00F0h: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ðñòóôõö÷øùúûüýþÿ`,
|
||||
expectedOutput: ALL_BYTES,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hexdump",
|
||||
args: []
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "From Hexdump: Linux hexdump",
|
||||
input: `00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|
||||
00000010 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f |................|
|
||||
00000020 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f | !"#$%&'()*+,-./|
|
||||
00000030 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f |0123456789:;<=>?|
|
||||
00000040 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f |@ABCDEFGHIJKLMNO|
|
||||
00000050 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f |PQRSTUVWXYZ[\\]^_|
|
||||
00000060 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f |\`abcdefghijklmno|
|
||||
00000070 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f |pqrstuvwxyz{|}~.|
|
||||
00000080 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f |................|
|
||||
00000090 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f |................|
|
||||
000000a0 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af |................|
|
||||
000000b0 b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf |................|
|
||||
000000c0 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf |................|
|
||||
000000d0 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df |................|
|
||||
000000e0 e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef |................|
|
||||
000000f0 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff |................|
|
||||
00000100`,
|
||||
expectedOutput: ALL_BYTES,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hexdump",
|
||||
args: []
|
||||
}
|
||||
],
|
||||
},
|
||||
]);
|
||||
23
test/tests/operations/OTP.js
Normal file
23
test/tests/operations/OTP.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* OTP HOTP tests.
|
||||
*
|
||||
* @author bwhitn [brian.m.whitney@outlook.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister.js";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Generate HOTP",
|
||||
input: "12345678901234567890",
|
||||
expectedOutput: "URI: otpauth://hotp/OTPAuthentication?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ\n\nPassword: 755224",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Generate HOTP",
|
||||
args: ["", 32, 6, 0],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
59
test/tests/operations/Regex.js
Normal file
59
test/tests/operations/Regex.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* StrUtils tests.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister.js";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Regex: non-HTML op",
|
||||
input: "/<>",
|
||||
expectedOutput: "/<>",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Regular expression",
|
||||
"args": ["User defined", "", true, true, false, false, false, false, "Highlight matches"]
|
||||
},
|
||||
{
|
||||
"op": "Remove whitespace",
|
||||
"args": [true, true, true, true, true, false]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Regex: Dot matches all",
|
||||
input: "Hello\nWorld",
|
||||
expectedOutput: "Hello\nWorld",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Regular expression",
|
||||
"args": ["User defined", ".+", true, true, true, false, false, false, "List matches"]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Regex: Astral off",
|
||||
input: "𝌆😆",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Regular expression",
|
||||
"args": ["User defined", "\\pS", true, true, false, false, false, false, "List matches"]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Regex: Astral on",
|
||||
input: "𝌆😆",
|
||||
expectedOutput: "𝌆\n😆",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Regular expression",
|
||||
"args": ["User defined", "\\pS", true, true, false, false, true, false, "List matches"]
|
||||
}
|
||||
],
|
||||
}
|
||||
]);
|
||||
58
test/tests/operations/StrUtils.js
Normal file → Executable file
58
test/tests/operations/StrUtils.js
Normal file → Executable file
@@ -8,21 +8,6 @@
|
||||
import TestRegister from "../../TestRegister.js";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Regex, non-HTML op",
|
||||
input: "/<>",
|
||||
expectedOutput: "/<>",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Regular expression",
|
||||
"args": ["User defined", "", true, true, false, "Highlight matches"]
|
||||
},
|
||||
{
|
||||
"op": "Remove whitespace",
|
||||
"args": [true, true, true, true, true, false]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Diff, basic usage",
|
||||
input: "testing23\n\ntesting123",
|
||||
@@ -233,13 +218,24 @@ TestRegister.addTests([
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Escape String: quotes",
|
||||
input: "Hello \"World\"! Escape 'these' quotes.",
|
||||
expectedOutput: "Hello \\\"World\\\"! Escape \\'these\\' quotes.",
|
||||
name: "Escape String: single quotes",
|
||||
input: "Escape 'these' quotes.",
|
||||
expectedOutput: "Escape \\'these\\' quotes.",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Escape string",
|
||||
"args": []
|
||||
"args": ["Special chars", "Single", false, true, false]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Escape String: double quotes",
|
||||
input: "Hello \"World\"!",
|
||||
expectedOutput: "Hello \\\"World\\\"!",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Escape string",
|
||||
"args": ["Special chars", "Double", false, true, false]
|
||||
}
|
||||
],
|
||||
},
|
||||
@@ -250,7 +246,7 @@ TestRegister.addTests([
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Escape string",
|
||||
"args": []
|
||||
"args": ["Special chars", "Double", false, true, false]
|
||||
}
|
||||
],
|
||||
},
|
||||
@@ -276,4 +272,26 @@ TestRegister.addTests([
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Escape String: complex",
|
||||
input: "null\0backspace\btab\tnewline\nverticaltab\vformfeed\fcarriagereturn\rdoublequote\"singlequote'hex\xa9unicode\u2665codepoint\u{1D306}",
|
||||
expectedOutput: "null\\0backspace\\btab\\tnewline\\nverticaltab\\x0bformfeed\\fcarriagereturn\\rdoublequote\"singlequote\\'hex\\xa9unicode\\u2665codepoint\\u{1d306}",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Escape string",
|
||||
"args": ["Special chars", "Single", false, true, false]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Unescape String: complex",
|
||||
input: "null\\0backspace\\btab\\tnewline\\nverticaltab\\vformfeed\\fcarriagereturn\\rdoublequote\\\"singlequote\\'hex\\xa9unicode\\u2665codepoint\\u{1D306}",
|
||||
expectedOutput: "null\0backspace\btab\tnewline\nverticaltab\vformfeed\fcarriagereturn\rdoublequote\"singlequote'hex\xa9unicode\u2665codepoint\u{1D306}",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "Unescape string",
|
||||
"args": []
|
||||
}
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -35,7 +35,7 @@ module.exports = {
|
||||
new webpack.ProvidePlugin({
|
||||
$: "jquery",
|
||||
jQuery: "jquery",
|
||||
moment: "moment-timezone"
|
||||
log: "loglevel"
|
||||
}),
|
||||
new webpack.BannerPlugin({
|
||||
banner: banner,
|
||||
@@ -53,7 +53,7 @@ module.exports = {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
exclude: /node_modules\/(?!jsesc)/,
|
||||
loader: "babel-loader?compact=false"
|
||||
},
|
||||
{
|
||||
@@ -108,9 +108,13 @@ module.exports = {
|
||||
children: false,
|
||||
chunks: false,
|
||||
modules: false,
|
||||
warningsFilter: /source-map/,
|
||||
entrypoints: false,
|
||||
warningsFilter: [/source-map/, /dependency is an expression/],
|
||||
},
|
||||
node: {
|
||||
fs: "empty"
|
||||
},
|
||||
performance: {
|
||||
hints: false
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user