Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb34ab4f6a | ||
|
|
08e4232166 | ||
|
|
adf9772928 | ||
|
|
562171ec86 | ||
|
|
e9e162319f | ||
|
|
17c9ffe107 | ||
|
|
1831c84a29 | ||
|
|
7e27449204 | ||
|
|
282476d530 | ||
|
|
fce0728d5d | ||
|
|
0e9ac90607 | ||
|
|
5383f56b26 | ||
|
|
a02484c6cd | ||
|
|
be365f66ef | ||
|
|
011dc09d5e | ||
|
|
fc4d6d2d2e | ||
|
|
9d73127cae | ||
|
|
223743e3b5 | ||
|
|
44ed372f21 | ||
|
|
4d1f970105 | ||
|
|
b28a891a40 | ||
|
|
834ff95702 | ||
|
|
3e93580aa4 | ||
|
|
3c021919dd | ||
|
|
2106e8ddb0 | ||
|
|
3472484601 | ||
|
|
826a8c8a74 | ||
|
|
c66703f0ca | ||
|
|
874e7d8d54 | ||
|
|
4e2b85b8c8 | ||
|
|
5314a456cb | ||
|
|
ba2a5b195c | ||
|
|
f8115671ee | ||
|
|
494279edd8 | ||
|
|
bd6673afed | ||
|
|
210daf7324 | ||
|
|
d60d595254 | ||
|
|
06708949a1 | ||
|
|
da901e20d9 | ||
|
|
c28999ec6f | ||
|
|
6810f38808 | ||
|
|
087cc6b8fd | ||
|
|
10c8101476 | ||
|
|
22028b074a | ||
|
|
3d086beed2 | ||
|
|
777d814e70 | ||
|
|
15c26a95c5 | ||
|
|
f87d3bd1cb | ||
|
|
b9ea1e8c71 | ||
|
|
db232f4ff2 | ||
|
|
70f705afbc | ||
|
|
880df212d5 | ||
|
|
b06acd99ec | ||
|
|
a59de80d18 | ||
|
|
1e8dee9935 | ||
|
|
928f1c3e4b | ||
|
|
014e70a7b1 | ||
|
|
5148b16246 | ||
|
|
b4ae4c5a00 | ||
|
|
70346bce35 | ||
|
|
3539e065fa | ||
|
|
8ffc58b340 | ||
|
|
503e733c81 | ||
|
|
7eabaf0de6 | ||
|
|
a8ad10757c | ||
|
|
111546ad1a | ||
|
|
a8fbd5164e | ||
|
|
8dde732514 | ||
|
|
f1659af5e4 | ||
|
|
e68fb51f44 | ||
|
|
95453131c8 | ||
|
|
c60d5c8e85 | ||
|
|
b31f32a7e7 | ||
|
|
f0b3bd0ede | ||
|
|
de5243ec67 | ||
|
|
c6de3eb2ae | ||
|
|
d7b6f29c81 | ||
|
|
5bebd71a44 | ||
|
|
eb769c7fb4 | ||
|
|
5bc5c0df90 | ||
|
|
cfc3684a16 | ||
|
|
0590020130 | ||
|
|
2a91af152d | ||
|
|
d8120d4e13 | ||
|
|
0ac211ce77 | ||
|
|
32c0d6f253 | ||
|
|
de762847e9 | ||
|
|
6248e32148 | ||
|
|
52f88ee32d | ||
|
|
f8d1cf2f60 | ||
|
|
e129425d8d | ||
|
|
ae20d82e1d | ||
|
|
463b2ce040 | ||
|
|
412b47abba | ||
|
|
ed216eee73 | ||
|
|
9dd5234962 | ||
|
|
018532016b | ||
|
|
7dfecc38f6 | ||
|
|
aa5afadcce | ||
|
|
d23a584b9e | ||
|
|
572f035877 | ||
|
|
b94eb6adb0 | ||
|
|
45fccb94e1 | ||
|
|
2628f17fae | ||
|
|
69fb6e77fc | ||
|
|
6992858e67 | ||
|
|
59917cca45 | ||
|
|
2f0b959aa4 | ||
|
|
950a12360e | ||
|
|
48831225ac | ||
|
|
4e8a79d8f1 | ||
|
|
82b5e97a2b | ||
|
|
7f168d49a6 | ||
|
|
ad571e6019 | ||
|
|
14d924f6c7 | ||
|
|
282f02f4d5 | ||
|
|
d36cede0c7 | ||
|
|
a262d70b88 | ||
|
|
77b098c5fe | ||
|
|
b491b9d77d | ||
|
|
237f792fb4 |
No files matched your search
+1
-2
@@ -1,2 +1 @@
|
||||
src/core/vendor/**
|
||||
src/web/static/clippy_assets/**
|
||||
src/core/vendor/**
|
||||
@@ -7,19 +7,18 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Prefix the title above with 'Bug report:' -->
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior or a link to the recipe / input used to cause the bug:
|
||||
Steps to reproduce the behaviour or a link to the recipe / input used to cause the bug:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
**Expected behaviour**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
@@ -27,9 +26,8 @@ If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (if relevant, please complete the following information):**
|
||||
- OS: [e.g. Windows]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
- Browser: [e.g. chrome 72, firefox 60]
|
||||
- CyberChef version: [e.g. 9.7.14]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
+2
-2
@@ -6,9 +6,9 @@ labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
<!-- Prefix the title above with 'Feature request:' -->
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
A clear and concise description of what the problem is. E.g. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
@@ -7,8 +7,6 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Prefix the title above with 'Operation request:' -->
|
||||
|
||||
## Summary
|
||||
|
||||
### Example Input
|
||||
|
||||
@@ -4,7 +4,6 @@ travis.log
|
||||
build
|
||||
.vscode
|
||||
.*.swp
|
||||
.DS_Store
|
||||
src/core/config/modules/*
|
||||
src/core/config/OperationConfig.json
|
||||
src/core/operations/index.mjs
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master).
|
||||
|
||||
|
||||
### [9.7.0] - 2019-09-13
|
||||
- 'Optical Character Recognition' operation added [@MShwed] [@n1474335] | [#632]
|
||||
|
||||
### [9.6.0] - 2019-09-04
|
||||
- 'Bacon Cipher Encode' and 'Bacon Cipher Decode' operations added [@kassi] | [#500]
|
||||
|
||||
### [9.5.0] - 2019-09-04
|
||||
- Various Steganography operations added: 'Extract LSB', 'Extract RGBA', 'Randomize Colour Palette', and 'View Bit Plane' [@Ge0rg3] | [#625]
|
||||
|
||||
### [9.4.0] - 2019-08-30
|
||||
- 'Render Markdown' operation added [@j433866] | [#627]
|
||||
|
||||
### [9.3.0] - 2019-08-30
|
||||
- 'Show on map' operation added [@j433866] | [#477]
|
||||
|
||||
### [9.2.0] - 2019-08-23
|
||||
- 'Parse UDP' operation added [@h345983745] | [#614]
|
||||
|
||||
@@ -170,6 +185,11 @@ All major and minor version changes will be documented in this file. Details of
|
||||
|
||||
|
||||
|
||||
[9.7.0]: https://github.com/gchq/CyberChef/releases/tag/v9.7.0
|
||||
[9.6.0]: https://github.com/gchq/CyberChef/releases/tag/v9.6.0
|
||||
[9.5.0]: https://github.com/gchq/CyberChef/releases/tag/v9.5.0
|
||||
[9.4.0]: https://github.com/gchq/CyberChef/releases/tag/v9.4.0
|
||||
[9.3.0]: https://github.com/gchq/CyberChef/releases/tag/v9.3.0
|
||||
[9.2.0]: https://github.com/gchq/CyberChef/releases/tag/v9.2.0
|
||||
[9.1.0]: https://github.com/gchq/CyberChef/releases/tag/v9.1.0
|
||||
[9.0.0]: https://github.com/gchq/CyberChef/releases/tag/v9.0.0
|
||||
@@ -241,6 +261,7 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[@masq]: https://github.com/masq
|
||||
[@Ge0rg3]: https://github.com/Ge0rg3
|
||||
[@MShwed]: https://github.com/MShwed
|
||||
[@kassi]: https://github.com/kassi
|
||||
|
||||
[#95]: https://github.com/gchq/CyberChef/pull/299
|
||||
[#173]: https://github.com/gchq/CyberChef/pull/173
|
||||
@@ -279,8 +300,10 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[#467]: https://github.com/gchq/CyberChef/pull/467
|
||||
[#468]: https://github.com/gchq/CyberChef/pull/468
|
||||
[#476]: https://github.com/gchq/CyberChef/pull/476
|
||||
[#477]: https://github.com/gchq/CyberChef/pull/477
|
||||
[#489]: https://github.com/gchq/CyberChef/pull/489
|
||||
[#496]: https://github.com/gchq/CyberChef/pull/496
|
||||
[#500]: https://github.com/gchq/CyberChef/pull/500
|
||||
[#506]: https://github.com/gchq/CyberChef/pull/506
|
||||
[#515]: https://github.com/gchq/CyberChef/pull/515
|
||||
[#516]: https://github.com/gchq/CyberChef/pull/516
|
||||
@@ -296,3 +319,6 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[#591]: https://github.com/gchq/CyberChef/pull/591
|
||||
[#595]: https://github.com/gchq/CyberChef/pull/595
|
||||
[#614]: https://github.com/gchq/CyberChef/pull/614
|
||||
[#625]: https://github.com/gchq/CyberChef/pull/625
|
||||
[#627]: https://github.com/gchq/CyberChef/pull/627
|
||||
[#632]: https://github.com/gchq/CyberChef/pull/632
|
||||
+40
-27
@@ -14,7 +14,6 @@ const path = require("path");
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
module.exports = function (grunt) {
|
||||
grunt.file.defaultEncoding = "utf8";
|
||||
grunt.file.preserveBOM = false;
|
||||
@@ -102,6 +101,26 @@ module.exports = function (grunt) {
|
||||
return entryModules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the correct delimiter to use to chain shell commands together
|
||||
* based on the current OS.
|
||||
*
|
||||
* @param {string[]} cmds
|
||||
* @returns {string}
|
||||
*/
|
||||
function chainCommands(cmds) {
|
||||
const win = process.platform === "win32";
|
||||
if (!win) {
|
||||
return cmds.join(";");
|
||||
}
|
||||
return cmds
|
||||
// && means that subsequent commands will not be executed if the
|
||||
// previous one fails. & would coninue on a fail
|
||||
.join("&&")
|
||||
// Windows does not support \n properly
|
||||
.replace("\n", "\\n");
|
||||
}
|
||||
|
||||
grunt.initConfig({
|
||||
clean: {
|
||||
dev: ["build/dev/*"],
|
||||
@@ -140,8 +159,7 @@ module.exports = function (grunt) {
|
||||
mode: "production",
|
||||
target: "web",
|
||||
entry: Object.assign({
|
||||
main: "./src/web/index.js",
|
||||
sitemap: "./src/web/static/sitemap.js"
|
||||
main: "./src/web/index.js"
|
||||
}, moduleEntryPoints),
|
||||
output: {
|
||||
path: __dirname + "/build/prod",
|
||||
@@ -232,7 +250,6 @@ module.exports = function (grunt) {
|
||||
"build/prod/**/*",
|
||||
"!build/prod/index.html",
|
||||
"!build/prod/BundleAnalyzerReport.html",
|
||||
"!build/prod/sitemap.js"
|
||||
],
|
||||
dest: `build/prod/CyberChef_v${pkg.version}.zip`
|
||||
}
|
||||
@@ -270,11 +287,7 @@ module.exports = function (grunt) {
|
||||
{
|
||||
src: "build/prod/index.html",
|
||||
dest: "build/prod/index.html"
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
dest: "build/prod/"
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
standalone: {
|
||||
@@ -322,33 +335,33 @@ module.exports = function (grunt) {
|
||||
},
|
||||
exec: {
|
||||
repoSize: {
|
||||
command: [
|
||||
command: chainCommands([
|
||||
"git ls-files | wc -l | xargs printf '\n%b\ttracked files\n'",
|
||||
"du -hs | egrep -o '^[^\t]*' | xargs printf '%b\trepository size\n'"
|
||||
].join(";"),
|
||||
]),
|
||||
stderr: false
|
||||
},
|
||||
cleanGit: {
|
||||
command: "git gc --prune=now --aggressive"
|
||||
},
|
||||
sitemap: {
|
||||
command: "node build/prod/sitemap.js > build/prod/sitemap.xml"
|
||||
command: "node --experimental-modules --no-warnings --no-deprecation src/web/static/sitemap.mjs > build/prod/sitemap.xml"
|
||||
},
|
||||
generateConfig: {
|
||||
command: [
|
||||
command: chainCommands([
|
||||
"echo '\n--- Regenerating config files. ---'",
|
||||
"echo [] > src/core/config/OperationConfig.json",
|
||||
"node --experimental-modules --no-warnings --no-deprecation src/core/config/scripts/generateOpsIndex.mjs",
|
||||
"node --experimental-modules --no-warnings --no-deprecation src/core/config/scripts/generateConfig.mjs",
|
||||
"echo '--- Config scripts finished. ---\n'"
|
||||
].join(";")
|
||||
])
|
||||
},
|
||||
generateNodeIndex: {
|
||||
command: [
|
||||
command: chainCommands([
|
||||
"echo '\n--- Regenerating node index ---'",
|
||||
"node --experimental-modules --no-warnings --no-deprecation src/node/config/scripts/generateNodeIndex.mjs",
|
||||
"echo '--- Node index generated. ---\n'"
|
||||
].join(";"),
|
||||
]),
|
||||
},
|
||||
opTests: {
|
||||
command: "node --experimental-modules --no-warnings --no-deprecation tests/operations/index.mjs"
|
||||
@@ -360,40 +373,40 @@ module.exports = function (grunt) {
|
||||
command: "node --experimental-modules --no-warnings --no-deprecation tests/node/index.mjs"
|
||||
},
|
||||
setupNodeConsumers: {
|
||||
command: [
|
||||
"echo '\n--- Testing node conumers ---'",
|
||||
command: chainCommands([
|
||||
"echo '\n--- Testing node consumers ---'",
|
||||
"npm link",
|
||||
`mkdir ${nodeConsumerTestPath}`,
|
||||
`cp tests/node/consumers/* ${nodeConsumerTestPath}`,
|
||||
`cd ${nodeConsumerTestPath}`,
|
||||
"npm link cyberchef"
|
||||
].join(";"),
|
||||
]),
|
||||
},
|
||||
teardownNodeConsumers: {
|
||||
command: [
|
||||
command: chainCommands([
|
||||
`rm -rf ${nodeConsumerTestPath}`,
|
||||
"echo '\n--- Node consumer tests complete ---'"
|
||||
].join(";"),
|
||||
]),
|
||||
},
|
||||
testCJSNodeConsumer: {
|
||||
command: [
|
||||
command: chainCommands([
|
||||
`cd ${nodeConsumerTestPath}`,
|
||||
"node --no-warnings cjs-consumer.js",
|
||||
].join(";"),
|
||||
]),
|
||||
stdout: false,
|
||||
},
|
||||
testESMNodeConsumer: {
|
||||
command: [
|
||||
command: chainCommands([
|
||||
`cd ${nodeConsumerTestPath}`,
|
||||
"node --no-warnings --experimental-modules esm-consumer.mjs",
|
||||
].join(";"),
|
||||
]),
|
||||
stdout: false,
|
||||
},
|
||||
testESMDeepImportNodeConsumer: {
|
||||
command: [
|
||||
command: chainCommands([
|
||||
`cd ${nodeConsumerTestPath}`,
|
||||
"node --no-warnings --experimental-modules esm-deep-import-consumer.mjs",
|
||||
].join(";"),
|
||||
]),
|
||||
stdout: false,
|
||||
},
|
||||
},
|
||||
|
||||
Generated
+1182
-1467
File diff suppressed because it is too large.
Load diff
+35
-32
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "9.2.2",
|
||||
"version": "9.7.14",
|
||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||
"author": "n1474335 <n1474335@gmail.com>",
|
||||
"homepage": "https://gchq.github.io/CyberChef",
|
||||
@@ -36,19 +36,20 @@
|
||||
"node >= 10"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.5.0",
|
||||
"@babel/plugin-transform-runtime": "^7.5.0",
|
||||
"@babel/preset-env": "^7.5.0",
|
||||
"autoprefixer": "^9.6.0",
|
||||
"babel-eslint": "^10.0.2",
|
||||
"@babel/core": "^7.5.5",
|
||||
"@babel/plugin-transform-runtime": "^7.5.5",
|
||||
"@babel/preset-env": "^7.5.5",
|
||||
"autoprefixer": "^9.6.1",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-plugin-dynamic-import-node": "^2.2.0",
|
||||
"chromedriver": "^75.0.1",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.0",
|
||||
"chromedriver": "^76.0.1",
|
||||
"colors": "^1.3.3",
|
||||
"css-loader": "^3.0.0",
|
||||
"eslint": "^6.0.1",
|
||||
"copy-webpack-plugin": "^5.0.4",
|
||||
"css-loader": "^3.2.0",
|
||||
"eslint": "^6.2.2",
|
||||
"exports-loader": "^0.7.0",
|
||||
"file-loader": "^4.0.0",
|
||||
"file-loader": "^4.2.0",
|
||||
"grunt": "^1.0.4",
|
||||
"grunt-accessibility": "~6.0.0",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
@@ -63,27 +64,27 @@
|
||||
"grunt-zip": "^0.18.2",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"imports-loader": "^0.8.0",
|
||||
"mini-css-extract-plugin": "^0.7.0",
|
||||
"nightwatch": "^1.1.13",
|
||||
"mini-css-extract-plugin": "^0.8.0",
|
||||
"nightwatch": "^1.2.1",
|
||||
"node-sass": "^4.12.0",
|
||||
"postcss-css-variables": "^0.13.0",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"prompt": "^1.0.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"sitemap": "^3.2.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"svg-url-loader": "^3.0.0",
|
||||
"url-loader": "^2.0.1",
|
||||
"webpack": "^4.35.2",
|
||||
"webpack-bundle-analyzer": "^3.3.2",
|
||||
"webpack-dev-server": "^3.7.2",
|
||||
"sass-loader": "^8.0.0",
|
||||
"sitemap": "^4.1.1",
|
||||
"style-loader": "^1.0.0",
|
||||
"svg-url-loader": "^3.0.1",
|
||||
"url-loader": "^2.1.0",
|
||||
"webpack": "^4.39.3",
|
||||
"webpack-bundle-analyzer": "^3.4.1",
|
||||
"webpack-dev-server": "^3.8.0",
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"worker-loader": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.4.4",
|
||||
"@babel/runtime": "^7.5.0",
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"arrive": "^2.4.1",
|
||||
"babel-plugin-transform-builtin-extend": "1.1.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
@@ -94,23 +95,22 @@
|
||||
"bootstrap-material-design": "^4.1.2",
|
||||
"bson": "^4.0.2",
|
||||
"chi-squared": "^1.1.0",
|
||||
"clippyjs": "0.0.3",
|
||||
"core-js": "^3.1.4",
|
||||
"crypto-api": "^0.8.3",
|
||||
"core-js": "^3.2.1",
|
||||
"crypto-api": "^0.8.5",
|
||||
"crypto-js": "^3.1.9-1",
|
||||
"ctph.js": "0.0.5",
|
||||
"d3": "^5.9.7",
|
||||
"d3": "^5.11.0",
|
||||
"d3-hexbin": "^0.2.2",
|
||||
"diff": "^4.0.1",
|
||||
"es6-promisify": "^6.0.1",
|
||||
"escodegen": "^1.11.1",
|
||||
"es6-promisify": "^6.0.2",
|
||||
"escodegen": "^1.12.0",
|
||||
"esm": "^3.2.25",
|
||||
"esmangle": "^1.0.1",
|
||||
"esprima": "^4.0.1",
|
||||
"exif-parser": "^0.1.12",
|
||||
"file-saver": "^2.0.2",
|
||||
"geodesy": "^1.1.3",
|
||||
"highlight.js": "^9.15.8",
|
||||
"highlight.js": "^9.15.10",
|
||||
"jimp": "^0.6.4",
|
||||
"jquery": "3.4.1",
|
||||
"js-crc": "^0.2.0",
|
||||
@@ -120,16 +120,17 @@
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jsqr": "^1.2.0",
|
||||
"jsrsasign": "8.0.12",
|
||||
"kbpgp": "2.1.2",
|
||||
"kbpgp": "2.1.3",
|
||||
"libbzip2-wasm": "0.0.4",
|
||||
"libyara-wasm": "0.0.12",
|
||||
"lodash": "^4.17.15",
|
||||
"loglevel": "^1.6.3",
|
||||
"loglevel-message-prefix": "^3.0.0",
|
||||
"markdown-it": "^9.1.0",
|
||||
"moment": "^2.24.0",
|
||||
"moment-timezone": "^0.5.25",
|
||||
"moment-timezone": "^0.5.26",
|
||||
"ngeohash": "^0.6.3",
|
||||
"node-forge": "^0.8.5",
|
||||
"node-forge": "^0.9.1",
|
||||
"node-md6": "^0.1.0",
|
||||
"nodom": "^2.2.0",
|
||||
"notepack.io": "^2.2.0",
|
||||
@@ -142,6 +143,7 @@
|
||||
"sortablejs": "^1.9.0",
|
||||
"split.js": "^1.5.11",
|
||||
"ssdeep.js": "0.0.2",
|
||||
"tesseract.js": "^2.0.0-alpha.15",
|
||||
"ua-parser-js": "^0.7.20",
|
||||
"utf8": "^3.0.0",
|
||||
"vkbeautify": "^0.99.3",
|
||||
@@ -157,6 +159,7 @@
|
||||
"test": "grunt test",
|
||||
"test-node-consumer": "grunt testnodeconsumer",
|
||||
"testui": "grunt testui",
|
||||
"testuidev": "npx nightwatch --env=dev",
|
||||
"lint": "grunt lint",
|
||||
"newop": "node --experimental-modules src/core/config/scripts/newOperation.mjs"
|
||||
}
|
||||
|
||||
+3
-3
@@ -129,7 +129,7 @@ class Dish {
|
||||
*
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
* @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
|
||||
* @returns {* | Promise} - (Broswer) A promise | (Node) value of dish in given type
|
||||
* @returns {* | Promise} - (Browser) A promise | (Node) value of dish in given type
|
||||
*/
|
||||
get(type, notUTF8=false) {
|
||||
if (typeof type === "string") {
|
||||
@@ -177,7 +177,7 @@ class Dish {
|
||||
this.type = type;
|
||||
|
||||
if (!this.valid()) {
|
||||
const sample = Utils.truncate(JSON.stringify(this.value), 13);
|
||||
const sample = Utils.truncate(JSON.stringify(this.value), 25);
|
||||
throw new DishError(`Data is not a valid ${Dish.enumLookup(type)}: ${sample}`);
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@ class Dish {
|
||||
*
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
* @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
|
||||
* @returns {Dish | Promise} - (Broswer) A promise | (Node) value of dish in given type
|
||||
* @returns {Dish | Promise} - (Browser) A promise | (Node) value of dish in given type
|
||||
*/
|
||||
presentAs(type, notUTF8=false) {
|
||||
const clone = this.clone();
|
||||
|
||||
+31
-8
@@ -201,9 +201,8 @@ class Utils {
|
||||
* Utils.parseEscapedChars("\\n");
|
||||
*/
|
||||
static parseEscapedChars(str) {
|
||||
return str.replace(/(\\)?\\([bfnrtv'"]|[0-3][0-7]{2}|[0-7]{1,2}|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]) {
|
||||
return str.replace(/\\([bfnrtv'"]|[0-3][0-7]{2}|[0-7]{1,2}|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\}|\\)/g, function(m, a) {
|
||||
switch (a[0]) {
|
||||
case "\\":
|
||||
return "\\";
|
||||
case "0":
|
||||
@@ -214,7 +213,7 @@ class Utils {
|
||||
case "5":
|
||||
case "6":
|
||||
case "7":
|
||||
return String.fromCharCode(parseInt(b, 8));
|
||||
return String.fromCharCode(parseInt(a, 8));
|
||||
case "b":
|
||||
return "\b";
|
||||
case "t":
|
||||
@@ -232,12 +231,12 @@ class Utils {
|
||||
case "'":
|
||||
return "'";
|
||||
case "x":
|
||||
return String.fromCharCode(parseInt(b.substr(1), 16));
|
||||
return String.fromCharCode(parseInt(a.substr(1), 16));
|
||||
case "u":
|
||||
if (b[1] === "{")
|
||||
return String.fromCodePoint(parseInt(b.slice(2, -1), 16));
|
||||
if (a[1] === "{")
|
||||
return String.fromCodePoint(parseInt(a.slice(2, -1), 16));
|
||||
else
|
||||
return String.fromCharCode(parseInt(b.substr(1), 16));
|
||||
return String.fromCharCode(parseInt(a.substr(1), 16));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1303,6 +1302,30 @@ export function sendStatusMessage(msg) {
|
||||
console.debug(msg);
|
||||
}
|
||||
|
||||
const debounceTimeouts = {};
|
||||
|
||||
/**
|
||||
* Debouncer to stop functions from being executed multiple times in a
|
||||
* short space of time
|
||||
* https://davidwalsh.name/javascript-debounce-function
|
||||
*
|
||||
* @param {function} func - The function to be executed after the debounce time
|
||||
* @param {number} wait - The time (ms) to wait before executing the function
|
||||
* @param {string} id - Unique ID to reference the timeout for the function
|
||||
* @param {object} scope - The object to bind to the debounced function
|
||||
* @param {array} args - Array of arguments to be passed to func
|
||||
* @returns {function}
|
||||
*/
|
||||
export function debounce(func, wait, id, scope, args) {
|
||||
return function() {
|
||||
const later = function() {
|
||||
func.apply(scope, args);
|
||||
};
|
||||
clearTimeout(debounceTimeouts[id]);
|
||||
debounceTimeouts[id] = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Polyfills
|
||||
|
||||
@@ -85,6 +85,8 @@
|
||||
"Vigenère Decode",
|
||||
"To Morse Code",
|
||||
"From Morse Code",
|
||||
"Bacon Cipher Encode",
|
||||
"Bacon Cipher Decode",
|
||||
"Bifid Cipher Encode",
|
||||
"Bifid Cipher Decode",
|
||||
"Affine Cipher Encode",
|
||||
@@ -121,6 +123,7 @@
|
||||
"Generate PGP Key Pair",
|
||||
"PGP Encrypt",
|
||||
"PGP Decrypt",
|
||||
"PGP Verify",
|
||||
"PGP Encrypt and Sign",
|
||||
"PGP Decrypt and Verify",
|
||||
"Parse SSH Host Key"
|
||||
@@ -228,6 +231,7 @@
|
||||
"Convert speed",
|
||||
"Convert data units",
|
||||
"Convert co-ordinate format",
|
||||
"Show on map",
|
||||
"Parse UNIX file permissions",
|
||||
"Swap endianness",
|
||||
"Parse colour code",
|
||||
@@ -281,6 +285,7 @@
|
||||
"Zip",
|
||||
"Unzip",
|
||||
"Bzip2 Decompress",
|
||||
"Bzip2 Compress",
|
||||
"Tar",
|
||||
"Untar"
|
||||
]
|
||||
@@ -357,7 +362,8 @@
|
||||
"BSON serialise",
|
||||
"BSON deserialise",
|
||||
"To MessagePack",
|
||||
"From MessagePack"
|
||||
"From MessagePack",
|
||||
"Render Markdown"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -366,8 +372,13 @@
|
||||
"Detect File Type",
|
||||
"Scan for Embedded Files",
|
||||
"Extract Files",
|
||||
"YARA Rules",
|
||||
"Remove EXIF",
|
||||
"Extract EXIF"
|
||||
"Extract EXIF",
|
||||
"Extract RGBA",
|
||||
"View Bit Plane",
|
||||
"Randomize Colour Palette",
|
||||
"Extract LSB"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -375,6 +386,7 @@
|
||||
"ops": [
|
||||
"Render Image",
|
||||
"Play Media",
|
||||
"Optical Character Recognition",
|
||||
"Remove EXIF",
|
||||
"Extract EXIF",
|
||||
"Split Colour Channels",
|
||||
@@ -392,6 +404,7 @@
|
||||
"Cover Image",
|
||||
"Image Hue/Saturation/Lightness",
|
||||
"Sharpen Image",
|
||||
"Normalise Image",
|
||||
"Convert Image Format",
|
||||
"Add Text To Image",
|
||||
"Hex Density chart",
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import OperationError from "./OperationError.mjs";
|
||||
import DishError from "./DishError.mjs";
|
||||
import ExcludedOperationError from "./ExcludedOperationError";
|
||||
|
||||
export {
|
||||
OperationError,
|
||||
DishError,
|
||||
ExcludedOperationError,
|
||||
};
|
||||
@@ -22,7 +22,7 @@ export const ENCODING_SCHEME = [
|
||||
/**
|
||||
* Lookup table for the binary value of each digit representation.
|
||||
*
|
||||
* I wrote a very nice algorithm to generate 8 4 2 1 encoding programatically,
|
||||
* I wrote a very nice algorithm to generate 8 4 2 1 encoding programmatically,
|
||||
* but unfortunately it's much easier (if less elegant) to use lookup tables
|
||||
* when supporting multiple encoding schemes.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Bacon Cipher resources.
|
||||
*
|
||||
* @author Karsten Silkenbäumer [github.com/kassi]
|
||||
* @copyright Karsten Silkenbäumer 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Bacon definitions.
|
||||
*/
|
||||
export const BACON_ALPHABETS = {
|
||||
"Standard (I=J and U=V)": {
|
||||
alphabet: "ABCDEFGHIKLMNOPQRSTUWXYZ",
|
||||
codes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23]
|
||||
},
|
||||
"Complete": {
|
||||
alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
}
|
||||
};
|
||||
export const BACON_TRANSLATION_01 = "0/1";
|
||||
export const BACON_TRANSLATION_AB = "A/B";
|
||||
export const BACON_TRANSLATION_CASE = "Case";
|
||||
export const BACON_TRANSLATION_AMNZ = "A-M/N-Z first letter";
|
||||
export const BACON_TRANSLATIONS = [
|
||||
BACON_TRANSLATION_01,
|
||||
BACON_TRANSLATION_AB,
|
||||
BACON_TRANSLATION_CASE,
|
||||
BACON_TRANSLATION_AMNZ,
|
||||
];
|
||||
export const BACON_TRANSLATIONS_FOR_ENCODING = [
|
||||
BACON_TRANSLATION_01,
|
||||
BACON_TRANSLATION_AB
|
||||
];
|
||||
export const BACON_CLEARER_MAP = {
|
||||
[BACON_TRANSLATION_01]: /[^01]/g,
|
||||
[BACON_TRANSLATION_AB]: /[^ABab]/g,
|
||||
[BACON_TRANSLATION_CASE]: /[^A-Za-z]/g,
|
||||
};
|
||||
export const BACON_NORMALIZE_MAP = {
|
||||
[BACON_TRANSLATION_AB]: {
|
||||
"A": "0",
|
||||
"B": "1",
|
||||
"a": "0",
|
||||
"b": "1"
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Swaps zeros to ones and ones to zeros.
|
||||
*
|
||||
* @param {string} data
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
* // returns "11001 01010"
|
||||
* swapZeroAndOne("00110 10101");
|
||||
*/
|
||||
export function swapZeroAndOne(string) {
|
||||
return string.replace(/[01]/g, function (c) {
|
||||
return {
|
||||
"0": "1",
|
||||
"1": "0"
|
||||
}[c];
|
||||
});
|
||||
}
|
||||
+11
-2
@@ -7,12 +7,12 @@
|
||||
*/
|
||||
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Base64's the input byte array using the given alphabet, returning a string.
|
||||
*
|
||||
* @param {byteArray|Uint8Array|string} data
|
||||
* @param {byteArray|Uint8Array|ArrayBuffer|string} data
|
||||
* @param {string} [alphabet="A-Za-z0-9+/="]
|
||||
* @returns {string}
|
||||
*
|
||||
@@ -25,11 +25,17 @@ import Utils from "../Utils.mjs";
|
||||
*/
|
||||
export function toBase64(data, alphabet="A-Za-z0-9+/=") {
|
||||
if (!data) return "";
|
||||
if (data instanceof ArrayBuffer) {
|
||||
data = new Uint8Array(data);
|
||||
}
|
||||
if (typeof data == "string") {
|
||||
data = Utils.strToByteArray(data);
|
||||
}
|
||||
|
||||
alphabet = Utils.expandAlphRange(alphabet).join("");
|
||||
if (alphabet.length !== 64 && alphabet.length !== 65) { // Allow for padding
|
||||
throw new OperationError(`Invalid Base64 alphabet length (${alphabet.length}): ${alphabet}`);
|
||||
}
|
||||
|
||||
let output = "",
|
||||
chr1, chr2, chr3,
|
||||
@@ -83,6 +89,9 @@ export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", r
|
||||
|
||||
alphabet = alphabet || "A-Za-z0-9+/=";
|
||||
alphabet = Utils.expandAlphRange(alphabet).join("");
|
||||
if (alphabet.length !== 64 && alphabet.length !== 65) { // Allow for padding
|
||||
throw new OperationError(`Invalid Base64 alphabet length (${alphabet.length}): ${alphabet}`);
|
||||
}
|
||||
|
||||
const output = [];
|
||||
let chr1, chr2, chr3,
|
||||
|
||||
@@ -6,9 +6,22 @@
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import geohash from "ngeohash";
|
||||
import geodesy from "geodesy";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import geohash from "ngeohash";
|
||||
/*
|
||||
Currently unable to update to geodesy v2 as we cannot load .js modules into a .mjs file.
|
||||
When we do update, imports will look like this:
|
||||
|
||||
import LatLonEllipsoidal from "geodesy/latlon-ellipsoidal.js";
|
||||
import Mgrs from "geodesy/mgrs.js";
|
||||
import OsGridRef from "geodesy/osgridref.js";
|
||||
import Utm from "geodesy/utm.js";
|
||||
*/
|
||||
import geodesy from "geodesy";
|
||||
const LatLonEllipsoidal = geodesy.LatLonEllipsoidal,
|
||||
Mgrs = geodesy.Mgrs,
|
||||
OsGridRef = geodesy.OsGridRef,
|
||||
Utm = geodesy.Utm;
|
||||
|
||||
/**
|
||||
* Co-ordinate formats
|
||||
@@ -116,22 +129,22 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
|
||||
switch (inFormat) {
|
||||
case "Geohash":
|
||||
hash = geohash.decode(input.replace(/[^A-Za-z0-9]/g, ""));
|
||||
latlon = new geodesy.LatLonEllipsoidal(hash.latitude, hash.longitude);
|
||||
latlon = new LatLonEllipsoidal(hash.latitude, hash.longitude);
|
||||
break;
|
||||
case "Military Grid Reference System":
|
||||
utm = geodesy.Mgrs.parse(input.replace(/[^A-Za-z0-9]/g, "")).toUtm();
|
||||
utm = Mgrs.parse(input.replace(/[^A-Za-z0-9]/g, "")).toUtm();
|
||||
latlon = utm.toLatLonE();
|
||||
break;
|
||||
case "Ordnance Survey National Grid":
|
||||
osng = geodesy.OsGridRef.parse(input.replace(/[^A-Za-z0-9]/g, ""));
|
||||
latlon = geodesy.OsGridRef.osGridToLatLon(osng);
|
||||
osng = OsGridRef.parse(input.replace(/[^A-Za-z0-9]/g, ""));
|
||||
latlon = OsGridRef.osGridToLatLon(osng);
|
||||
break;
|
||||
case "Universal Transverse Mercator":
|
||||
// Geodesy needs a space between the first 2 digits and the next letter
|
||||
if (/^[\d]{2}[A-Za-z]/.test(input)) {
|
||||
input = input.slice(0, 2) + " " + input.slice(2);
|
||||
}
|
||||
utm = geodesy.Utm.parse(input);
|
||||
utm = Utm.parse(input);
|
||||
latlon = utm.toLatLonE();
|
||||
break;
|
||||
case "Degrees Minutes Seconds":
|
||||
@@ -143,7 +156,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
|
||||
if (splitLat.length >= 3 && splitLong.length >= 3) {
|
||||
lat = convDMSToDD(splitLat[0], splitLat[1], splitLat[2], 10);
|
||||
lon = convDMSToDD(splitLong[0], splitLong[1], splitLong[2], 10);
|
||||
latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lon.degrees);
|
||||
latlon = new LatLonEllipsoidal(lat.degrees, lon.degrees);
|
||||
} else {
|
||||
throw new OperationError("Invalid co-ordinate format for Degrees Minutes Seconds");
|
||||
}
|
||||
@@ -152,7 +165,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
|
||||
splitLat = splitInput(split[0]);
|
||||
if (splitLat.length >= 3) {
|
||||
lat = convDMSToDD(splitLat[0], splitLat[1], splitLat[2]);
|
||||
latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lat.degrees);
|
||||
latlon = new LatLonEllipsoidal(lat.degrees, lat.degrees);
|
||||
} else {
|
||||
throw new OperationError("Invalid co-ordinate format for Degrees Minutes Seconds");
|
||||
}
|
||||
@@ -168,7 +181,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
|
||||
// Convert to decimal degrees, and then convert to a geodesy object
|
||||
lat = convDDMToDD(splitLat[0], splitLat[1], 10);
|
||||
lon = convDDMToDD(splitLong[0], splitLong[1], 10);
|
||||
latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lon.degrees);
|
||||
latlon = new LatLonEllipsoidal(lat.degrees, lon.degrees);
|
||||
} else {
|
||||
// Not a pair, so only try to convert one set of co-ordinates
|
||||
splitLat = splitInput(input);
|
||||
@@ -176,7 +189,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
|
||||
throw new OperationError("Invalid co-ordinate format for Degrees Decimal Minutes.");
|
||||
}
|
||||
lat = convDDMToDD(splitLat[0], splitLat[1], 10);
|
||||
latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lat.degrees);
|
||||
latlon = new LatLonEllipsoidal(lat.degrees, lat.degrees);
|
||||
}
|
||||
break;
|
||||
case "Decimal Degrees":
|
||||
@@ -186,14 +199,14 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
|
||||
if (splitLat.length !== 1 || splitLong.length !== 1) {
|
||||
throw new OperationError("Invalid co-ordinate format for Decimal Degrees.");
|
||||
}
|
||||
latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLong[0]);
|
||||
latlon = new LatLonEllipsoidal(splitLat[0], splitLong[0]);
|
||||
} else {
|
||||
// Not a pair, so only try to convert one set of co-ordinates
|
||||
splitLat = splitInput(split[0]);
|
||||
if (splitLat.length !== 1) {
|
||||
throw new OperationError("Invalid co-ordinate format for Decimal Degrees.");
|
||||
}
|
||||
latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLat[0]);
|
||||
latlon = new LatLonEllipsoidal(splitLat[0], splitLat[0]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -260,7 +273,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
|
||||
convLat = mgrs.toString(precision);
|
||||
break;
|
||||
case "Ordnance Survey National Grid":
|
||||
osng = geodesy.OsGridRef.latLonToOsGrid(latlon);
|
||||
osng = OsGridRef.latLonToOsGrid(latlon);
|
||||
if (osng.toString() === "") {
|
||||
throw new OperationError("Could not convert co-ordinates to OS National Grid. Are the co-ordinates in range?");
|
||||
}
|
||||
|
||||
+10
-1
@@ -32,7 +32,7 @@ export const WORD_DELIM_OPTIONS = ["Line feed", "CRLF", "Forward slash", "Backsl
|
||||
export const INPUT_DELIM_OPTIONS = ["Line feed", "CRLF", "Space", "Comma", "Semi-colon", "Colon", "Nothing (separate chars)"];
|
||||
|
||||
/**
|
||||
* Armithmetic sequence delimiters
|
||||
* Arithmetic sequence delimiters
|
||||
*/
|
||||
export const ARITHMETIC_DELIM_OPTIONS = ["Line feed", "Space", "Comma", "Semi-colon", "Colon", "CRLF"];
|
||||
|
||||
@@ -72,3 +72,12 @@ export const JOIN_DELIM_OPTIONS = [
|
||||
{name: "Nothing (join chars)", value: ""}
|
||||
];
|
||||
|
||||
/**
|
||||
* RGBA list delimiters.
|
||||
*/
|
||||
export const RGBA_DELIM_OPTIONS = [
|
||||
{name: "Comma", value: ","},
|
||||
{name: "Space", value: " "},
|
||||
{name: "CRLF", value: "\\r\\n"},
|
||||
{name: "Line Feed", value: "\n"}
|
||||
];
|
||||
+995
-19
File diff suppressed because it is too large.
Load diff
@@ -75,7 +75,7 @@ function bytesMatch(sig, buf, offset=0) {
|
||||
* Given a buffer, detects magic byte sequences at specific positions and returns the
|
||||
* extension and mime type.
|
||||
*
|
||||
* @param {Uint8Array} buf
|
||||
* @param {Uint8Array|ArrayBuffer} buf
|
||||
* @param {string[]} [categories=All] - Which categories of file to look for
|
||||
* @returns {Object[]} types
|
||||
* @returns {string} type.name - Name of file type
|
||||
@@ -84,6 +84,10 @@ function bytesMatch(sig, buf, offset=0) {
|
||||
* @returns {string} [type.desc] - Description
|
||||
*/
|
||||
export function detectFileType(buf, categories=Object.keys(FILE_SIGNATURES)) {
|
||||
if (buf instanceof ArrayBuffer) {
|
||||
buf = new Uint8Array(buf);
|
||||
}
|
||||
|
||||
if (!(buf && buf.length > 1)) {
|
||||
return [];
|
||||
}
|
||||
@@ -174,7 +178,7 @@ export function scanForFileTypes(buf, categories=Object.keys(FILE_SIGNATURES)) {
|
||||
* @param {Uint8Array} buf - The buffer to search
|
||||
* @param {Object} sig - A single signature object (Not an array of signatures)
|
||||
* @param {number} offset - Where to start search from
|
||||
* @returs {number} The position of the match or -1 if one cannot be found.
|
||||
* @returns {number} The position of the match or -1 if one cannot be found.
|
||||
*/
|
||||
function locatePotentialSig(buf, sig, offset) {
|
||||
// Find values for first key and value in sig
|
||||
@@ -203,7 +207,7 @@ function locatePotentialSig(buf, sig, offset) {
|
||||
* Detects whether the given buffer is a file of the type specified.
|
||||
*
|
||||
* @param {string|RegExp} type
|
||||
* @param {Uint8Array} buf
|
||||
* @param {Uint8Array|ArrayBuffer} buf
|
||||
* @returns {string|false} The mime type or false if the type does not match
|
||||
*/
|
||||
export function isType(type, buf) {
|
||||
@@ -230,7 +234,7 @@ export function isType(type, buf) {
|
||||
/**
|
||||
* Detects whether the given buffer contains an image file.
|
||||
*
|
||||
* @param {Uint8Array} buf
|
||||
* @param {Uint8Array|ArrayBuffer} buf
|
||||
* @returns {string|false} The mime type or false if the type does not match
|
||||
*/
|
||||
export function isImage(buf) {
|
||||
|
||||
@@ -97,6 +97,7 @@ class Magic {
|
||||
|
||||
if (!fileType.length) return null;
|
||||
return {
|
||||
name: fileType[0].name,
|
||||
ext: fileType[0].extension,
|
||||
mime: fileType[0].mime,
|
||||
desc: fileType[0].description
|
||||
|
||||
@@ -37,7 +37,7 @@ export async function parseQrCode(input, normalise) {
|
||||
image = await jimp.read(image);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error normalising iamge. (${err})`);
|
||||
throw new OperationError(`Error normalising image. (${err})`);
|
||||
}
|
||||
|
||||
const qrData = jsQR(image.bitmap.data, image.getWidth(), image.getHeight());
|
||||
|
||||
@@ -121,7 +121,7 @@ class AddTextToImage extends Operation {
|
||||
let xPos = args[3],
|
||||
yPos = args[4];
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class BLAKE2b extends Operation {
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string} The input having been hashed with BLAKE2b in the encoding format speicifed.
|
||||
* @returns {string} The input having been hashed with BLAKE2b in the encoding format specified.
|
||||
*/
|
||||
run(input, args) {
|
||||
const [outSize, outFormat] = args;
|
||||
|
||||
@@ -51,7 +51,7 @@ class BLAKE2s extends Operation {
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string} The input having been hashed with BLAKE2s in the encoding format speicifed.
|
||||
* @returns {string} The input having been hashed with BLAKE2s in the encoding format specified.
|
||||
*/
|
||||
run(input, args) {
|
||||
const [outSize, outFormat] = args;
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* @author Karsten Silkenbäumer [github.com/kassi]
|
||||
* @copyright Karsten Silkenbäumer 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import {
|
||||
BACON_ALPHABETS,
|
||||
BACON_TRANSLATION_CASE, BACON_TRANSLATION_AMNZ, BACON_TRANSLATIONS, BACON_CLEARER_MAP, BACON_NORMALIZE_MAP,
|
||||
swapZeroAndOne
|
||||
} from "../lib/Bacon";
|
||||
|
||||
/**
|
||||
* Bacon Cipher Decode operation
|
||||
*/
|
||||
class BaconCipherDecode extends Operation {
|
||||
/**
|
||||
* BaconCipherDecode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Bacon Cipher Decode";
|
||||
this.module = "Default";
|
||||
this.description = "Bacon's cipher or the Baconian cipher is a method of steganography devised by Francis Bacon in 1605. A message is concealed in the presentation of text, rather than its content.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bacon%27s_cipher";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Alphabet",
|
||||
"type": "option",
|
||||
"value": Object.keys(BACON_ALPHABETS)
|
||||
},
|
||||
{
|
||||
"name": "Translation",
|
||||
"type": "option",
|
||||
"value": BACON_TRANSLATIONS
|
||||
},
|
||||
{
|
||||
"name": "Invert Translation",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [alphabet, translation, invert] = args;
|
||||
const alphabetObject = BACON_ALPHABETS[alphabet];
|
||||
|
||||
// remove invalid characters
|
||||
input = input.replace(BACON_CLEARER_MAP[translation], "");
|
||||
|
||||
// normalize to unique alphabet
|
||||
if (BACON_NORMALIZE_MAP[translation] !== undefined) {
|
||||
input = input.replace(/./g, function (c) {
|
||||
return BACON_NORMALIZE_MAP[translation][c];
|
||||
});
|
||||
} else if (translation === BACON_TRANSLATION_CASE) {
|
||||
const codeA = "A".charCodeAt(0);
|
||||
const codeZ = "Z".charCodeAt(0);
|
||||
input = input.replace(/./g, function (c) {
|
||||
const code = c.charCodeAt(0);
|
||||
if (code >= codeA && code <= codeZ) {
|
||||
return "1";
|
||||
} else {
|
||||
return "0";
|
||||
}
|
||||
});
|
||||
} else if (translation === BACON_TRANSLATION_AMNZ) {
|
||||
const words = input.split(/\s+/);
|
||||
const letters = words.map(function (e) {
|
||||
if (e) {
|
||||
const code = e[0].toUpperCase().charCodeAt(0);
|
||||
return code >= "N".charCodeAt(0) ? "1" : "0";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
});
|
||||
input = letters.join("");
|
||||
}
|
||||
|
||||
if (invert) {
|
||||
input = swapZeroAndOne(input);
|
||||
}
|
||||
|
||||
// group into 5
|
||||
const inputArray = input.match(/(.{5})/g) || [];
|
||||
|
||||
let output = "";
|
||||
for (let i = 0; i < inputArray.length; i++) {
|
||||
const code = inputArray[i];
|
||||
const number = parseInt(code, 2);
|
||||
output += number < alphabetObject.alphabet.length ? alphabetObject.alphabet[number] : "?";
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
export default BaconCipherDecode;
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @author Karsten Silkenbäumer [github.com/kassi]
|
||||
* @copyright Karsten Silkenbäumer 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import {
|
||||
BACON_ALPHABETS,
|
||||
BACON_TRANSLATIONS_FOR_ENCODING, BACON_TRANSLATION_AB,
|
||||
swapZeroAndOne
|
||||
} from "../lib/Bacon";
|
||||
|
||||
/**
|
||||
* Bacon Cipher Encode operation
|
||||
*/
|
||||
class BaconCipherEncode extends Operation {
|
||||
/**
|
||||
* BaconCipherEncode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Bacon Cipher Encode";
|
||||
this.module = "Default";
|
||||
this.description = "Bacon's cipher or the Baconian cipher is a method of steganography devised by Francis Bacon in 1605. A message is concealed in the presentation of text, rather than its content.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bacon%27s_cipher";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Alphabet",
|
||||
"type": "option",
|
||||
"value": Object.keys(BACON_ALPHABETS)
|
||||
},
|
||||
{
|
||||
"name": "Translation",
|
||||
"type": "option",
|
||||
"value": BACON_TRANSLATIONS_FOR_ENCODING
|
||||
},
|
||||
{
|
||||
"name": "Keep extra characters",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "Invert Translation",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [alphabet, translation, keep, invert] = args;
|
||||
|
||||
const alphabetObject = BACON_ALPHABETS[alphabet];
|
||||
const charCodeA = "A".charCodeAt(0);
|
||||
const charCodeZ = "Z".charCodeAt(0);
|
||||
|
||||
let output = input.replace(/./g, function (c) {
|
||||
const charCode = c.toUpperCase().charCodeAt(0);
|
||||
if (charCode >= charCodeA && charCode <= charCodeZ) {
|
||||
let code = charCode - charCodeA;
|
||||
if (alphabetObject.codes !== undefined) {
|
||||
code = alphabetObject.codes[code];
|
||||
}
|
||||
const bacon = ("00000" + code.toString(2)).substr(-5, 5);
|
||||
return bacon;
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
});
|
||||
|
||||
if (invert) {
|
||||
output = swapZeroAndOne(output);
|
||||
}
|
||||
if (!keep) {
|
||||
output = output.replace(/[^01]/g, "");
|
||||
const outputArray = output.match(/(.{5})/g) || [];
|
||||
output = outputArray.join(" ");
|
||||
}
|
||||
if (translation === BACON_TRANSLATION_AB) {
|
||||
output = output.replace(/[01]/g, function (c) {
|
||||
return {
|
||||
"0": "A",
|
||||
"1": "B"
|
||||
}[c];
|
||||
});
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
export default BaconCipherEncode;
|
||||
@@ -53,7 +53,7 @@ class BlurImage extends Operation {
|
||||
async run(input, args) {
|
||||
const [blurAmount, blurType] = args;
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class Bzip2Decompress extends Operation {
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
async run(input, args) {
|
||||
const [small] = args;
|
||||
if (input.byteLength <= 0) {
|
||||
throw new OperationError("Please provide an input.");
|
||||
|
||||
@@ -107,7 +107,7 @@ class ContainImage extends Operation {
|
||||
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
|
||||
};
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class ConvertDataUnits extends Operation {
|
||||
const DATA_UNITS = [
|
||||
"Bits (b)", "Nibbles", "Octets", "Bytes (B)",
|
||||
"[Binary bits (2^n)]", "Kibibits (Kib)", "Mebibits (Mib)", "Gibibits (Gib)", "Tebibits (Tib)", "Pebibits (Pib)", "Exbibits (Eib)", "Zebibits (Zib)", "Yobibits (Yib)", "[/Binary bits (2^n)]",
|
||||
"[Decimal bits (10^n)]", "Decabits", "Hectobits", "Kilobits (kb)", "Megabits (Mb)", "Gigabits (Gb)", "Terabits (Tb)", "Petabits (Pb)", "Exabits (Eb)", "Zettabits (Zb)", "Yottabits (Yb)", "[/Decimal bits (10^n)]",
|
||||
"[Decimal bits (10^n)]", "Decabits", "Hectobits", "Kilobits (Kb)", "Megabits (Mb)", "Gigabits (Gb)", "Terabits (Tb)", "Petabits (Pb)", "Exabits (Eb)", "Zettabits (Zb)", "Yottabits (Yb)", "[/Decimal bits (10^n)]",
|
||||
"[Binary bytes (8 x 2^n)]", "Kibibytes (KiB)", "Mebibytes (MiB)", "Gibibytes (GiB)", "Tebibytes (TiB)", "Pebibytes (PiB)", "Exbibytes (EiB)", "Zebibytes (ZiB)", "Yobibytes (YiB)", "[/Binary bytes (8 x 2^n)]",
|
||||
"[Decimal bytes (8 x 10^n)]", "Kilobytes (KB)", "Megabytes (MB)", "Gigabytes (GB)", "Terabytes (TB)", "Petabytes (PB)", "Exabytes (EB)", "Zettabytes (ZB)", "Yottabytes (YB)", "[/Decimal bytes (8 x 10^n)]"
|
||||
];
|
||||
|
||||
@@ -93,7 +93,7 @@ class ConvertImageFormat extends Operation {
|
||||
|
||||
const mime = formatMap[format];
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file format.");
|
||||
}
|
||||
let image;
|
||||
|
||||
@@ -102,7 +102,7 @@ class CoverImage extends Operation {
|
||||
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
|
||||
};
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ class CropImage extends Operation {
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [xPos, yPos, width, height, autocrop, autoTolerance, autoFrames, autoSymmetric, autoBorder] = args;
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,12 @@ class DESDecrypt extends Operation {
|
||||
DES uses a key length of 8 bytes (64 bits).
|
||||
Triple DES uses a key length of 24 bytes (192 bits).`);
|
||||
}
|
||||
if (iv.length !== 8) {
|
||||
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
||||
|
||||
DES uses an IV length of 8 bytes (64 bits).
|
||||
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
|
||||
@@ -73,6 +73,12 @@ class DESEncrypt extends Operation {
|
||||
DES uses a key length of 8 bytes (64 bits).
|
||||
Triple DES uses a key length of 24 bytes (192 bits).`);
|
||||
}
|
||||
if (iv.length !== 8) {
|
||||
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
||||
|
||||
DES uses an IV length of 8 bytes (64 bits).
|
||||
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
|
||||
@@ -8,6 +8,13 @@ import Operation from "../Operation.mjs";
|
||||
import {detectFileType} from "../lib/FileType.mjs";
|
||||
import {FILE_SIGNATURES} from "../lib/FileSignatures.mjs";
|
||||
|
||||
// Concat all supported extensions into a single flat list
|
||||
const exts = [].concat.apply([], Object.keys(FILE_SIGNATURES).map(cat =>
|
||||
[].concat.apply([], FILE_SIGNATURES[cat].map(sig =>
|
||||
sig.extension.split(",")
|
||||
))
|
||||
)).unique().sort().join(", ");
|
||||
|
||||
/**
|
||||
* Detect File Type operation
|
||||
*/
|
||||
@@ -22,11 +29,7 @@ class DetectFileType extends Operation {
|
||||
this.name = "Detect File Type";
|
||||
this.module = "Default";
|
||||
this.description = "Attempts to guess the MIME (Multipurpose Internet Mail Extensions) type of the data based on 'magic bytes'.<br><br>Currently supports the following file types: " +
|
||||
Object.keys(FILE_SIGNATURES).map(cat =>
|
||||
FILE_SIGNATURES[cat].map(sig =>
|
||||
sig.extension.split(",")[0]
|
||||
).join(", ")
|
||||
).join(", ") + ".";
|
||||
exts + ".";
|
||||
this.infoURL = "https://wikipedia.org/wiki/List_of_file_signatures";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
|
||||
@@ -38,7 +38,7 @@ class DitherImage extends Operation {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* @author Ge0rg3 [georgeomnet+cyberchef@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import { fromBinary } from "../lib/Binary.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import jimp from "jimp";
|
||||
|
||||
/**
|
||||
* Extract LSB operation
|
||||
*/
|
||||
class ExtractLSB extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractLSB constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract LSB";
|
||||
this.module = "Image";
|
||||
this.description = "Extracts the Least Significant Bit data from each pixel in an image. This is a common way to hide data in Steganography.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bit_numbering#Least_significant_bit_in_digital_steganography";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [
|
||||
{
|
||||
name: "Colour Pattern #1",
|
||||
type: "option",
|
||||
value: COLOUR_OPTIONS,
|
||||
},
|
||||
{
|
||||
name: "Colour Pattern #2",
|
||||
type: "option",
|
||||
value: ["", ...COLOUR_OPTIONS],
|
||||
},
|
||||
{
|
||||
name: "Colour Pattern #3",
|
||||
type: "option",
|
||||
value: ["", ...COLOUR_OPTIONS],
|
||||
},
|
||||
{
|
||||
name: "Colour Pattern #4",
|
||||
type: "option",
|
||||
value: ["", ...COLOUR_OPTIONS],
|
||||
},
|
||||
{
|
||||
name: "Pixel Order",
|
||||
type: "option",
|
||||
value: ["Row", "Column"],
|
||||
},
|
||||
{
|
||||
name: "Bit",
|
||||
type: "number",
|
||||
value: 0
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(input)) throw new OperationError("Please enter a valid image file.");
|
||||
|
||||
const bit = 7 - args.pop(),
|
||||
pixelOrder = args.pop(),
|
||||
colours = args.filter(option => option !== "").map(option => COLOUR_OPTIONS.indexOf(option)),
|
||||
parsedImage = await jimp.read(input),
|
||||
width = parsedImage.bitmap.width,
|
||||
height = parsedImage.bitmap.height,
|
||||
rgba = parsedImage.bitmap.data;
|
||||
|
||||
if (bit < 0 || bit > 7) {
|
||||
throw new OperationError("Error: Bit argument must be between 0 and 7");
|
||||
}
|
||||
|
||||
let i, combinedBinary = "";
|
||||
|
||||
if (pixelOrder === "Row") {
|
||||
for (i = 0; i < rgba.length; i += 4) {
|
||||
for (const colour of colours) {
|
||||
combinedBinary += Utils.bin(rgba[i + colour])[bit];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let rowWidth;
|
||||
const pixelWidth = width * 4;
|
||||
for (let col = 0; col < width; col++) {
|
||||
for (let row = 0; row < height; row++) {
|
||||
rowWidth = row * pixelWidth;
|
||||
for (const colour of colours) {
|
||||
i = rowWidth + (col + colour * 4);
|
||||
combinedBinary += Utils.bin(rgba[i])[bit];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fromBinary(combinedBinary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const COLOUR_OPTIONS = ["R", "G", "B", "A"];
|
||||
|
||||
export default ExtractLSB;
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @author Ge0rg3 [georgeomnet+cyberchef@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import jimp from "jimp";
|
||||
|
||||
import {RGBA_DELIM_OPTIONS} from "../lib/Delim.mjs";
|
||||
|
||||
/**
|
||||
* Extract RGBA operation
|
||||
*/
|
||||
class ExtractRGBA extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractRGBA constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract RGBA";
|
||||
this.module = "Image";
|
||||
this.description = "Extracts each pixel's RGBA value in an image. These are sometimes used in Steganography to hide text or data.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/RGBA_color_space";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Delimiter",
|
||||
type: "editableOption",
|
||||
value: RGBA_DELIM_OPTIONS
|
||||
},
|
||||
{
|
||||
name: "Include Alpha",
|
||||
type: "boolean",
|
||||
value: true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(input)) throw new OperationError("Please enter a valid image file.");
|
||||
|
||||
const delimiter = args[0],
|
||||
includeAlpha = args[1],
|
||||
parsedImage = await jimp.read(input);
|
||||
|
||||
let bitmap = parsedImage.bitmap.data;
|
||||
bitmap = includeAlpha ? bitmap : bitmap.filter((val, idx) => idx % 4 !== 3);
|
||||
|
||||
return bitmap.join(delimiter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ExtractRGBA;
|
||||
@@ -45,7 +45,7 @@ class FlipImage extends Operation {
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [flipAxis] = args;
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid input file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class FromQuotedPrintable extends Operation {
|
||||
|
||||
this.name = "From Quoted Printable";
|
||||
this.module = "Default";
|
||||
this.description = "Converts QP-encoded text back to standard text.";
|
||||
this.description = "Converts QP-encoded text back to standard text.<br><br>e.g. The quoted-printable encoded string <code>hello=20world</code> becomes <code>hello world</code>";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Quoted-printable";
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
|
||||
@@ -58,7 +58,7 @@ class GeneratePGPKeyPair extends Operation {
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
async run(input, args) {
|
||||
const [keyType, keySize] = args[0].split("-"),
|
||||
password = args[1],
|
||||
name = args[2],
|
||||
|
||||
@@ -57,7 +57,7 @@ class HammingDistance extends Operation {
|
||||
samples = input.split(delim);
|
||||
|
||||
if (samples.length !== 2) {
|
||||
throw new OperationError("Error: You can only calculae the edit distance between 2 strings. Please ensure exactly two inputs are provided, separated by the specified delimiter.");
|
||||
throw new OperationError("Error: You can only calculate 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) {
|
||||
|
||||
@@ -54,7 +54,7 @@ class ImageBrightnessContrast extends Operation {
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [brightness, contrast] = args;
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class ImageFilter extends Operation {
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [filterType] = args;
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class ImageHueSaturationLightness extends Operation {
|
||||
async run(input, args) {
|
||||
const [hue, saturation, lightness] = args;
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class ImageOpacity extends Operation {
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [opacity] = args;
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class InvertImage extends Operation {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid input file format.");
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class NormaliseImage extends Operation {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @author mshwed [m@ttshwed.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
|
||||
import Tesseract from "tesseract.js";
|
||||
const { TesseractWorker } = Tesseract;
|
||||
|
||||
import process from "process";
|
||||
|
||||
/**
|
||||
* Optical Character Recognition operation
|
||||
*/
|
||||
class OpticalCharacterRecognition extends Operation {
|
||||
|
||||
/**
|
||||
* OpticalCharacterRecognition constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Optical Character Recognition";
|
||||
this.module = "OCR";
|
||||
this.description = "Optical character recognition or optical character reader (OCR) is the mechanical or electronic conversion of images of typed, handwritten or printed text into machine-encoded text.<br><br>Supported image formats: png, jpg, bmp, pbm.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Optical_character_recognition";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Show confidence",
|
||||
type: "boolean",
|
||||
value: true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [showConfidence] = args;
|
||||
|
||||
if (!isWorkerEnvironment()) throw OperationError("This operation only works in a browser");
|
||||
|
||||
const type = isImage(input);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid File Type");
|
||||
}
|
||||
|
||||
const assetDir = isWorkerEnvironment() ? `${self.docURL}/assets/` : `${process.cwd()}/src/core/vendor/`;
|
||||
|
||||
try {
|
||||
const image = `data:${type};base64,${toBase64(input)}`;
|
||||
const worker = new TesseractWorker({
|
||||
workerPath: `${assetDir}tesseract/worker.min.js`,
|
||||
langPath: `${assetDir}tesseract/lang-data`,
|
||||
corePath: `${assetDir}tesseract/tesseract-core.wasm.js`,
|
||||
});
|
||||
const result = await worker.recognize(image)
|
||||
.progress(progress => {
|
||||
if (isWorkerEnvironment()) {
|
||||
self.sendStatusMessage(`Status: ${progress.status} - ${(parseFloat(progress.progress)*100).toFixed(2)}%`);
|
||||
}
|
||||
});
|
||||
|
||||
if (showConfidence) {
|
||||
return `Confidence: ${result.confidence}%\n\n${result.text}`;
|
||||
} else {
|
||||
return result.text;
|
||||
}
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error performing OCR on image. (${err})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default OpticalCharacterRecognition;
|
||||
@@ -51,7 +51,7 @@ class ParseQRCode extends Operation {
|
||||
async run(input, args) {
|
||||
const [normalise] = args;
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
return await parseQrCode(input, normalise);
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @author Ge0rg3 [georgeomnet+cyberchef@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { runHash } from "../lib/Hash.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import jimp from "jimp";
|
||||
|
||||
/**
|
||||
* Randomize Colour Palette operation
|
||||
*/
|
||||
class RandomizeColourPalette extends Operation {
|
||||
|
||||
/**
|
||||
* RandomizeColourPalette constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Randomize Colour Palette";
|
||||
this.module = "Image";
|
||||
this.description = "Randomizes each colour in an image's colour palette. This can often reveal text or symbols that were previously a very similar colour to their surroundings, a technique sometimes used in Steganography.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Indexed_color";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
name: "Seed",
|
||||
type: "string",
|
||||
value: ""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(input)) throw new OperationError("Please enter a valid image file.");
|
||||
|
||||
const seed = args[0] || (Math.random().toString().substr(2)),
|
||||
parsedImage = await jimp.read(input),
|
||||
width = parsedImage.bitmap.width,
|
||||
height = parsedImage.bitmap.height;
|
||||
|
||||
let rgbString, rgbHash, rgbHex;
|
||||
|
||||
parsedImage.scan(0, 0, width, height, function(x, y, idx) {
|
||||
rgbString = this.bitmap.data.slice(idx, idx+3).join(".");
|
||||
rgbHash = runHash("md5", Utils.strToArrayBuffer(seed + rgbString));
|
||||
rgbHex = rgbHash.substr(0, 6) + "ff";
|
||||
parsedImage.setPixelColor(parseInt(rgbHex, 16), x, y);
|
||||
});
|
||||
|
||||
const imageBuffer = await parsedImage.getBufferAsync(jimp.AUTO);
|
||||
|
||||
return new Uint8Array(imageBuffer).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the extracted data as an image for web apps.
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.byteLength) return "";
|
||||
const type = isImage(data);
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default RandomizeColourPalette;
|
||||
@@ -77,7 +77,7 @@ class RawInflate extends Operation {
|
||||
}),
|
||||
result = new Uint8Array(inflate.decompress());
|
||||
|
||||
// Raw Inflate somethimes messes up and returns nonsense like this:
|
||||
// Raw Inflate sometimes messes up and returns nonsense like this:
|
||||
// ]....]....]....]....]....]....]....]....]....]....]....]....]....]...
|
||||
// e.g. Input data of [8b, 1d, dc, 44]
|
||||
// Look for the first two square brackets:
|
||||
|
||||
@@ -64,7 +64,7 @@ class RemoveWhitespace extends Operation {
|
||||
run(input, args) {
|
||||
const [
|
||||
removeSpaces,
|
||||
removeCariageReturns,
|
||||
removeCarriageReturns,
|
||||
removeLineFeeds,
|
||||
removeTabs,
|
||||
removeFormFeeds,
|
||||
@@ -73,7 +73,7 @@ class RemoveWhitespace extends Operation {
|
||||
let data = input;
|
||||
|
||||
if (removeSpaces) data = data.replace(/ /g, "");
|
||||
if (removeCariageReturns) data = data.replace(/\r/g, "");
|
||||
if (removeCarriageReturns) data = data.replace(/\r/g, "");
|
||||
if (removeLineFeeds) data = data.replace(/\n/g, "");
|
||||
if (removeTabs) data = data.replace(/\t/g, "");
|
||||
if (removeFormFeeds) data = data.replace(/\f/g, "");
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import hljs from "highlight.js";
|
||||
|
||||
/**
|
||||
* Render Markdown operation
|
||||
*/
|
||||
class RenderMarkdown extends Operation {
|
||||
|
||||
/**
|
||||
* RenderMarkdown constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Render Markdown";
|
||||
this.module = "Code";
|
||||
this.description = "Renders input Markdown as HTML. HTML rendering is disabled to avoid XSS.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Markdown";
|
||||
this.inputType = "string";
|
||||
this.outputType = "html";
|
||||
this.args = [
|
||||
{
|
||||
name: "Autoconvert URLs to links",
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Enable syntax highlighting",
|
||||
type: "boolean",
|
||||
value: true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [convertLinks, enableHighlighting] = args,
|
||||
md = new MarkdownIt({
|
||||
linkify: convertLinks,
|
||||
html: false, // Explicitly disable HTML rendering
|
||||
highlight: function(str, lang) {
|
||||
if (lang && hljs.getLanguage(lang) && enableHighlighting) {
|
||||
try {
|
||||
return hljs.highlight(lang, str).value;
|
||||
} catch (__) {}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}),
|
||||
rendered = md.render(input);
|
||||
|
||||
return `<div style="font-family: var(--primary-font-family)">${rendered}</div>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default RenderMarkdown;
|
||||
@@ -87,7 +87,7 @@ class ResizeImage extends Operation {
|
||||
"Bezier": jimp.RESIZE_BEZIER
|
||||
};
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class RotateImage extends Operation {
|
||||
async run(input, args) {
|
||||
const [degrees] = args;
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class SharpenImage extends Operation {
|
||||
async run(input, args) {
|
||||
const [radius, amount, threshold] = args;
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Show on map operation
|
||||
*/
|
||||
class ShowOnMap extends Operation {
|
||||
|
||||
/**
|
||||
* ShowOnMap constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Show on map";
|
||||
this.module = "Hashing";
|
||||
this.description = "Displays co-ordinates on a slippy map.<br><br>Co-ordinates will be converted to decimal degrees before being shown on the map.<br><br>Supported formats:<ul><li>Degrees Minutes Seconds (DMS)</li><li>Degrees Decimal Minutes (DDM)</li><li>Decimal Degrees (DD)</li><li>Geohash</li><li>Military Grid Reference System (MGRS)</li><li>Ordnance Survey National Grid (OSNG)</li><li>Universal Transverse Mercator (UTM)</li></ul><br>This operation will not work offline.";
|
||||
this.infoURL = "https://foundation.wikimedia.org/wiki/Maps_Terms_of_Use";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
name: "Zoom Level",
|
||||
type: "number",
|
||||
value: 13
|
||||
},
|
||||
{
|
||||
name: "Input Format",
|
||||
type: "option",
|
||||
value: ["Auto"].concat(FORMATS)
|
||||
},
|
||||
{
|
||||
name: "Input Delimiter",
|
||||
type: "option",
|
||||
value: [
|
||||
"Auto",
|
||||
"Direction Preceding",
|
||||
"Direction Following",
|
||||
"\\n",
|
||||
"Comma",
|
||||
"Semi-colon",
|
||||
"Colon"
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
if (input.replace(/\s+/g, "") !== "") {
|
||||
const inFormat = args[1],
|
||||
inDelim = args[2];
|
||||
let latLong;
|
||||
try {
|
||||
latLong = convertCoordinates(input, inFormat, inDelim, "Decimal Degrees", "Comma", "None", 5);
|
||||
} catch (error) {
|
||||
throw new OperationError(error);
|
||||
}
|
||||
latLong = latLong.replace(/[,]$/, "");
|
||||
latLong = latLong.replace(/°/g, "");
|
||||
return latLong;
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} data
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async present(data, args) {
|
||||
if (data.replace(/\s+/g, "") === "") {
|
||||
data = "0, 0";
|
||||
}
|
||||
const zoomLevel = args[0];
|
||||
const tileUrl = "https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png",
|
||||
tileAttribution = "<a href=\"https://wikimediafoundation.org/wiki/Maps_Terms_of_Use\">Wikimedia maps</a> | © <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors",
|
||||
leafletUrl = "https://unpkg.com/leaflet@1.5.0/dist/leaflet.js",
|
||||
leafletCssUrl = "https://unpkg.com/leaflet@1.5.0/dist/leaflet.css";
|
||||
return `<link rel="stylesheet" href="${leafletCssUrl}" crossorigin=""/>
|
||||
<style>#output-html { white-space: normal; padding: 0; }</style>
|
||||
<div id="presentedMap" style="width: 100%; height: 100%;"></div>
|
||||
<script type="text/javascript">
|
||||
var mapscript = document.createElement('script');
|
||||
document.body.appendChild(mapscript);
|
||||
mapscript.onload = function() {
|
||||
var presentMap = L.map('presentedMap').setView([${data}], ${zoomLevel});
|
||||
L.tileLayer('${tileUrl}', {
|
||||
attribution: '${tileAttribution}'
|
||||
}).addTo(presentMap);
|
||||
|
||||
L.marker([${data}]).addTo(presentMap)
|
||||
.bindPopup('${data}')
|
||||
.openPopup();
|
||||
};
|
||||
mapscript.src = "${leafletUrl}";
|
||||
</script>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default ShowOnMap;
|
||||
@@ -40,7 +40,7 @@ class ToBase64 extends Operation {
|
||||
*/
|
||||
run(input, args) {
|
||||
const alphabet = args[0];
|
||||
return toBase64(new Uint8Array(input), alphabet);
|
||||
return toBase64(input, alphabet);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -108,7 +108,7 @@ class ToQuotedPrintable extends Operation {
|
||||
* @private
|
||||
* @param {number} nr
|
||||
* @param {byteArray[]} ranges
|
||||
* @returns {bolean}
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_checkRanges(nr, ranges) {
|
||||
for (let i = ranges.length - 1; i >= 0; i--) {
|
||||
|
||||
@@ -75,6 +75,12 @@ class TripleDESDecrypt extends Operation {
|
||||
Triple DES uses a key length of 24 bytes (192 bits).
|
||||
DES uses a key length of 8 bytes (64 bits).`);
|
||||
}
|
||||
if (iv.length !== 8) {
|
||||
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
||||
|
||||
Triple DES uses an IV length of 8 bytes (64 bits).
|
||||
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
|
||||
@@ -75,6 +75,12 @@ class TripleDESEncrypt extends Operation {
|
||||
Triple DES uses a key length of 24 bytes (192 bits).
|
||||
DES uses a key length of 8 bytes (64 bits).`);
|
||||
}
|
||||
if (iv.length !== 8) {
|
||||
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
||||
|
||||
Triple DES uses an IV length of 8 bytes (64 bits).
|
||||
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class Typex extends Operation {
|
||||
|
||||
this.name = "Typex";
|
||||
this.module = "Default";
|
||||
this.description = "Encipher/decipher with the WW2 Typex machine.<br><br>Typex was originally built by the British Royal Air Force prior to WW2, and is based on the Enigma machine with some improvements made, including using five rotors with more stepping points and interchangeable wiring cores. It was used across the British and Commonewealth militaries. A number of later variants were produced; here we simulate a WW2 era Mark 22 Typex with plugboards for the reflector and input. Typex rotors were changed regularly and none are public: a random example set are provided.<br><br>To configure the reflector plugboard, enter a string of connected pairs of letters in the reflector box, e.g. <code>AB CD EF</code> connects A to B, C to D, and E to F (you'll need to connect every letter). There is also an input plugboard: unlike Enigma's plugboard, it's not restricted to pairs, so it's entered like a rotor (without stepping). To create your own rotor, enter the letters that the rotor maps A to Z to, in order, optionally followed by <code><</code> then a list of stepping points.<br><br>More detailed descriptions of the Enigma, Typex and Bombe operations <a href='https://github.com/gchq/CyberChef/wiki/Enigma,-the-Bombe,-and-Typex'>can be found here</a>.";
|
||||
this.description = "Encipher/decipher with the WW2 Typex machine.<br><br>Typex was originally built by the British Royal Air Force prior to WW2, and is based on the Enigma machine with some improvements made, including using five rotors with more stepping points and interchangeable wiring cores. It was used across the British and Commonwealth militaries. A number of later variants were produced; here we simulate a WW2 era Mark 22 Typex with plugboards for the reflector and input. Typex rotors were changed regularly and none are public: a random example set are provided.<br><br>To configure the reflector plugboard, enter a string of connected pairs of letters in the reflector box, e.g. <code>AB CD EF</code> connects A to B, C to D, and E to F (you'll need to connect every letter). There is also an input plugboard: unlike Enigma's plugboard, it's not restricted to pairs, so it's entered like a rotor (without stepping). To create your own rotor, enter the letters that the rotor maps A to Z to, in order, optionally followed by <code><</code> then a list of stepping points.<br><br>More detailed descriptions of the Enigma, Typex and Bombe operations <a href='https://github.com/gchq/CyberChef/wiki/Enigma,-the-Bombe,-and-Typex'>can be found here</a>.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Typex";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
|
||||
@@ -56,7 +56,7 @@ class UNIXTimestampToWindowsFiletime extends Operation {
|
||||
} else if (units === "Milliseconds (ms)") {
|
||||
input = input.multipliedBy(new BigNumber("10000"));
|
||||
} else if (units === "Microseconds (μs)") {
|
||||
input = input.multiplyiedBy(new BigNumber("10"));
|
||||
input = input.multipliedBy(new BigNumber("10"));
|
||||
} else if (units === "Nanoseconds (ns)") {
|
||||
input = input.dividedBy(new BigNumber("100"));
|
||||
} else {
|
||||
|
||||
@@ -49,7 +49,7 @@ class URLEncode extends Operation {
|
||||
* @returns {string}
|
||||
*/
|
||||
encodeAllChars (str) {
|
||||
// TODO Do this programatically
|
||||
// TODO Do this programmatically
|
||||
return encodeURIComponent(str)
|
||||
.replace(/!/g, "%21")
|
||||
.replace(/#/g, "%23")
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* @author Ge0rg3 [georgeomnet+cyberchef@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import jimp from "jimp";
|
||||
|
||||
/**
|
||||
* View Bit Plane operation
|
||||
*/
|
||||
class ViewBitPlane extends Operation {
|
||||
|
||||
/**
|
||||
* ViewBitPlane constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "View Bit Plane";
|
||||
this.module = "Image";
|
||||
this.description = "Extracts and displays a bit plane of any given image. These show only a single bit from each pixel, and can be used to hide messages in Steganography.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bit_plane";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
name: "Colour",
|
||||
type: "option",
|
||||
value: COLOUR_OPTIONS
|
||||
},
|
||||
{
|
||||
name: "Bit",
|
||||
type: "number",
|
||||
value: 0
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(input)) throw new OperationError("Please enter a valid image file.");
|
||||
|
||||
const [colour, bit] = args,
|
||||
parsedImage = await jimp.read(input),
|
||||
width = parsedImage.bitmap.width,
|
||||
height = parsedImage.bitmap.height,
|
||||
colourIndex = COLOUR_OPTIONS.indexOf(colour),
|
||||
bitIndex = 7-bit;
|
||||
|
||||
if (bit < 0 || bit > 7) {
|
||||
throw new OperationError("Error: Bit argument must be between 0 and 7");
|
||||
}
|
||||
|
||||
let pixel, bin, newPixelValue;
|
||||
|
||||
parsedImage.scan(0, 0, width, height, function(x, y, idx) {
|
||||
pixel = this.bitmap.data[idx + colourIndex];
|
||||
bin = Utils.bin(pixel);
|
||||
newPixelValue = 255;
|
||||
|
||||
if (bin.charAt(bitIndex) === "1") newPixelValue = 0;
|
||||
|
||||
for (let i=0; i < 3; i++) {
|
||||
this.bitmap.data[idx + i] = newPixelValue;
|
||||
}
|
||||
this.bitmap.data[idx + 3] = 255;
|
||||
|
||||
});
|
||||
|
||||
const imageBuffer = await parsedImage.getBufferAsync(jimp.AUTO);
|
||||
|
||||
return new Uint8Array(imageBuffer).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the extracted data as an image for web apps.
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
const type = isImage(data);
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const COLOUR_OPTIONS = [
|
||||
"Red",
|
||||
"Green",
|
||||
"Blue",
|
||||
"Alpha"
|
||||
];
|
||||
|
||||
export default ViewBitPlane;
|
||||
@@ -55,7 +55,7 @@ class VigenèreDecode extends Operation {
|
||||
keyIndex = alphabet.indexOf(chr);
|
||||
msgIndex = alphabet.indexOf(input[i]);
|
||||
// Subtract indexes from each other, add 26 just in case the value is negative,
|
||||
// modulo to remove if neccessary
|
||||
// modulo to remove if necessary
|
||||
output += alphabet[(msgIndex - keyIndex + alphabet.length) % 26];
|
||||
} else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
|
||||
chr = key[(i - fail) % key.length].toLowerCase();
|
||||
|
||||
@@ -20,7 +20,7 @@ class Whirlpool extends Operation {
|
||||
|
||||
this.name = "Whirlpool";
|
||||
this.module = "Crypto";
|
||||
this.description = "Whirlpool is a cryptographic hash function designed by Vincent Rijmen (co-creator of AES) and Paulo S. L. M. Barreto, who first described it in 2000.<br><br>Several variants exist:<ul><li>Whirlpool-0 is the original version released in 2000.</li><li>Whirlpool-T is the first revision, released in 2001, improving the generation of the s-box.</li><li>Whirlpool is the latest revision, released in 2003, fixing a flaw in the difusion matrix.</li></ul>";
|
||||
this.description = "Whirlpool is a cryptographic hash function designed by Vincent Rijmen (co-creator of AES) and Paulo S. L. M. Barreto, who first described it in 2000.<br><br>Several variants exist:<ul><li>Whirlpool-0 is the original version released in 2000.</li><li>Whirlpool-T is the first revision, released in 2001, improving the generation of the s-box.</li><li>Whirlpool is the latest revision, released in 2003, fixing a flaw in the diffusion matrix.</li></ul>";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Whirlpool_(cryptography)";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
|
||||
var TesseractCoreWASM = (function() {
|
||||
var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
|
||||
return (
|
||||
function(TesseractCoreWASM) {
|
||||
TesseractCoreWASM = TesseractCoreWASM || {};
|
||||
|
||||
var Module=typeof TesseractCoreWASM!=="undefined"?TesseractCoreWASM:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}Module["arguments"]=[];Module["thisProgram"]="./this.program";Module["quit"]=(function(status,toThrow){throw toThrow});Module["preRun"]=[];Module["postRun"]=[];var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof require==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}else{return scriptDirectory+path}}if(ENVIRONMENT_IS_NODE){scriptDirectory=__dirname+"/";var nodeFS;var nodePath;Module["read"]=function shell_read(filename,binary){var ret;ret=tryParseAsDataURI(filename);if(!ret){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);ret=nodeFS["readFileSync"](filename)}return binary?ret:ret.toString()};Module["readBinary"]=function readBinary(filename){var ret=Module["read"](filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){Module["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}Module["arguments"]=process["argv"].slice(2);process["on"]("uncaughtException",(function(ex){if(!(ex instanceof ExitStatus)){throw ex}}));process["on"]("unhandledRejection",abort);Module["quit"]=(function(status){process["exit"](status)});Module["inspect"]=(function(){return"[Emscripten Module object]"})}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){Module["read"]=function shell_read(f){var data=tryParseAsDataURI(f);if(data){return intArrayToString(data)}return read(f)}}Module["readBinary"]=function readBinary(f){var data;data=tryParseAsDataURI(f);if(data){return data}if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof quit==="function"){Module["quit"]=(function(status){quit(status)})}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}Module["read"]=function shell_read(url){try{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText}catch(err){var data=tryParseAsDataURI(url);if(data){return intArrayToString(data)}throw err}};if(ENVIRONMENT_IS_WORKER){Module["readBinary"]=function readBinary(url){try{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}catch(err){var data=tryParseAsDataURI(url);if(data){return data}throw err}}}Module["readAsync"]=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}var data=tryParseAsDataURI(url);if(data){onload(data.buffer);return}onerror()};xhr.onerror=onerror;xhr.send(null)};Module["setWindowTitle"]=(function(title){document.title=title})}else{}var out=Module["print"]||(typeof console!=="undefined"?console.log.bind(console):typeof print!=="undefined"?print:null);var err=Module["printErr"]||(typeof printErr!=="undefined"?printErr:typeof console!=="undefined"&&console.warn.bind(console)||out);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=undefined;var STACK_ALIGN=16;function staticAlloc(size){var ret=STATICTOP;STATICTOP=STATICTOP+size+15&-16;return ret}function dynamicAlloc(size){var ret=HEAP32[DYNAMICTOP_PTR>>2];var end=ret+size+15&-16;HEAP32[DYNAMICTOP_PTR>>2]=end;if(end>=TOTAL_MEMORY){var success=enlargeMemory();if(!success){HEAP32[DYNAMICTOP_PTR>>2]=ret;return 0}}return ret}function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;var ret=size=Math.ceil(size/factor)*factor;return ret}function getNativeTypeSize(type){switch(type){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(type[type.length-1]==="*"){return 4}else if(type[0]==="i"){var bits=parseInt(type.substr(1));assert(bits%8===0);return bits/8}else{return 0}}}}var asm2wasmImports={"f64-remLine truncated
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return TesseractCoreWASM;
|
||||
}
|
||||
);
|
||||
})();
|
||||
if (typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = TesseractCoreWASM;
|
||||
else if (typeof define === 'function' && define['amd'])
|
||||
define([], function() { return TesseractCoreWASM; });
|
||||
else if (typeof exports === 'object')
|
||||
exports["TesseractCoreWASM"] = TesseractCoreWASM;
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
!function(t){var e={};function r(n){if(e[n])return e[n].exports;var i=e[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)r.d(n,i,function(e){return t[e]}.bind(null,i));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=12)}([function(t,e,r){"use strict";function n(t){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var i=r(5),o=r(25),s=Object.prototype.toString;function a(t){return"[object Array]"===s.call(t)}function f(t){return null!==t&&"object"===n(t)}function u(t){return"[object Function]"===s.call(t)}function h(t,e){if(null!=t)if("object"!==n(t)&&(t=[t]),a(t))for(var r=0,i=t.length;r<i;r++)e.call(null,t[r],r,t);else for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&e.call(null,t[o],o,t)}t.exports={isArray:a,isArrayBuffer:function(t){return"[object ArrayBuffer]"===s.call(t)},isBuffer:o,isFormData:function(t){return"undefined"!=typeof FormData&&t instanceof FormData},isArrayBufferView:function(t){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(t):t&&t.buffer&&t.buffer instanceof ArrayBuffer},isString:function(t){return"string"==typeof t},isNumber:function(t){return"number"==typeof t},isObject:f,isUndefined:function(t){return void 0===t},isDate:function(t){return"[object Date]"===s.call(t)},isFile:function(t){return"[object File]"===s.call(t)},isBlob:function(t){return"[object Blob]"===s.call(t)},isFunction:u,isStream:function(t){return f(t)&&u(t.pipe)},isURLSearchParams:function(t){return"undefined"!=typeof URLSearchParams&&t instanceof URLSearchParams},isStandardBrowserEnv:function(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product)&&"undefined"!=typeof window&&"undefined"!=typeof document},forEach:h,merge:function t(){var e={};function r(r,i){"object"===n(e[i])&&"object"===n(r)?e[i]=t(e[i],r):e[i]=r}for(var i=0,o=arguments.length;i<o;i++)h(arguments[i],r);return e},extend:function(t,e,r){return h(e,function(e,n){t[n]=r&&"function"==typeof e?i(e,r):e}),t},trim:function(t){return t.replace(/^\s*/,"").replace(/\s*$/,"")}}},function(t,e,r){"use strict";(function(t){
|
||||
/*!
|
||||
* The buffer module from node.js, for the browser.
|
||||
*
|
||||
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
var n=r(20),i=r(21),o=r(22);function s(){return f.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function a(t,e){if(s()<e)throw new RangeError("Invalid typed array length");return f.TYPED_ARRAY_SUPPORT?(t=new Uint8Array(e)).__proto__=f.prototype:(null===t&&(t=new f(e)),t.length=e),t}function f(t,e,r){if(!(f.TYPED_ARRAY_SUPPORT||this instanceof f))return new f(t,e,r);if("number"==typeof t){if("string"==typeof e)throw new Error("If encoding is specified then the first argument must be a string");return c(this,t)}return u(this,t,e,r)}function u(t,e,r,n){if("number"==typeof e)throw new TypeError('"value" argument must not be a number');return"undefined"!=typeof ArrayBuffer&&e instanceof ArrayBuffer?function(t,e,r,n){if(e.byteLength,r<0||e.byteLength<r)throw new RangeError("'offset' is out of bounds");if(e.byteLength<r+(n||0))throw new RangeError("'length' is out of bounds");e=void 0===r&&void 0===n?new Uint8Array(e):void 0===n?new Uint8Array(e,r):new Uint8Array(e,r,n);f.TYPED_ARRAY_SUPPORT?(t=e).__proto__=f.prototype:t=l(t,e);return t}(t,e,r,n):"string"==typeof e?function(t,e,r){"string"==typeof r&&""!==r||(r="utf8");if(!f.isEncoding(r))throw new TypeError('"encoding" must be a valid string encoding');var n=0|d(e,r),i=(t=a(t,n)).write(e,r);i!==n&&(t=t.slice(0,i));return t}(t,e,r):function(t,e){if(f.isBuffer(e)){var r=0|p(e.length);return 0===(t=a(t,r)).length?t:(e.copy(t,0,0,r),t)}if(e){if("undefined"!=typeof ArrayBuffer&&e.buffer instanceof ArrayBuffer||"length"in e)return"number"!=typeof e.length||(n=e.length)!=n?a(t,0):l(t,e);if("Buffer"===e.type&&o(e.data))return l(t,e.data)}var n;throw new TypeError("First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.")}(t,e)}function h(t){if("number"!=typeof t)throw new TypeError('"size" argument must be a number');if(t<0)throw new RangeError('"size" argument must not be negative')}function c(t,e){if(h(e),t=a(t,e<0?0:0|p(e)),!f.TYPED_ARRAY_SUPPORT)for(var r=0;r<e;++r)t[r]=0;return t}function l(t,e){var r=e.length<0?0:0|p(e.length);t=a(t,r);for(var n=0;n<r;n+=1)t[n]=255&e[n];return t}function p(t){if(t>=s())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+s().toString(16)+" bytes");return 0|t}function d(t,e){if(f.isBuffer(t))return t.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(t)||t instanceof ArrayBuffer))return t.byteLength;"string"!=typeof t&&(t=""+t);var r=t.length;if(0===r)return 0;for(var n=!1;;)switch(e){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":case void 0:return z(t).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return Y(t).length;default:if(n)return z(t).length;e=(""+e).toLowerCase(),n=!0}}function m(t,e,r){var n=t[e];t[e]=t[r],t[r]=n}function g(t,e,r,n,i){if(0===t.length)return-1;if("string"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),r=+r,isNaN(r)&&(r=i?0:t.length-1),r<0&&(r=t.length+r),r>=t.length){if(i)return-1;r=t.length-1}else if(r<0){if(!i)return-1;r=0}if("string"==typeof e&&(e=f.from(e,n)),f.isBuffer(e))return 0===e.length?-1:A(t,e,r,n,i);if("number"==typeof e)return e&=255,f.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(t,e,r):Uint8Array.prototype.lastIndexOf.call(t,e,r):A(t,[e],r,n,i);throw new TypeError("val must be string, number or Buffer")}function A(t,e,r,n,i){var o,s=1,a=t.length,f=e.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(t.length<2||e.length<2)return-1;s=2,a/=2,f/=2,r/=2}function u(t,e){return 1===s?t[e]:t.readUInt16BE(e*s)}if(i){var h=-1;for(o=r;o<a;o++)if(u(t,o)===u(e,-1===h?0:o-h)){if(-1===h&&(h=o),o-h+1===f)return h*s}else-1!==h&&(o-=o-h),h=-1}else for(r+f>a&&(r=a-f),o=r;o>=0;o--){for(var c=!0,l=0;l<f;l++)if(u(t,o+l)!==u(e,l)){c=!1;break}if(c)return o}return-1}function y(t,e,r,n){r=Number(r)||0;var i=t.length-r;n?(n=Number(n))>i&&(n=i):n=i;var o=e.length;if(o%2!=0)throw new TypeError("Invalid hex string");n>o/2&&(n=o/2);for(var s=0;s<n;++s){var a=parseInt(e.substr(2*s,2),16);if(isNaN(a))return s;t[r+s]=a}return s}function b(t,e,r,n){return F(z(e,t.length-r),t,r,n)}function v(t,e,r,n){return F(function(t){for(var e=[],r=0;r<t.length;++r)e.push(255&t.charCodeAt(r));return e}(e),t,r,n)}function w(t,e,r,n){return v(t,e,r,n)}function x(t,e,r,n){return F(Y(e),t,r,n)}function _(t,e,r,n){return F(function(t,e){for(var r,n,i,o=[],s=0;s<t.length&&!((e-=2)<0);++s)r=t.charCodeAt(s),n=r>>8,i=r%256,o.push(i),o.push(n);return o}(e,t.length-r),t,r,n)}function E(t,e,r){return 0===e&&r===t.length?n.fromByteArray(t):n.fromByteArray(t.slice(e,r))}function S(t,e,r){r=Math.min(t.length,r);for(var n=[],i=e;i<r;){var o,s,a,f,u=t[i],h=null,c=u>239?4:u>223?3:u>191?2:1;if(i+c<=r)switch(c){case 1:u<128&&(h=u);break;case 2:128==(192&(o=t[i+1]))&&(f=(31&u)<<6|63&o)>127&&(h=f);break;case 3:o=t[i+1],s=t[i+2],128==(1Line truncated
|
||||
/*!
|
||||
* Determine if an object is a Buffer
|
||||
*
|
||||
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
t.exports=function(t){return null!=t&&(r(t)||function(t){return"function"==typeof t.readFloatLE&&"function"==typeof t.slice&&r(t.slice(0,0))}(t)||!!t._isBuffer)}},function(t,e,r){"use strict";var n=r(2),i=r(0),o=r(35),s=r(36);function a(t){this.defaults=t,this.interceptors={request:new o,response:new o}}a.prototype.request=function(t){"string"==typeof t&&(t=i.merge({url:arguments[0]},arguments[1])),(t=i.merge(n,{method:"get"},this.defaults,t)).method=t.method.toLowerCase();var e=[s,void 0],r=Promise.resolve(t);for(this.interceptors.request.forEach(function(t){e.unshift(t.fulfilled,t.rejected)}),this.interceptors.response.forEach(function(t){e.push(t.fulfilled,t.rejected)});e.length;)r=r.then(e.shift(),e.shift());return r},i.forEach(["delete","get","head","options"],function(t){a.prototype[t]=function(e,r){return this.request(i.merge(r||{},{method:t,url:e}))}}),i.forEach(["post","put","patch"],function(t){a.prototype[t]=function(e,r,n){return this.request(i.merge(n||{},{method:t,url:e,data:r}))}}),t.exports=a},function(t,e,r){"use strict";var n=r(0);t.exports=function(t,e){n.forEach(t,function(r,n){n!==e&&n.toUpperCase()===e.toUpperCase()&&(t[e]=r,delete t[n])})}},function(t,e,r){"use strict";var n=r(8);t.exports=function(t,e,r){var i=r.config.validateStatus;r.status&&i&&!i(r.status)?e(n("Request failed with status code "+r.status,r.config,null,r.request,r)):t(r)}},function(t,e,r){"use strict";t.exports=function(t,e,r,n,i){return t.config=e,r&&(t.code=r),t.request=n,t.response=i,t}},function(t,e,r){"use strict";var n=r(0);function i(t){return encodeURIComponent(t).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}t.exports=function(t,e,r){if(!e)return t;var o;if(r)o=r(e);else if(n.isURLSearchParams(e))o=e.toString();else{var s=[];n.forEach(e,function(t,e){null!=t&&(n.isArray(t)?e+="[]":t=[t],n.forEach(t,function(t){n.isDate(t)?t=t.toISOString():n.isObject(t)&&(t=JSON.stringify(t)),s.push(i(e)+"="+i(t))}))}),o=s.join("&")}return o&&(t+=(-1===t.indexOf("?")?"?":"&")+o),t}},function(t,e,r){"use strict";var n=r(0),i=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];t.exports=function(t){var e,r,o,s={};return t?(n.forEach(t.split("\n"),function(t){if(o=t.indexOf(":"),e=n.trim(t.substr(0,o)).toLowerCase(),r=n.trim(t.substr(o+1)),e){if(s[e]&&i.indexOf(e)>=0)return;s[e]="set-cookie"===e?(s[e]?s[e]:[]).concat([r]):s[e]?s[e]+", "+r:r}}),s):s}},function(t,e,r){"use strict";var n=r(0);t.exports=n.isStandardBrowserEnv()?function(){var t,e=/(msie|trident)/i.test(navigator.userAgent),r=document.createElement("a");function i(t){var n=t;return e&&(r.setAttribute("href",n),n=r.href),r.setAttribute("href",n),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,""):"",host:r.host,search:r.search?r.search.replace(/^\?/,""):"",hash:r.hash?r.hash.replace(/^#/,""):"",hostname:r.hostname,port:r.port,pathname:"/"===r.pathname.charAt(0)?r.pathname:"/"+r.pathname}}return t=i(window.location.href),function(e){var r=n.isString(e)?i(e):e;return r.protocol===t.protocol&&r.host===t.host}}():function(){return!0}},function(t,e,r){"use strict";var n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";function i(){this.message="String contains an invalid character"}i.prototype=new Error,i.prototype.code=5,i.prototype.name="InvalidCharacterError",t.exports=function(t){for(var e,r,o=String(t),s="",a=0,f=n;o.charAt(0|a)||(f="=",a%1);s+=f.charAt(63&e>>8-a%1*8)){if((r=o.charCodeAt(a+=.75))>255)throw new i;e=e<<8|r}return s}},function(t,e,r){"use strict";var n=r(0);t.exports=n.isStandardBrowserEnv()?{write:function(t,e,r,i,o,s){var a=[];a.push(t+"="+encodeURIComponent(e)),n.isNumber(r)&&a.push("expires="+new Date(r).toGMTString()),n.isString(i)&&a.push("path="+i),n.isString(o)&&a.push("domain="+o),!0===s&&a.push("secure"),document.cookie=a.join("; ")},read:function(t){var e=document.cookie.match(new RegExp("(^|;\\s*)("+t+")=([^;]*)"));return e?decodeURIComponent(e[3]):null},remove:function(t){this.write(t,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}}},function(t,e,r){"use strict";var n=r(0);function i(){this.handlers=[]}i.prototype.use=function(t,e){return this.handlers.push({fulfilled:t,rejected:e}),this.handlers.length-1},i.prototype.eject=function(t){this.handlers[t]&&(this.handlers[t]=null)},i.prototype.forEach=function(t){n.forEach(this.handlers,function(e){null!==e&&t(e)})},t.exports=i},function(t,e,r){"use strict";var n=r(0),i=r(37),o=r(9),s=r(2),a=r(38),f=r(39);function u(t){t.cancelToken&&t.cancelToken.throwIfRequested()}t.exports=function(t){return u(t),t.baseURL&&!a(t.url)&&(t.url=f(t.baseURL,t.url)),t.headers=t.headers||{},t.data=i(t.data,t.headers,t.transformRequest),t.headers=n.merge(t.headers.commonLine truncated
|
||||
(function(){"use strict";function n(t){throw t}var i=void 0,o=!0,s="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array&&"undefined"!=typeof DataView;function a(t,e){this.index="number"==typeof e?e:0,this.m=0,this.buffer=t instanceof(s?Uint8Array:Array)?t:new(s?Uint8Array:Array)(32768),2*this.buffer.length<=this.index&&n(Error("invalid index")),this.buffer.length<=this.index&&this.f()}a.prototype.f=function(){var t,e=this.buffer,r=e.length,n=new(s?Uint8Array:Array)(r<<1);if(s)n.set(e);else for(t=0;t<r;++t)n[t]=e[t];return this.buffer=n},a.prototype.d=function(t,e,r){var n,i=this.buffer,o=this.index,s=this.m,a=i[o];if(r&&1<e&&(t=8<e?(p[255&t]<<24|p[t>>>8&255]<<16|p[t>>>16&255]<<8|p[t>>>24&255])>>32-e:p[t]>>8-e),8>e+s)a=a<<e|t,s+=e;else for(n=0;n<e;++n)a=a<<1|t>>e-n-1&1,8==++s&&(s=0,i[o++]=p[a],a=0,o===i.length&&(i=this.f()));i[o]=a,this.buffer=i,this.m=s,this.index=o},a.prototype.finish=function(){var t,e=this.buffer,r=this.index;return 0<this.m&&(e[r]<<=8-this.m,e[r]=p[e[r]],r++),s?t=e.subarray(0,r):(e.length=r,t=e),t};var f,u=new(s?Uint8Array:Array)(256);for(f=0;256>f;++f){for(var h=l=f,c=7,l=l>>>1;l;l>>>=1)h<<=1,h|=1&l,--c;u[f]=(h<<c&255)>>>0}var p=u;function d(t,e,r){var n,i="number"==typeof e?e:e=0,o="number"==typeof r?r:t.length;for(n=-1,i=7&o;i--;++e)n=n>>>8^g[255&(n^t[e])];for(i=o>>3;i--;e+=8)n=(n=(n=(n=(n=(n=(n=(n=n>>>8^g[255&(n^t[e])])>>>8^g[255&(n^t[e+1])])>>>8^g[255&(n^t[e+2])])>>>8^g[255&(n^t[e+3])])>>>8^g[255&(n^t[e+4])])>>>8^g[255&(n^t[e+5])])>>>8^g[255&(n^t[e+6])])>>>8^g[255&(n^t[e+7])];return(4294967295^n)>>>0}var m=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117],g=s?new Uint32Array(m):m;function A(){}function y(t){this.buffer=new(s?Uint16Array:Array)(2*t),this.length=0}function b(t){var e,r,n,i,o,a,f,u,h,c,l=t.length,p=0,d=Number.POSITIVE_INFINITY;for(u=0;u<l;++u)t[u]>p&&(p=t[u]),t[u]<d&&(d=t[u]);for(e=1<<p,r=new(s?Uint32Array:Array)(e),n=1,i=0,o=2;n<=p;){for(u=0;u<l;++u)if(t[u]===n){for(a=0,f=i,h=0;h<n;++h)a=a<<1|1&f,f>>=1;for(c=n<<16|u,h=a;h<e;h+=o)r[h]=c;++i}++n,i<<=1,o<<=1}return[r,p,d]}function v(t,e){this.k=x,this.F=0,this.input=s&&t instanceof Array?new Uint8Array(t):t,this.b=0,e&&(e.lazy&&(this.F=e.lazy),"number"==typeof e.compressionType&&(this.k=e.compressionType),e.outputBuffer&&(this.a=s&&e.Line truncated
|
||||
//# sourceMappingURL=worker.min.js.map
|
||||
@@ -43,11 +43,13 @@ class NodeRecipe {
|
||||
} else {
|
||||
throw new TypeError("Inputted function not a Chef operation.");
|
||||
}
|
||||
// CASE: op with configuration
|
||||
} else if (ing.op && ing.args) {
|
||||
// Return op and args pair for opList item.
|
||||
// CASE: op, maybe with configuration
|
||||
} else if (ing.op) {
|
||||
const sanitisedOp = this._validateIngredient(ing.op);
|
||||
return {op: sanitisedOp, args: ing.args};
|
||||
if (ing.args) {
|
||||
return {op: sanitisedOp, args: ing.args};
|
||||
}
|
||||
return sanitisedOp;
|
||||
} else {
|
||||
throw new TypeError("Recipe can only contain function names or functions");
|
||||
}
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@ import NodeDish from "./NodeDish.mjs";
|
||||
import NodeRecipe from "./NodeRecipe.mjs";
|
||||
import OperationConfig from "../core/config/OperationConfig.json";
|
||||
import { sanitise, removeSubheadingsFromArray, sentenceToCamelCase } from "./apiUtils.mjs";
|
||||
import ExludedOperationError from "../core/errors/ExcludedOperationError.mjs";
|
||||
import ExcludedOperationError from "../core/errors/ExcludedOperationError.mjs";
|
||||
|
||||
|
||||
/**
|
||||
@@ -320,12 +320,12 @@ export function bake() {
|
||||
* Explain that the given operation is not included in the Node.js version.
|
||||
* @param {String} name - name of operation
|
||||
*/
|
||||
export function _explainExludedFunction(name) {
|
||||
export function _explainExcludedFunction(name) {
|
||||
/**
|
||||
* Throw new error type with useful message.
|
||||
*/
|
||||
const func = () => {
|
||||
throw new ExludedOperationError(`Sorry, the ${name} operation is not available in the Node.js version of CyberChef.`);
|
||||
throw new ExcludedOperationError(`Sorry, the ${name} operation is not available in the Node.js version of CyberChef.`);
|
||||
};
|
||||
// Add opName prop so NodeRecipe can handle it, just like wrap does.
|
||||
func.opName = name;
|
||||
|
||||
@@ -71,7 +71,7 @@ export function sanitise(str) {
|
||||
|
||||
|
||||
/**
|
||||
* sonething like this => somethingLikeThis
|
||||
* something like this => somethingLikeThis
|
||||
* ABC a sentence => ABCASentence
|
||||
*/
|
||||
export function sentenceToCamelCase(str) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Operations to exlude from the Node API
|
||||
* Operations to exclude from the Node API
|
||||
*
|
||||
* @author d98762656 [d98762625@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
|
||||
@@ -39,8 +39,9 @@ let code = `/**
|
||||
|
||||
|
||||
import NodeDish from "./NodeDish.mjs";
|
||||
import { _wrap, help, bake, _explainExludedFunction } from "./api.mjs";
|
||||
import { _wrap, help, bake, _explainExcludedFunction } from "./api.mjs";
|
||||
import File from "./File.mjs";
|
||||
import { OperationError, DishError, ExcludedOperationError } from "../core/errors/index";
|
||||
import {
|
||||
// import as core_ to avoid name clashes after wrap.
|
||||
`;
|
||||
@@ -69,7 +70,7 @@ includedOperations.forEach((op) => {
|
||||
});
|
||||
|
||||
excludedOperations.forEach((op) => {
|
||||
code += ` "${decapitalise(op)}": _explainExludedFunction("${op}"),\n`;
|
||||
code += ` "${decapitalise(op)}": _explainExcludedFunction("${op}"),\n`;
|
||||
});
|
||||
|
||||
code += ` };
|
||||
@@ -115,6 +116,9 @@ Object.keys(operations).forEach((op) => {
|
||||
code += " NodeDish as Dish,\n";
|
||||
code += " prebaked as bake,\n";
|
||||
code += " help,\n";
|
||||
code += " OperationError,\n";
|
||||
code += " ExcludedOperationError,\n";
|
||||
code += " DishError,\n";
|
||||
code += "};\n";
|
||||
|
||||
|
||||
|
||||
+3
-26
@@ -4,7 +4,7 @@
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "../core/Utils";
|
||||
import Utils, { debounce } from "../core/Utils";
|
||||
import {fromBase64} from "../core/lib/Base64";
|
||||
import Manager from "./Manager";
|
||||
import HTMLCategory from "./HTMLCategory";
|
||||
@@ -41,7 +41,6 @@ class App {
|
||||
this.autoBakePause = false;
|
||||
this.progress = 0;
|
||||
this.ingId = 0;
|
||||
this.timeouts = {};
|
||||
}
|
||||
|
||||
|
||||
@@ -295,7 +294,7 @@ class App {
|
||||
minSize: minimise ? [0, 0, 0] : [240, 310, 450],
|
||||
gutterSize: 4,
|
||||
expandToMin: true,
|
||||
onDrag: this.debounce(function() {
|
||||
onDrag: debounce(function() {
|
||||
this.manager.recipe.adjustWidth();
|
||||
this.manager.input.calcMaxTabs();
|
||||
this.manager.output.calcMaxTabs();
|
||||
@@ -723,6 +722,7 @@ class App {
|
||||
this.updateTitle(false, null, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the page title to contain the new recipe
|
||||
*
|
||||
@@ -766,29 +766,6 @@ class App {
|
||||
this.loadURIParams();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Debouncer to stop functions from being executed multiple times in a
|
||||
* short space of time
|
||||
* https://davidwalsh.name/javascript-debounce-function
|
||||
*
|
||||
* @param {function} func - The function to be executed after the debounce time
|
||||
* @param {number} wait - The time (ms) to wait before executing the function
|
||||
* @param {string} id - Unique ID to reference the timeout for the function
|
||||
* @param {object} scope - The object to bind to the debounced function
|
||||
* @param {array} args - Array of arguments to be passed to func
|
||||
* @returns {function}
|
||||
*/
|
||||
debounce(func, wait, id, scope, args) {
|
||||
return function() {
|
||||
const later = function() {
|
||||
func.apply(scope, args);
|
||||
};
|
||||
clearTimeout(this.timeouts[id]);
|
||||
this.timeouts[id] = setTimeout(later, wait);
|
||||
}.bind(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1 +0,0 @@
|
||||
clippy.ready('Clippy', {"overlayCount": 1, "sounds": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"], "framesize": [124, 93], "animations": {"Congratulate": {"frames": [{"duration": 100, "images": [[0, 0]], "sound": "15"}, {"duration": 10, "images": [[124, 0]]}, {"duration": 10, "images": [[248, 0]]}, {"duration": 10, "images": [[372, 0]], "sound": "14"}, {"duration": 10, "images": [[496, 0]]}, {"duration": 10, "images": [[620, 0]]}, {"duration": 10, "images": [[744, 0]]}, {"duration": 10, "images": [[868, 0]]}, {"duration": 10, "images": [[992, 0]], "sound": "1"}, {"duration": 100, "images": [[1116, 0]]}, {"duration": 100, "images": [[1240, 0]]}, {"duration": 100, "images": [[1364, 0]]}, {"duration": 1200, "images": [[1488, 0]]}, {"duration": 100, "images": [[1612, 0]], "sound": "10"}, {"duration": 100, "images": [[1736, 0]]}, {"duration": 1200, "images": [[1488, 0]]}, {"duration": 100, "images": [[1860, 0]]}, {"duration": 100, "images": [[1984, 0]]}, {"duration": 100, "images": [[2108, 0]]}, {"duration": 100, "images": [[2232, 0]]}, {"duration": 100, "images": [[2356, 0]], "exitBranch": 21}, {"duration": 100, "images": [[0, 0]]}]}, "LookRight": {"frames": [{"duration": 100, "images": [[0, 0]], "exitBranch": 6}, {"duration": 100, "images": [[620, 651]], "exitBranch": 5}, {"duration": 100, "images": [[744, 651]], "exitBranch": 4}, {"duration": 1200, "images": [[868, 651]]}, {"duration": 100, "images": [[992, 651]]}, {"duration": 100, "images": [[1116, 651]]}, {"duration": 100, "images": [[0, 0]]}]}, "SendMail": {"frames": [{"duration": 100, "images": [[0, 0]], "sound": "15"}, {"duration": 100, "images": [[1240, 1209]]}, {"duration": 100, "images": [[1364, 1209]]}, {"duration": 100, "images": [[1488, 1209]]}, {"duration": 100, "images": [[1612, 1209]]}, {"duration": 100, "images": [[1736, 1209]]}, {"duration": 100, "images": [[1860, 1209]]}, {"duration": 100, "images": [[1984, 1209]]}, {"duration": 100, "images": [[2108, 1209]]}, {"duration": 100, "images": [[2232, 1209]]}, {"duration": 100, "images": [[2356, 1209]]}, {"duration": 100, "images": [[2480, 1209]]}, {"duration": 100, "images": [[2604, 1209]]}, {"duration": 100, "images": [[2728, 1209]]}, {"duration": 100, "images": [[2852, 1209]]}, {"duration": 100, "images": [[2976, 1209]]}, {"duration": 100, "images": [[3100, 1209]]}, {"duration": 100, "images": [[3224, 1209]]}, {"duration": 100, "images": [[0, 1302]]}, {"duration": 100, "images": [[124, 1302]]}, {"duration": 100, "images": [[248, 1302]]}, {"duration": 100, "images": [[372, 1302]], "sound": "14"}, {"duration": 100, "images": [[496, 1302]], "exitBranch": 24}, {"duration": 100, "images": [[620, 1302]]}, {"duration": 100, "images": [[744, 1302]], "exitBranch": 26}, {"duration": 100, "images": [[868, 1302]]}, {"duration": 100, "images": [[992, 1302]], "exitBranch": 27}, {"duration": 100, "images": [[1116, 1302]], "exitBranch": 28}, {"duration": 100, "images": [[1240, 1302]], "exitBranch": 29}, {"duration": 100, "images": [[1364, 1302]], "exitBranch": 30}, {"duration": 100, "images": [[1488, 1302]], "exitBranch": 31}, {"duration": 100, "images": [[1612, 1302]], "exitBranch": 32}, {"duration": 100, "images": [[1736, 1302]]}, {"duration": 100, "images": [[1860, 1302]]}, {"duration": 100, "images": [[1984, 1302]]}, {"duration": 100, "images": [[2108, 1302]]}, {"duration": 100, "images": [[2232, 1302]]}, {"duration": 100, "images": [[2356, 1302]]}, {"duration": 100, "images": [[2480, 1302]]}, {"duration": 100, "images": [[2604, 1302]]}, {"duration": 100, "images": [[2728, 1302]]}, {"duration": 100, "images": [[2852, 1302]]}, {"duration": 100, "images": [[2976, 1302]]}, {"duration": 100, "images": [[3100, 1302]]}, {"duration": 100, "images": [[3224, 1302]]}, {"duration": 100, "images": [[0, 1395]]}, {"duration": 100, "images": [[124, 1395]]}, {"duration": 100, "images": [[248, 1395]], "exitBranch": 48}, {"duration": 100, "images": [[372, 1395]], "exitBranch": 49}, {"duration": 100, "images": [[496, 1395]]}, {"duration": 100, "images": [[620, 1395]], "sound": "4"}, {"duration": 100, "images": [[744, 1395]]}, {"duration": 100, "images": [[868, 1395]]}, {"duration": 600}, {"duration": 100, "images": [[992, 1395]]}, {"duration": 100, "images": [[1116, 1395]]}, {"duration": 100, "images": [[1240, 1395]]}, {"duration": 100, "images": [[1364, 1395]]}, {"duration": 100, "images": [[1488, 1395]]}, {"duration": 100, "images": [[1612, 1395]]}, {"duration": 100, "images": [[1736, 1395]]}, {"duration": 100, "images": [[1860, 1395]]}, {"duration": 100, "images": [[0, 0]]}]}, "Thinking": {"frames": [{"duration": 100, "images": [[0, 0]], "sound": "15"}, {"duration": 100, "images": [[124, 93]]}, {"duration": 100, "images": [[248, 93]]}, {"duration": 100, "images": [[372, 93]]}, {"duration": 100, "images": [[496, 93]], "sound": "14"}, {"duration": 100, "images": [[620, 93]]}, {"duration": 100, "images": [[744, 93]]}, {"duration": 100, "images": [[868, 93]]}, {"duration": 100, "images":
Line truncated
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB |
@@ -1,62 +0,0 @@
|
||||
.clippy, .clippy-balloon {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clippy-balloon {
|
||||
|
||||
background: #FFC;
|
||||
color: black;
|
||||
padding: 8px;
|
||||
border: 1px solid black;
|
||||
border-radius: 5px;
|
||||
|
||||
}
|
||||
|
||||
.clippy-content {
|
||||
max-width: 200px;
|
||||
min-width: 120px;
|
||||
font-family: "Microsoft Sans", sans-serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.clippy-tip {
|
||||
width: 10px;
|
||||
height: 16px;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAgCAMAAAAlvKiEAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRF///MAAAA////52QwgAAAAAN0Uk5T//8A18oNQQAAAGxJREFUeNqs0kEOwCAIRFHn3//QTUU6xMyyxii+jQosrTPkyPEM6IN3FtzIRk1U4dFeKWQiH6pRRowMVKEmvronEynkwj0uZJgR22+YLopPSo9P34wJSamLSU7lSIWLJU7NkNomNlhqxUeAAQC+TQLZyEuJBwAAAABJRU5ErkJggg==) no-repeat;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.clippy-top-left .clippy-tip {
|
||||
top: 100%;
|
||||
margin-top: 0px;
|
||||
left: 100%;
|
||||
margin-left: -50px;
|
||||
}
|
||||
|
||||
.clippy-top-right .clippy-tip {
|
||||
top: 100%;
|
||||
margin-top: 0px;
|
||||
left: 0;
|
||||
margin-left: 50px;
|
||||
background-position: -10px 0;
|
||||
|
||||
}
|
||||
|
||||
.clippy-bottom-right .clippy-tip {
|
||||
top: 0;
|
||||
margin-top: -16px;
|
||||
left: 0;
|
||||
margin-left: 50px;
|
||||
background-position: -10px -16px;
|
||||
}
|
||||
|
||||
.clippy-bottom-left .clippy-tip {
|
||||
top: 0;
|
||||
margin-top: -16px;
|
||||
left: 100%;
|
||||
margin-left: -50px;
|
||||
background-position: 0px -16px;
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 229 B |
Binary file not shown.
|
Before Width: | Height: | Size: 238 B |
@@ -1,4 +1,4 @@
|
||||
import sm from "sitemap";
|
||||
import Sitemap from "sitemap";
|
||||
import OperationConfig from "../../core/config/OperationConfig.json";
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import OperationConfig from "../../core/config/OperationConfig.json";
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
const sitemap = sm.createSitemap({
|
||||
const sitemap = Sitemap.createSitemap({
|
||||
hostname: "https://gchq.github.io/CyberChef",
|
||||
});
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
/* Libraries */
|
||||
import "highlight.js/styles/vs.css";
|
||||
import "../static/clippy_assets/clippy.css";
|
||||
|
||||
/* Frameworks */
|
||||
import "./vendors/bootstrap.scss";
|
||||
|
||||
@@ -45,6 +45,10 @@ div#output {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#operations.split {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.split.split-horizontal, .gutter.gutter-horizontal {
|
||||
height: 100%;
|
||||
float: left;
|
||||
|
||||
@@ -82,14 +82,6 @@ a:focus {
|
||||
border-color: var(--btn-success-hover-border-colour);
|
||||
}
|
||||
|
||||
input[type="search"] {
|
||||
appearance: searchfield;
|
||||
}
|
||||
|
||||
input[type="search"]::-webkit-search-cancel-button {
|
||||
appearance: searchfield-cancel-button;
|
||||
}
|
||||
|
||||
select.form-control:not([size]):not([multiple]), select.custom-file-control:not([size]):not([multiple]) {
|
||||
height: unset !important;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import ChefWorker from "worker-loader?inline&fallback=false!../../core/ChefWorker";
|
||||
import ChefWorker from "worker-loader?inline&fallback=false!../../core/ChefWorker.js";
|
||||
|
||||
/**
|
||||
* Waiter to handle conversations with a ChefWorker in the background.
|
||||
|
||||
@@ -25,7 +25,7 @@ class ControlsWaiter {
|
||||
|
||||
|
||||
/**
|
||||
* Initialise Bootstrap componenets
|
||||
* Initialise Bootstrap components
|
||||
*/
|
||||
initComponents() {
|
||||
$("body").bootstrapMaterialDesign();
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import LoaderWorker from "worker-loader?inline&fallback=false!../workers/LoaderWorker";
|
||||
import InputWorker from "worker-loader?inline&fallback=false!../workers/InputWorker";
|
||||
import Utils from "../../core/Utils.mjs";
|
||||
import LoaderWorker from "worker-loader?inline&fallback=false!../workers/LoaderWorker.js";
|
||||
import InputWorker from "worker-loader?inline&fallback=false!../workers/InputWorker.mjs";
|
||||
import Utils, { debounce } from "../../core/Utils.mjs";
|
||||
import { toBase64 } from "../../core/lib/Base64.mjs";
|
||||
import { isImage } from "../../core/lib/FileType.mjs";
|
||||
|
||||
@@ -270,7 +270,7 @@ class InputWaiter {
|
||||
this.showLoadingInfo(r.data, true);
|
||||
break;
|
||||
case "setInput":
|
||||
this.app.debounce(this.set, 50, "setInput", this, [r.data.inputObj, r.data.silent])();
|
||||
debounce(this.set, 50, "setInput", this, [r.data.inputObj, r.data.silent])();
|
||||
break;
|
||||
case "inputAdded":
|
||||
this.inputAdded(r.data.changeTab, r.data.inputNum);
|
||||
@@ -316,7 +316,7 @@ class InputWaiter {
|
||||
*/
|
||||
bakeAll() {
|
||||
this.app.progress = 0;
|
||||
this.app.debounce(this.manager.controls.toggleBakeButtonFunction, 20, "toggleBakeButton", this, ["loading"]);
|
||||
debounce(this.manager.controls.toggleBakeButtonFunction, 20, "toggleBakeButton", this, ["loading"]);
|
||||
this.inputWorker.postMessage({
|
||||
action: "bakeAll"
|
||||
});
|
||||
@@ -681,7 +681,7 @@ class InputWaiter {
|
||||
* @param {event} e
|
||||
*/
|
||||
debounceInputChange(e) {
|
||||
this.app.debounce(this.inputChange, 50, "inputChange", this, [e])();
|
||||
debounce(this.inputChange, 50, "inputChange", this, [e])();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -172,7 +172,7 @@ class OperationsWaiter {
|
||||
$(el).find("[data-toggle=popover]").addBack("[data-toggle=popover]")
|
||||
.popover({trigger: "manual"})
|
||||
.on("mouseenter", function(e) {
|
||||
if (e.buttons > 0) return; // Mouse button held down - likely dragging an opertion
|
||||
if (e.buttons > 0) return; // Mouse button held down - likely dragging an operation
|
||||
const _this = this;
|
||||
$(this).popover("show");
|
||||
$(".popover").on("mouseleave", function () {
|
||||
|
||||
@@ -58,7 +58,7 @@ class OptionsWaiter {
|
||||
|
||||
/**
|
||||
* Handler for options click events.
|
||||
* Dispays the options pane.
|
||||
* Displays the options pane.
|
||||
*
|
||||
* @param {event} e
|
||||
*/
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "../../core/Utils.mjs";
|
||||
import Utils, { debounce } from "../../core/Utils.mjs";
|
||||
import Dish from "../../core/Dish.mjs";
|
||||
import FileSaver from "file-saver";
|
||||
import ZipWorker from "worker-loader?inline&fallback=false!../workers/ZipWorker";
|
||||
import ZipWorker from "worker-loader?inline&fallback=false!../workers/ZipWorker.mjs";
|
||||
|
||||
/**
|
||||
* Waiter to handle events related to the output
|
||||
@@ -174,7 +174,7 @@ class OutputWaiter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the stored bake ID for the output in the ouptut array
|
||||
* Updates the stored bake ID for the output in the output array
|
||||
*
|
||||
* @param {number} bakeId
|
||||
* @param {number} inputNum
|
||||
@@ -369,7 +369,7 @@ class OutputWaiter {
|
||||
}
|
||||
|
||||
this.setOutputInfo(length, lines, output.data.duration);
|
||||
this.backgroundMagic();
|
||||
debounce(this.backgroundMagic, 50, "backgroundMagic", this, [])();
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
@@ -717,7 +717,7 @@ class OutputWaiter {
|
||||
}
|
||||
}
|
||||
|
||||
this.app.debounce(this.set, 50, "setOutput", this, [inputNum])();
|
||||
debounce(this.set, 50, "setOutput", this, [inputNum])();
|
||||
|
||||
document.getElementById("output-html").scroll(0, 0);
|
||||
document.getElementById("output-text").scroll(0, 0);
|
||||
@@ -1046,15 +1046,25 @@ class OutputWaiter {
|
||||
* @param {Object[]} options
|
||||
*/
|
||||
backgroundMagicResult(options) {
|
||||
if (!options.length ||
|
||||
!options[0].recipe.length)
|
||||
return;
|
||||
if (!options.length) return;
|
||||
|
||||
const currentRecipeConfig = this.app.getRecipeConfig();
|
||||
const newRecipeConfig = currentRecipeConfig.concat(options[0].recipe);
|
||||
const opSequence = options[0].recipe.map(o => o.op).join(", ");
|
||||
let msg = "",
|
||||
newRecipeConfig;
|
||||
|
||||
this.showMagicButton(opSequence, options[0].data, newRecipeConfig);
|
||||
if (options[0].recipe.length) {
|
||||
const opSequence = options[0].recipe.map(o => o.op).join(", ");
|
||||
newRecipeConfig = currentRecipeConfig.concat(options[0].recipe);
|
||||
msg = `<i>${opSequence}</i> will produce <span class="data-text">"${Utils.escapeHtml(Utils.truncate(options[0].data), 30)}"</span>`;
|
||||
} else if (options[0].fileType && options[0].fileType.name) {
|
||||
const ft = options[0].fileType;
|
||||
newRecipeConfig = currentRecipeConfig.concat([{op: "Detect File Type", args: []}]);
|
||||
msg = `<i>${ft.name}</i> file detected`;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
this.showMagicButton(msg, newRecipeConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1072,15 +1082,14 @@ class OutputWaiter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the Magic button with a title and adds a link to a complete recipe.
|
||||
* Displays the Magic button with a title and adds a link to a recipe.
|
||||
*
|
||||
* @param {string} opSequence
|
||||
* @param {string} result
|
||||
* @param {string} msg
|
||||
* @param {Object[]} recipeConfig
|
||||
*/
|
||||
showMagicButton(opSequence, result, recipeConfig) {
|
||||
showMagicButton(msg, recipeConfig) {
|
||||
const magicButton = document.getElementById("magic");
|
||||
magicButton.setAttribute("data-original-title", `<i>${opSequence}</i> will produce <span class="data-text">"${Utils.escapeHtml(Utils.truncate(result), 30)}"</span>`);
|
||||
magicButton.setAttribute("data-original-title", msg);
|
||||
magicButton.setAttribute("data-recipe", JSON.stringify(recipeConfig), null, "");
|
||||
magicButton.classList.remove("hidden");
|
||||
magicButton.classList.add("pulse");
|
||||
|
||||
@@ -4,17 +4,13 @@
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import clippy from "clippyjs";
|
||||
import "../static/clippy_assets/agents/Clippy/agent.js";
|
||||
import clippyMap from "../static/clippy_assets/agents/Clippy/map.png";
|
||||
|
||||
/**
|
||||
* Waiter to handle seasonal events and easter eggs.
|
||||
*/
|
||||
class SeasonalWaiter {
|
||||
|
||||
/**
|
||||
* SeasonalWaiter contructor.
|
||||
* SeasonalWaiter constructor.
|
||||
*
|
||||
* @param {App} app - The main view object for CyberChef.
|
||||
* @param {Manager} manager - The CyberChef event manager.
|
||||
@@ -22,8 +18,6 @@ class SeasonalWaiter {
|
||||
constructor(app, manager) {
|
||||
this.app = app;
|
||||
this.manager = manager;
|
||||
|
||||
this.clippyAgent = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,14 +28,6 @@ class SeasonalWaiter {
|
||||
// Konami code
|
||||
this.kkeys = [];
|
||||
window.addEventListener("keydown", this.konamiCodeListener.bind(this));
|
||||
|
||||
// Clippy
|
||||
const now = new Date();
|
||||
if (now.getMonth() === 3 && now.getDate() === 1) {
|
||||
this.addClippyOption();
|
||||
this.manager.addDynamicListener(".option-item #clippy", "change", this.setupClippy, this);
|
||||
this.setupClippy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,285 +51,6 @@ class SeasonalWaiter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an option in the Options menu for turning Clippy on or off
|
||||
*/
|
||||
addClippyOption() {
|
||||
const optionsBody = document.getElementById("options-body"),
|
||||
optionItem = document.createElement("span");
|
||||
|
||||
optionItem.className = "bmd-form-group is-filled";
|
||||
optionItem.innerHTML = `<div class="checkbox option-item">
|
||||
<label for="clippy">
|
||||
<input type="checkbox" option="clippy" id="clippy" checked="">
|
||||
Use the Clippy helper
|
||||
</label>
|
||||
</div>`;
|
||||
optionsBody.appendChild(optionItem);
|
||||
|
||||
if (!("clippy" in this.app.options)) {
|
||||
this.app.options.clippy = true;
|
||||
}
|
||||
|
||||
this.manager.options.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up Clippy for April Fools Day
|
||||
*/
|
||||
setupClippy() {
|
||||
// Destroy any previous agents
|
||||
if (this.clippyAgent) {
|
||||
this.clippyAgent.closeBalloonImmediately();
|
||||
this.clippyAgent.hide();
|
||||
}
|
||||
|
||||
if (!this.app.options.clippy) {
|
||||
if (this.clippyTimeouts) this.clippyTimeouts.forEach(t => clearTimeout(t));
|
||||
return;
|
||||
}
|
||||
|
||||
// Set base path to # to prevent external network requests
|
||||
const clippyAssets = "#";
|
||||
// Shim the library to prevent external network requests
|
||||
shimClippy(clippy);
|
||||
|
||||
const self = this;
|
||||
clippy.load("Clippy", (agent) => {
|
||||
shimClippyAgent(agent);
|
||||
self.clippyAgent = agent;
|
||||
agent.show();
|
||||
agent.speak("Hello, I'm Clippy, your personal cyber assistant!");
|
||||
}, undefined, clippyAssets);
|
||||
|
||||
// Watch for the Auto Magic button appearing
|
||||
const magic = document.getElementById("magic");
|
||||
const observer = new MutationObserver((mutationsList, observer) => {
|
||||
// Read in message and recipe
|
||||
let msg, recipe;
|
||||
for (const mutation of mutationsList) {
|
||||
if (mutation.attributeName === "data-original-title") {
|
||||
msg = magic.getAttribute("data-original-title");
|
||||
}
|
||||
if (mutation.attributeName === "data-recipe") {
|
||||
recipe = magic.getAttribute("data-recipe");
|
||||
}
|
||||
}
|
||||
|
||||
// Close balloon if it is currently showing a magic hint
|
||||
const balloon = self.clippyAgent._balloon._balloon;
|
||||
if (balloon.is(":visible") && balloon.text().indexOf("That looks like encoded data") >= 0) {
|
||||
self.clippyAgent._balloon.hide(true);
|
||||
this.clippyAgent._balloon._hidden = true;
|
||||
}
|
||||
|
||||
// If a recipe was found, get Clippy to tell the user
|
||||
if (recipe) {
|
||||
recipe = this.manager.controls.generateStateUrl(true, true, JSON.parse(recipe));
|
||||
msg = `That looks like encoded data!<br><br>${msg}<br><br>Click <a class="clippyMagicRecipe" href="${recipe}">here</a> to load this recipe.`;
|
||||
|
||||
// Stop current balloon activity immediately and trigger speak again
|
||||
this.clippyAgent.closeBalloonImmediately();
|
||||
self.clippyAgent.speak(msg, true);
|
||||
// self.clippyAgent._queue.next();
|
||||
}
|
||||
});
|
||||
observer.observe(document.getElementById("magic"), {attributes: true});
|
||||
|
||||
// Play animations for various things
|
||||
this.manager.addListeners("#search", "click", () => {
|
||||
this.clippyAgent.play("Searching");
|
||||
}, this);
|
||||
this.manager.addListeners("#save,#save-to-file", "click", () => {
|
||||
this.clippyAgent.play("Save");
|
||||
}, this);
|
||||
this.manager.addListeners("#clr-recipe,#clr-io", "click", () => {
|
||||
this.clippyAgent.play("EmptyTrash");
|
||||
}, this);
|
||||
this.manager.addListeners("#bake", "click", e => {
|
||||
if (e.target.closest("button").textContent.toLowerCase().indexOf("bake") >= 0) {
|
||||
this.clippyAgent.play("Thinking");
|
||||
} else {
|
||||
this.clippyAgent.play("EmptyTrash");
|
||||
}
|
||||
this.clippyAgent._queue.clear();
|
||||
}, this);
|
||||
this.manager.addListeners("#input-text", "keydown", () => {
|
||||
this.clippyAgent.play("Writing");
|
||||
this.clippyAgent._queue.clear();
|
||||
}, this);
|
||||
this.manager.addDynamicListener("a.clippyMagicRecipe", "click", (e) => {
|
||||
this.clippyAgent.play("Congratulate");
|
||||
}, this);
|
||||
|
||||
this.clippyTimeouts = [];
|
||||
// Show challenge after timeout
|
||||
this.clippyTimeouts.push(setTimeout(() => {
|
||||
const hex = "1f 8b 08 00 ae a1 9b 5c 00 ff 05 40 a1 12 00 10 0c fd 26 61 5b 76 aa 9d 26 a8 02 02 37 84 f7 fb bb c5 a4 5f 22 c6 09 e5 6e c5 4c 2d 3f e9 30 a6 ea 41 a2 f2 ac 1c 00 00 00";
|
||||
self.clippyAgent.speak(`How about a fun challenge?<br><br>Try decoding this (click to load):<br><a href="#recipe=[]&input=${encodeURIComponent(btoa(hex))}">${hex}</a>`, true);
|
||||
self.clippyAgent.play("GetAttention");
|
||||
}, 1 * 60 * 1000));
|
||||
|
||||
this.clippyTimeouts.push(setTimeout(() => {
|
||||
self.clippyAgent.speak("<i>Did you know?</i><br><br>You can load files into CyberChef up to around 500MB using drag and drop or the load file button.", 15000);
|
||||
self.clippyAgent.play("Wave");
|
||||
}, 2 * 60 * 1000));
|
||||
|
||||
this.clippyTimeouts.push(setTimeout(() => {
|
||||
self.clippyAgent.speak("<i>Did you know?</i><br><br>You can use the 'Fork' operation to split up your input and run the recipe over each branch separately.<br><br><a class='clippyMagicRecipe' href=\"#recipe=Fork('%5C%5Cn','%5C%5Cn',false)From_UNIX_Timestamp('Seconds%20(s)')&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA\">Here's an example</a>.", 15000);
|
||||
self.clippyAgent.play("Print");
|
||||
}, 3 * 60 * 1000));
|
||||
|
||||
this.clippyTimeouts.push(setTimeout(() => {
|
||||
self.clippyAgent.speak("<i>Did you know?</i><br><br>The 'Magic' operation uses a number of methods to detect encoded data and the operations which can be used to make sense of it. A technical description of these methods can be found <a href=\"https://github.com/gchq/CyberChef/wiki/Automatic-detection-of-encoded-data-using-CyberChef-Magic\">here</a>.", 15000);
|
||||
self.clippyAgent.play("Alert");
|
||||
}, 4 * 60 * 1000));
|
||||
|
||||
this.clippyTimeouts.push(setTimeout(() => {
|
||||
self.clippyAgent.speak("<i>Did you know?</i><br><br>You can use parts of the input as arguments to operations.<br><br><a class='clippyMagicRecipe' href=\"#recipe=Register('key%3D(%5B%5C%5Cda-f%5D*)',true,false)Find_/_Replace(%7B'option':'Regex','string':'.*data%3D(.*)'%7D,'$1',true,false,true)RC4(%7B'option':'Hex','string':'$R0'%7D,'Hex','Latin1')&input=aHR0cDovL21hbHdhcmV6LmJpei9iZWFjb24ucGhwP2tleT0wZTkzMmE1YyZkYXRhPThkYjdkNWViZTM4NjYzYTU0ZWNiYjMzNGUzZGIxMQ\">Click here for an example</a>.", 15000);
|
||||
self.clippyAgent.play("CheckingSomething");
|
||||
}, 5 * 60 * 1000));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shims various ClippyJS functions to modify behaviour.
|
||||
*
|
||||
* @param {Clippy} clippy - The Clippy library
|
||||
*/
|
||||
function shimClippy(clippy) {
|
||||
// Shim _loadSounds so that it doesn't actually try to load any sounds
|
||||
clippy.load._loadSounds = function _loadSounds (name, path) {
|
||||
let dfd = clippy.load._sounds[name];
|
||||
if (dfd) return dfd;
|
||||
|
||||
// set dfd if not defined
|
||||
dfd = clippy.load._sounds[name] = $.Deferred();
|
||||
|
||||
// Resolve immediately without loading
|
||||
dfd.resolve({});
|
||||
|
||||
return dfd.promise();
|
||||
};
|
||||
|
||||
// Shim _loadMap so that it uses the local copy
|
||||
clippy.load._loadMap = function _loadMap (path) {
|
||||
let dfd = clippy.load._maps[path];
|
||||
if (dfd) return dfd;
|
||||
|
||||
// set dfd if not defined
|
||||
dfd = clippy.load._maps[path] = $.Deferred();
|
||||
|
||||
const src = clippyMap;
|
||||
const img = new Image();
|
||||
|
||||
img.onload = dfd.resolve;
|
||||
img.onerror = dfd.reject;
|
||||
|
||||
// start loading the map;
|
||||
img.setAttribute("src", src);
|
||||
|
||||
return dfd.promise();
|
||||
};
|
||||
|
||||
// Make sure we don't request the remote map
|
||||
clippy.Animator.prototype._setupElement = function _setupElement (el) {
|
||||
const frameSize = this._data.framesize;
|
||||
el.css("display", "none");
|
||||
el.css({ width: frameSize[0], height: frameSize[1] });
|
||||
el.css("background", "url('" + clippyMap + "') no-repeat");
|
||||
|
||||
return el;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shims various ClippyJS Agent functions to modify behaviour.
|
||||
*
|
||||
* @param {Agent} agent - The Clippy Agent
|
||||
*/
|
||||
function shimClippyAgent(agent) {
|
||||
// Turn off all sounds
|
||||
agent._animator._playSound = () => {};
|
||||
|
||||
// Improve speak function to support HTML markup
|
||||
const self = agent._balloon;
|
||||
agent._balloon.speak = (complete, text, hold) => {
|
||||
self._hidden = false;
|
||||
self.show();
|
||||
const c = self._content;
|
||||
// set height to auto
|
||||
c.height("auto");
|
||||
c.width("auto");
|
||||
// add the text
|
||||
c.html(text);
|
||||
// set height
|
||||
c.height(c.height());
|
||||
c.width(c.width());
|
||||
c.text("");
|
||||
self.reposition();
|
||||
|
||||
self._complete = complete;
|
||||
self._sayWords(text, hold, complete);
|
||||
if (hold) agent._queue.next();
|
||||
};
|
||||
|
||||
// Improve the _sayWords function to allow HTML and support timeouts
|
||||
agent._balloon.WORD_SPEAK_TIME = 60;
|
||||
agent._balloon._sayWords = (text, hold, complete) => {
|
||||
self._active = true;
|
||||
self._hold = hold;
|
||||
const words = text.split(/[^\S-]/);
|
||||
const time = self.WORD_SPEAK_TIME;
|
||||
const el = self._content;
|
||||
let idx = 1;
|
||||
clearTimeout(self.holdTimeout);
|
||||
|
||||
self._addWord = $.proxy(function () {
|
||||
if (!self._active) return;
|
||||
if (idx > words.length) {
|
||||
delete self._addWord;
|
||||
self._active = false;
|
||||
if (!self._hold) {
|
||||
complete();
|
||||
self.hide();
|
||||
} else if (typeof hold === "number") {
|
||||
self.holdTimeout = setTimeout(() => {
|
||||
self._hold = false;
|
||||
complete();
|
||||
self.hide();
|
||||
}, hold);
|
||||
}
|
||||
} else {
|
||||
el.html(words.slice(0, idx).join(" "));
|
||||
idx++;
|
||||
self._loop = window.setTimeout($.proxy(self._addWord, self), time);
|
||||
}
|
||||
}, self);
|
||||
|
||||
self._addWord();
|
||||
};
|
||||
|
||||
// Add break-word to balloon CSS
|
||||
agent._balloon._balloon.css("word-break", "break-word");
|
||||
|
||||
// Close the balloon on click (unless it was a link)
|
||||
agent._balloon._balloon.click(e => {
|
||||
if (e.target.nodeName !== "A") {
|
||||
agent._balloon.hide(true);
|
||||
agent._balloon._hidden = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Add function to immediately close the balloon even if it is currently doing something
|
||||
agent.closeBalloonImmediately = () => {
|
||||
agent._queue.clear();
|
||||
agent._balloon.hide(true);
|
||||
agent._balloon._hidden = true;
|
||||
agent._queue.next();
|
||||
};
|
||||
}
|
||||
|
||||
export default SeasonalWaiter;
|
||||
@@ -4,6 +4,8 @@
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import { debounce } from "../../core/Utils.mjs";
|
||||
|
||||
/**
|
||||
* Waiter to handle events related to the window object.
|
||||
*/
|
||||
@@ -25,13 +27,13 @@ class WindowWaiter {
|
||||
* continuous resetting).
|
||||
*/
|
||||
windowResize() {
|
||||
this.app.debounce(this.app.resetLayout, 200, "windowResize", this.app, [])();
|
||||
debounce(this.app.resetLayout, 200, "windowResize", this.app, [])();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler for window blur events.
|
||||
* Saves the current time so that we can calculate how long the window was unfocussed for when
|
||||
* Saves the current time so that we can calculate how long the window was unfocused for when
|
||||
* focus is returned.
|
||||
*/
|
||||
windowBlur() {
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import ChefWorker from "worker-loader?inline&fallback=false!../../core/ChefWorker";
|
||||
import DishWorker from "worker-loader?inline&fallback=false!../workers/DishWorker";
|
||||
import ChefWorker from "worker-loader?inline&fallback=false!../../core/ChefWorker.js";
|
||||
import DishWorker from "worker-loader?inline&fallback=false!../workers/DishWorker.mjs";
|
||||
import { debounce } from "../../core/Utils.mjs";
|
||||
|
||||
/**
|
||||
* Waiter to handle conversations with the ChefWorker
|
||||
@@ -281,7 +282,7 @@ class WorkerWaiter {
|
||||
*/
|
||||
setBakingStatus(bakingStatus) {
|
||||
this.app.baking = bakingStatus;
|
||||
this.app.debounce(this.manager.controls.toggleBakeButtonFunction, 20, "toggleBakeButton", this, [bakingStatus ? "cancel" : "bake"])();
|
||||
debounce(this.manager.controls.toggleBakeButtonFunction, 20, "toggleBakeButton", this, [bakingStatus ? "cancel" : "bake"])();
|
||||
|
||||
if (bakingStatus) this.manager.output.hideMagicButton();
|
||||
}
|
||||
@@ -352,7 +353,7 @@ class WorkerWaiter {
|
||||
* @param {object} workerObj - Object containing the worker information
|
||||
* @param {ChefWorker} workerObj.worker - The actual worker object
|
||||
* @param {number} workerObj.inputNum - The inputNum of the input being baked by the worker
|
||||
* @param {boolean} workerObj.active - If true, the worker is currrently baking an input
|
||||
* @param {boolean} workerObj.active - If true, the worker is currently baking an input
|
||||
*/
|
||||
workerFinished(workerObj) {
|
||||
const workerIdx = this.chefWorkers.indexOf(workerObj);
|
||||
|
||||
@@ -945,7 +945,7 @@ self.updateMaxTabs = function(maxTabs, activeTab) {
|
||||
* @param {boolean} searchData.showLoading - If true, include loading inputs in the results
|
||||
* @param {boolean} searchData.showLoaded - If true, include loaded inputs in the results
|
||||
* @param {string} searchData.filter - A regular expression to match the inputs on
|
||||
* @param {string} searchData.filterType - Either "CONTENT" or "FILENAME". Detemines what should be matched with filter
|
||||
* @param {string} searchData.filterType - Either "CONTENT" or "FILENAME". Determines what should be matched with filter
|
||||
* @param {number} searchData.numResults - The maximum number of results to be returned
|
||||
*/
|
||||
self.filterTabs = function(searchData) {
|
||||
|
||||
@@ -75,7 +75,7 @@ module.exports = {
|
||||
// Confirm that it has been added to the recipe
|
||||
browser
|
||||
.useCss()
|
||||
.waitForElementVisible(op)
|
||||
.waitForElementVisible(op, 100)
|
||||
.expect.element(op).text.to.contain("To Hex");
|
||||
|
||||
// Enter input
|
||||
@@ -107,6 +107,10 @@ module.exports = {
|
||||
loadOp("BSON deserialise", browser)
|
||||
.waitForElementNotVisible("#output-loader", 5000);
|
||||
|
||||
// Charts
|
||||
loadOp("Entropy", browser)
|
||||
.waitForElementNotVisible("#output-loader", 5000);
|
||||
|
||||
// Ciphers
|
||||
loadOp("AES Encrypt", browser)
|
||||
.waitForElementNotVisible("#output-loader", 5000);
|
||||
@@ -135,6 +139,10 @@ module.exports = {
|
||||
loadOp("Encode text", browser)
|
||||
.waitForElementNotVisible("#output-loader", 5000);
|
||||
|
||||
// Hashing
|
||||
loadOp("Streebog", browser)
|
||||
.waitForElementNotVisible("#output-loader", 5000);
|
||||
|
||||
// Image
|
||||
loadOp("Extract EXIF", browser)
|
||||
.waitForElementNotVisible("#output-loader", 5000);
|
||||
@@ -162,6 +170,54 @@ module.exports = {
|
||||
// UserAgent
|
||||
loadOp("Parse User Agent", browser)
|
||||
.waitForElementNotVisible("#output-loader", 5000);
|
||||
|
||||
// YARA
|
||||
loadOp("YARA Rules", browser)
|
||||
.waitForElementNotVisible("#output-loader", 5000);
|
||||
|
||||
browser.click("#clr-recipe");
|
||||
},
|
||||
|
||||
"Move around the UI": browser => {
|
||||
const otherCat = "//a[contains(@class, 'category-title') and contains(@data-target, '#catOther')]",
|
||||
genUUID = "//li[contains(@class, 'operation') and text()='Generate UUID']";
|
||||
|
||||
browser.useXpath();
|
||||
|
||||
// Scroll to a lower category
|
||||
browser
|
||||
.getLocationInView(otherCat)
|
||||
.expect.element(otherCat).to.be.visible;
|
||||
|
||||
// Open category
|
||||
browser
|
||||
.click(otherCat)
|
||||
.expect.element(genUUID).to.be.visible;
|
||||
|
||||
// Add op to recipe
|
||||
/* mouseButtonUp drops wherever the actual cursor is, not necessarily in the right place,
|
||||
so we can't test Sortable.js properly using Nightwatch. html-dnd doesn't work either.
|
||||
Instead of relying on drag and drop, we double click on the op to load it. */
|
||||
browser
|
||||
.getLocationInView(genUUID)
|
||||
.moveToElement(genUUID, 10, 10)
|
||||
.doubleClick()
|
||||
.useCss()
|
||||
.waitForElementVisible(".operation .op-title", 1000)
|
||||
.waitForElementNotVisible("#stale-indicator", 1000)
|
||||
.expect.element("#output-text").to.have.value.which.matches(/[\da-f-]{36}/);
|
||||
|
||||
browser.click("#clr-recipe");
|
||||
},
|
||||
|
||||
"Search": browser => {
|
||||
// Search for an op
|
||||
browser
|
||||
.useCss()
|
||||
.clearValue("#search")
|
||||
.setValue("#search", "md5")
|
||||
.useXpath()
|
||||
.waitForElementVisible("//ul[@id='search-results']//u[text()='MD5']", 1000);
|
||||
},
|
||||
|
||||
after: browser => {
|
||||
|
||||
@@ -104,8 +104,6 @@ class TestRegister {
|
||||
* Run all api related tests and wrap results in report format
|
||||
*/
|
||||
runApiTests() {
|
||||
console.log("Running tests...");
|
||||
|
||||
return Promise.all(this.apiTests.map(async function(test, i) {
|
||||
const result = {
|
||||
test: test,
|
||||
|
||||
Loaded 100 of 113 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user