mirror of
https://github.com/gchq/CyberChef
synced 2025-12-05 23:53:27 +00:00
Compare commits
164 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5314a456cb | ||
|
|
ba2a5b195c | ||
|
|
06708949a1 | ||
|
|
da901e20d9 | ||
|
|
6810f38808 | ||
|
|
087cc6b8fd | ||
|
|
10c8101476 | ||
|
|
22028b074a | ||
|
|
3d086beed2 | ||
|
|
777d814e70 | ||
|
|
15c26a95c5 | ||
|
|
f87d3bd1cb | ||
|
|
b9ea1e8c71 | ||
|
|
db232f4ff2 | ||
|
|
70f705afbc | ||
|
|
880df212d5 | ||
|
|
b06acd99ec | ||
|
|
a59de80d18 | ||
|
|
1e8dee9935 | ||
|
|
928f1c3e4b | ||
|
|
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 | ||
|
|
4f2749e0dc | ||
|
|
c9deaae744 | ||
|
|
950a12360e | ||
|
|
686ca284fe | ||
|
|
b21643f485 | ||
|
|
4c28627459 | ||
|
|
6a6fe0f56d | ||
|
|
48831225ac | ||
|
|
4e8a79d8f1 | ||
|
|
570a4c7fca | ||
|
|
094d352e5f | ||
|
|
44b90be7d6 | ||
|
|
e7980a8886 | ||
|
|
1c9c0a48be | ||
|
|
c541eebe3e | ||
|
|
c51e6efe74 | ||
|
|
6c9ce15b26 | ||
|
|
8e1bd36b4c | ||
|
|
d3e3e6e6fc | ||
|
|
f1794a2dfe | ||
|
|
1efccff730 | ||
|
|
0031345383 | ||
|
|
46fa7475cf | ||
|
|
afc7c40975 | ||
|
|
dc99797f7b | ||
|
|
4624266a5c | ||
|
|
05bfd99318 | ||
|
|
db3faf16b0 | ||
|
|
9fc451ece8 | ||
|
|
9e1079027b | ||
|
|
9774a4bd26 | ||
|
|
ce9e864757 | ||
|
|
737ea19c9e | ||
|
|
82b5e97a2b | ||
|
|
c43f829854 | ||
|
|
f43a868607 | ||
|
|
9f2d1453ed | ||
|
|
082d939f7d | ||
|
|
8d628cf0ed | ||
|
|
19553dcfed | ||
|
|
a7938526aa | ||
|
|
863551ee1d | ||
|
|
772c6bbba5 | ||
|
|
148dcbb0c5 | ||
|
|
82abdb50b1 | ||
|
|
b8dbb11136 | ||
|
|
b14cb99587 | ||
|
|
1d32a5939c | ||
|
|
ae1cd8ba3e | ||
|
|
1fb6bffe1c | ||
|
|
59864e3781 | ||
|
|
e2c7d8c678 | ||
|
|
62f82c5d12 | ||
|
|
ec70d8a3a2 | ||
|
|
1b4471a946 | ||
|
|
43472394c7 | ||
|
|
a4e9025b8e | ||
|
|
6b9e93e310 | ||
|
|
06b385563c | ||
|
|
d90a23bfd5 | ||
|
|
91cdd50ba7 | ||
|
|
4bc4db8232 | ||
|
|
863675e636 | ||
|
|
1cdcaebb4d | ||
|
|
cdb30d86b0 | ||
|
|
138e3c4239 | ||
|
|
66b82598e3 | ||
|
|
780eecf35b | ||
|
|
8548d39318 | ||
|
|
6f26ff0a89 | ||
|
|
2ffd2c6b7a | ||
|
|
0e95ad8ed6 | ||
|
|
3a8b362dfd | ||
|
|
82b94fad5d | ||
|
|
42cfed5fa8 | ||
|
|
e4452b906e | ||
|
|
36abaeb6fb | ||
|
|
e4d98eba6b | ||
|
|
9c6ceaa58a | ||
|
|
7f168d49a6 | ||
|
|
ac1c93d29b | ||
|
|
944842d4eb | ||
|
|
d56ff0825a | ||
|
|
bac2e8c014 | ||
|
|
59cdd259ac | ||
|
|
219469f24f | ||
|
|
ad571e6019 | ||
|
|
14d924f6c7 | ||
|
|
282f02f4d5 | ||
|
|
d36cede0c7 | ||
|
|
a262d70b88 | ||
|
|
77b098c5fe | ||
|
|
b491b9d77d | ||
|
|
237f792fb4 |
@@ -43,6 +43,7 @@
|
||||
|
||||
// stylistic conventions
|
||||
"brace-style": ["error", "1tbs"],
|
||||
"space-before-blocks": ["error", "always"],
|
||||
"block-spacing": "error",
|
||||
"array-bracket-spacing": "error",
|
||||
"comma-spacing": "error",
|
||||
|
||||
8
.github/CONTRIBUTING.md
vendored
8
.github/CONTRIBUTING.md
vendored
@@ -6,7 +6,7 @@ There are lots of opportunities to contribute to CyberChef. If you want ideas, t
|
||||
|
||||
Before your contributions can be accepted, you must:
|
||||
|
||||
- Sign the [GCHQ Contributor Licence Agreement](https://github.com/gchq/Gaffer/wiki/GCHQ-OSS-Contributor-License-Agreement-V1.0)
|
||||
- Sign the [GCHQ Contributor Licence Agreement](https://cla-assistant.io/gchq/CyberChef)
|
||||
- Push your changes to your fork.
|
||||
- Submit a pull request.
|
||||
|
||||
@@ -25,9 +25,9 @@ Before your contributions can be accepted, you must:
|
||||
## Design Principles
|
||||
|
||||
1. If at all possible, all operations and features should be client-side and not rely on connections to an external server. This increases the utility of CyberChef on closed networks and in virtual machines that are not connected to the Internet. Calls to external APIs may be accepted if there is no other option, but not for critical components.
|
||||
2. Latency should be kept to a minimum to enhance the user experience. This means that all operation code should sit on the client, rather than being loaded dynamically from a server.
|
||||
3. Use Vanilla JS if at all possible to reduce the number of libraries required and relied upon. Frameworks like jQuery, although included, should not be used unless absolutely necessary.
|
||||
4. Minimise the use of large libraries, especially for niche operations that won't be used very often - these will be downloaded by everyone using the app, whether they use that operation or not (due to principal 2).
|
||||
2. Latency should be kept to a minimum to enhance the user experience. This means that operation code should sit on the client and be executed there. However, as a trade-off between latency and bandwidth, operation code with large dependencies can be loaded in discrete modules in order to reduce the size of the initial download. The downloading of additional modules must remain entirely transparent so that the user is not inconvenienced.
|
||||
3. Large libraries should be kept in separate modules so that they are not downloaded by everyone who uses the app, just those who specifically require the relevant operations.
|
||||
4. Use Vanilla JS if at all possible to reduce the number of libraries required and relied upon. Frameworks like jQuery, although included, should not be used unless absolutely necessary.
|
||||
|
||||
|
||||
With these principles in mind, any changes or additions to CyberChef should keep it:
|
||||
|
||||
33
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
33
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: 'Bug report: <Insert title here>'
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
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 behaviour**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
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]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -32,4 +32,3 @@ If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/feature_request.md
vendored
4
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -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.
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/operation-request.md
vendored
2
.github/ISSUE_TEMPLATE/operation-request.md
vendored
@@ -7,8 +7,6 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Prefix the title above with 'Operation request:' -->
|
||||
|
||||
## Summary
|
||||
|
||||
### Example Input
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,9 +2,6 @@ node_modules
|
||||
npm-debug.log
|
||||
travis.log
|
||||
build
|
||||
docs/*
|
||||
!docs/*.conf.json
|
||||
!docs/*.ico
|
||||
.vscode
|
||||
.*.swp
|
||||
.DS_Store
|
||||
|
||||
@@ -3,6 +3,5 @@ npm-debug.log
|
||||
travis.log
|
||||
build/*
|
||||
!build/node
|
||||
docs
|
||||
.vscode
|
||||
.github
|
||||
|
||||
@@ -11,8 +11,7 @@ before_script:
|
||||
script:
|
||||
- grunt lint
|
||||
- grunt test
|
||||
- grunt docs
|
||||
- npm run node-prod
|
||||
- grunt testnodeconsumer
|
||||
- grunt prod --msg="$COMPILE_MSG"
|
||||
- xvfb-run --server-args="-screen 0 1200x800x24" grunt testui
|
||||
before_deploy:
|
||||
@@ -34,7 +33,7 @@ deploy:
|
||||
file_glob: true
|
||||
file:
|
||||
- build/prod/*.zip
|
||||
- build/node/CyberChef.js
|
||||
- src/node/cjs.js
|
||||
on:
|
||||
repo: gchq/CyberChef
|
||||
tags: true
|
||||
|
||||
43
CHANGELOG.md
43
CHANGELOG.md
@@ -2,12 +2,37 @@
|
||||
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]
|
||||
|
||||
### [9.1.0] - 2019-08-22
|
||||
- 'Parse SSH Host Key' operation added [@j433866] | [#595]
|
||||
- 'Defang IP Addresses' operation added [@h345983745] | [#556]
|
||||
|
||||
## [9.0.0] - 2019-07-09
|
||||
- [Multiple inputs](https://github.com/gchq/CyberChef/wiki/Multiple-Inputs) are now supported in the main web UI, allowing you to upload and process multiple files at once [@j433866] | [#566]
|
||||
- A [Node.js API](https://github.com/gchq/CyberChef/wiki/Node-API) has been implemented, meaning that CyberChef can now be used as a library, either to provide specific operations, or an entire baking environment [@d98762625] | [#291]
|
||||
- A [read-eval-print loop (REPL)](https://github.com/gchq/CyberChef/wiki/Node-API#repl) is also included to enable prototyping and experimentation with the API [@d98762625] | [#291]
|
||||
- Light and dark Solarized themes added [@j433866] | [#566]
|
||||
|
||||
<details>
|
||||
<summary>Click to expand v8 minor versions</summary>
|
||||
|
||||
### [8.38.0] - 2019-07-03
|
||||
- 'Streebog' and 'GOST hash' operations added [@MShwed] [@n1474335] | [#530]
|
||||
|
||||
@@ -129,6 +154,8 @@ All major and minor version changes will be documented in this file. Details of
|
||||
### [8.1.0] - 2018-08-19
|
||||
- 'Dechunk HTTP response' operation added [@sevzero] | [#311]
|
||||
|
||||
</details>
|
||||
|
||||
## [8.0.0] - 2018-08-05
|
||||
- Codebase rewritten using [ES modules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/) and [classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) [@n1474335] [@d98762625] [@artemisbot] [@picapi] | [#284]
|
||||
- Operation architecture restructured to make adding new operations a lot simpler [@n1474335] | [#284]
|
||||
@@ -158,6 +185,13 @@ 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
|
||||
[8.38.0]: https://github.com/gchq/CyberChef/releases/tag/v8.38.0
|
||||
[8.37.0]: https://github.com/gchq/CyberChef/releases/tag/v8.37.0
|
||||
@@ -227,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
|
||||
@@ -265,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
|
||||
@@ -275,7 +312,13 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[#531]: https://github.com/gchq/CyberChef/pull/531
|
||||
[#533]: https://github.com/gchq/CyberChef/pull/533
|
||||
[#535]: https://github.com/gchq/CyberChef/pull/535
|
||||
[#556]: https://github.com/gchq/CyberChef/pull/556
|
||||
[#566]: https://github.com/gchq/CyberChef/pull/566
|
||||
[#571]: https://github.com/gchq/CyberChef/pull/571
|
||||
[#585]: https://github.com/gchq/CyberChef/pull/585
|
||||
[#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
|
||||
|
||||
133
Gruntfile.js
133
Gruntfile.js
@@ -3,7 +3,6 @@
|
||||
const webpack = require("webpack");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
|
||||
const NodeExternals = require("webpack-node-externals");
|
||||
const glob = require("glob");
|
||||
const path = require("path");
|
||||
|
||||
@@ -15,7 +14,6 @@ const path = require("path");
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
const NODE_PROD = process.env.NODE_ENV === "production";
|
||||
|
||||
module.exports = function (grunt) {
|
||||
grunt.file.defaultEncoding = "utf8";
|
||||
@@ -36,8 +34,7 @@ module.exports = function (grunt) {
|
||||
grunt.registerTask("node",
|
||||
"Compiles CyberChef into a single NodeJS module.",
|
||||
[
|
||||
"clean:node", "clean:config", "clean:nodeConfig", "exec:generateConfig",
|
||||
"exec:generateNodeIndex", "webpack:node", "webpack:nodeRepl", "chmod:build"
|
||||
"clean:node", "clean:config", "clean:nodeConfig", "exec:generateConfig", "exec:generateNodeIndex"
|
||||
]);
|
||||
|
||||
grunt.registerTask("test",
|
||||
@@ -51,17 +48,14 @@ module.exports = function (grunt) {
|
||||
"A task which runs all the UI tests in the tests directory. The prod task must already have been run.",
|
||||
["connect:prod", "exec:browserTests"]);
|
||||
|
||||
grunt.registerTask("docs",
|
||||
"Compiles documentation in the /docs directory.",
|
||||
["clean:docs", "jsdoc", "chmod:docs"]);
|
||||
|
||||
grunt.registerTask("testnodeconsumer",
|
||||
"A task which checks whether consuming CJS and ESM apps work with the CyberChef build",
|
||||
["exec:setupNodeConsumers", "exec:testCJSNodeConsumer", "exec:testESMNodeConsumer", "exec:testESMDeepImportNodeConsumer", "exec:teardownNodeConsumers"]);
|
||||
|
||||
grunt.registerTask("default",
|
||||
"Lints the code base",
|
||||
["eslint", "exec:repoSize"]);
|
||||
|
||||
|
||||
grunt.registerTask("doc", "docs");
|
||||
grunt.registerTask("tests", "test");
|
||||
grunt.registerTask("lint", "eslint");
|
||||
|
||||
@@ -69,7 +63,6 @@ module.exports = function (grunt) {
|
||||
// Load tasks provided by each plugin
|
||||
grunt.loadNpmTasks("grunt-eslint");
|
||||
grunt.loadNpmTasks("grunt-webpack");
|
||||
grunt.loadNpmTasks("grunt-jsdoc");
|
||||
grunt.loadNpmTasks("grunt-contrib-clean");
|
||||
grunt.loadNpmTasks("grunt-contrib-copy");
|
||||
grunt.loadNpmTasks("grunt-contrib-watch");
|
||||
@@ -90,7 +83,8 @@ module.exports = function (grunt) {
|
||||
COMPILE_MSG: JSON.stringify(grunt.option("compile-msg") || grunt.option("msg") || ""),
|
||||
PKG_VERSION: JSON.stringify(pkg.version),
|
||||
},
|
||||
moduleEntryPoints = listEntryModules();
|
||||
moduleEntryPoints = listEntryModules(),
|
||||
nodeConsumerTestPath = "~/tmp-cyberchef";
|
||||
|
||||
|
||||
/**
|
||||
@@ -115,7 +109,6 @@ module.exports = function (grunt) {
|
||||
node: ["build/node/*"],
|
||||
config: ["src/core/config/OperationConfig.json", "src/core/config/modules/*", "src/code/operations/index.mjs"],
|
||||
nodeConfig: ["src/node/index.mjs", "src/node/config/OperationConfig.json"],
|
||||
docs: ["docs/*", "!docs/*.conf.json", "!docs/*.ico", "!docs/*.png"],
|
||||
standalone: ["build/prod/CyberChef*.html"]
|
||||
},
|
||||
eslint: {
|
||||
@@ -128,22 +121,6 @@ module.exports = function (grunt) {
|
||||
node: ["src/node/**/*.{js,mjs}"],
|
||||
tests: ["tests/**/*.{js,mjs}"],
|
||||
},
|
||||
jsdoc: {
|
||||
options: {
|
||||
destination: "docs",
|
||||
template: "node_modules/ink-docstrap/template",
|
||||
recurse: true,
|
||||
readme: "./README.md",
|
||||
configure: "docs/jsdoc.conf.json"
|
||||
},
|
||||
all: {
|
||||
src: [
|
||||
"src/**/*.js",
|
||||
"src/**/*.mjs",
|
||||
"!src/core/vendor/**/*"
|
||||
],
|
||||
}
|
||||
},
|
||||
accessibility: {
|
||||
options: {
|
||||
accessibilityLevel: "WCAG2A",
|
||||
@@ -163,8 +140,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",
|
||||
@@ -201,46 +177,6 @@ module.exports = function (grunt) {
|
||||
]
|
||||
};
|
||||
},
|
||||
node: {
|
||||
mode: NODE_PROD ? "production" : "development",
|
||||
target: "node",
|
||||
entry: "./src/node/index.mjs",
|
||||
externals: [NodeExternals({
|
||||
whitelist: ["crypto-api/src/crypto-api"]
|
||||
})],
|
||||
output: {
|
||||
filename: "CyberChef.js",
|
||||
path: __dirname + "/build/node",
|
||||
library: "CyberChef",
|
||||
libraryTarget: "commonjs2"
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin(BUILD_CONSTANTS),
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1
|
||||
})
|
||||
],
|
||||
},
|
||||
nodeRepl: {
|
||||
mode: NODE_PROD ? "production" : "development",
|
||||
target: "node",
|
||||
entry: "./src/node/repl-index.mjs",
|
||||
externals: [NodeExternals({
|
||||
whitelist: ["crypto-api/src/crypto-api"]
|
||||
})],
|
||||
output: {
|
||||
filename: "CyberChef-repl.js",
|
||||
path: __dirname + "/build/node",
|
||||
library: "CyberChef",
|
||||
libraryTarget: "commonjs2"
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin(BUILD_CONSTANTS),
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1
|
||||
})
|
||||
],
|
||||
}
|
||||
},
|
||||
"webpack-dev-server": {
|
||||
options: {
|
||||
@@ -295,7 +231,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`
|
||||
}
|
||||
@@ -333,12 +268,7 @@ module.exports = function (grunt) {
|
||||
{
|
||||
src: "build/prod/index.html",
|
||||
dest: "build/prod/index.html"
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
src: "docs/**",
|
||||
dest: "build/prod/"
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
standalone: {
|
||||
@@ -370,12 +300,6 @@ module.exports = function (grunt) {
|
||||
mode: "755",
|
||||
},
|
||||
src: ["build/**/*", "build/"]
|
||||
},
|
||||
docs: {
|
||||
options: {
|
||||
mode: "755",
|
||||
},
|
||||
src: ["docs/**/*", "docs/"]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -402,7 +326,7 @@ module.exports = function (grunt) {
|
||||
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: [
|
||||
@@ -428,7 +352,44 @@ module.exports = function (grunt) {
|
||||
},
|
||||
nodeTests: {
|
||||
command: "node --experimental-modules --no-warnings --no-deprecation tests/node/index.mjs"
|
||||
}
|
||||
},
|
||||
setupNodeConsumers: {
|
||||
command: [
|
||||
"echo '\n--- Testing node conumers ---'",
|
||||
"npm link",
|
||||
`mkdir ${nodeConsumerTestPath}`,
|
||||
`cp tests/node/consumers/* ${nodeConsumerTestPath}`,
|
||||
`cd ${nodeConsumerTestPath}`,
|
||||
"npm link cyberchef"
|
||||
].join(";"),
|
||||
},
|
||||
teardownNodeConsumers: {
|
||||
command: [
|
||||
`rm -rf ${nodeConsumerTestPath}`,
|
||||
"echo '\n--- Node consumer tests complete ---'"
|
||||
].join(";"),
|
||||
},
|
||||
testCJSNodeConsumer: {
|
||||
command: [
|
||||
`cd ${nodeConsumerTestPath}`,
|
||||
"node --no-warnings cjs-consumer.js",
|
||||
].join(";"),
|
||||
stdout: false,
|
||||
},
|
||||
testESMNodeConsumer: {
|
||||
command: [
|
||||
`cd ${nodeConsumerTestPath}`,
|
||||
"node --no-warnings --experimental-modules esm-consumer.mjs",
|
||||
].join(";"),
|
||||
stdout: false,
|
||||
},
|
||||
testESMDeepImportNodeConsumer: {
|
||||
command: [
|
||||
`cd ${nodeConsumerTestPath}`,
|
||||
"node --no-warnings --experimental-modules esm-deep-import-consumer.mjs",
|
||||
].join(";"),
|
||||
stdout: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
11
README.md
11
README.md
@@ -3,7 +3,6 @@
|
||||
[](https://travis-ci.org/gchq/CyberChef)
|
||||
[](https://david-dm.org/gchq/CyberChef)
|
||||
[](https://www.npmjs.com/package/cyberchef)
|
||||

|
||||
[](https://github.com/gchq/CyberChef/blob/master/LICENSE)
|
||||
[](https://gitter.im/gchq/CyberChef?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
@@ -77,9 +76,13 @@ You can use as many operations as you like in simple or complex ways. Some examp
|
||||
|
||||
CyberChef is built to support
|
||||
|
||||
- Google Chrome 40+
|
||||
- Mozilla Firefox 35+
|
||||
- Microsoft Edge 14+
|
||||
- Google Chrome 50+
|
||||
- Mozilla Firefox 38+
|
||||
|
||||
|
||||
## Node.js support
|
||||
|
||||
CyberChef is built to fully support Node.js `v10` and partially supports `v12`. Named imports using a deep import specifier does not work in `v12`. For more information, see the Node API page in the project [wiki pages](https://github.com/gchq/CyberChef/wiki)
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
26
SECURITY.md
Normal file
26
SECURITY.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
CyberChef is supported on a best endeavours basis. Patches will be applied to
|
||||
the latest version rather than retroactively to older versions. To ensure you
|
||||
are using the most secure version of CyberChef, please make sure you have the
|
||||
[latest release](https://github.com/gchq/CyberChef/releases/latest). The
|
||||
official [live demo](https://gchq.github.io/CyberChef/) is always up to date.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
In most scenarios, the most appropriate way to report a vulnerability is to
|
||||
[raise a new issue](https://github.com/gchq/CyberChef/issues/new/choose)
|
||||
describing the problem in as much detail as possible, ideally with examples.
|
||||
This will obviously be public. If you feel that the vulnerability is
|
||||
significant enough to warrant a private disclosure, please email
|
||||
[oss@gchq.gov.uk](mailto:oss@gchq.gov.uk) and
|
||||
[n1474335@gmail.com](mailto:n1474335@gmail.com).
|
||||
|
||||
Disclosures of vulnerabilities in CyberChef are always welcomed. Whilst we aim
|
||||
to write clean and secure code free from bugs, we recognise that this is an open
|
||||
source project written by analysts in their spare time, relying on dozens of
|
||||
open source libraries that are modified and updated on a regular basis. We hope
|
||||
that the community will continue to support us as we endeavour to maintain and
|
||||
develop this tool together.
|
||||
BIN
docs/favicon.ico
BIN
docs/favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"tags": {
|
||||
"allowUnknownTags": true
|
||||
},
|
||||
"plugins": [
|
||||
"plugins/markdown",
|
||||
"node_modules/jsdoc-babel"
|
||||
],
|
||||
"templates": {
|
||||
"systemName": "CyberChef",
|
||||
"footer": "",
|
||||
"copyright": "© Crown Copyright 2017",
|
||||
"navType": "inline",
|
||||
"theme": "cerulean",
|
||||
"linenums": true,
|
||||
"collapseSymbols": false,
|
||||
"inverseNav": true,
|
||||
"outputSourceFiles": true,
|
||||
"outputSourcePath": true,
|
||||
"dateFormat": "ddd MMM Do YYYY",
|
||||
"sort": false,
|
||||
"logoFile": "cyberchef-32x32.png",
|
||||
"cleverLinks": false,
|
||||
"monospaceLinks": false,
|
||||
"protocol": "html://",
|
||||
"methodHeadingReturns": false
|
||||
},
|
||||
"markdown": {
|
||||
"parser": "gfm",
|
||||
"hardwrap": true
|
||||
}
|
||||
}
|
||||
2978
package-lock.json
generated
2978
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
88
package.json
88
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "9.0.2",
|
||||
"version": "9.7.8",
|
||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||
"author": "n1474335 <n1474335@gmail.com>",
|
||||
"homepage": "https://gchq.github.io/CyberChef",
|
||||
@@ -27,29 +27,29 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/gchq/CyberChef/"
|
||||
},
|
||||
"main": "build/node/CyberChef.js",
|
||||
"main": "src/node/cjs.js",
|
||||
"module": "src/node/index.mjs",
|
||||
"bugs": "https://github.com/gchq/CyberChef/issues",
|
||||
"browserslist": [
|
||||
"Chrome >= 40",
|
||||
"Firefox >= 35",
|
||||
"Edge >= 14",
|
||||
"node >= 6.5"
|
||||
"Chrome >= 50",
|
||||
"Firefox >= 38",
|
||||
"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",
|
||||
@@ -60,34 +60,31 @@
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-eslint": "^22.0.0",
|
||||
"grunt-exec": "~3.0.0",
|
||||
"grunt-jsdoc": "^2.4.0",
|
||||
"grunt-webpack": "^3.1.3",
|
||||
"grunt-zip": "^0.18.2",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"imports-loader": "^0.8.0",
|
||||
"ink-docstrap": "^1.3.2",
|
||||
"jsdoc-babel": "^0.5.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",
|
||||
@@ -99,21 +96,22 @@
|
||||
"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",
|
||||
@@ -123,16 +121,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.11",
|
||||
"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",
|
||||
@@ -145,6 +144,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",
|
||||
@@ -156,13 +156,11 @@
|
||||
"scripts": {
|
||||
"start": "grunt dev",
|
||||
"build": "grunt prod",
|
||||
"node": "NODE_ENV=development grunt node",
|
||||
"node-prod": "NODE_ENV=production grunt node",
|
||||
"repl": "grunt node && node build/node/CyberChef-repl.js",
|
||||
"repl": "node src/node/repl.js",
|
||||
"test": "grunt test",
|
||||
"test-node": "grunt test-node",
|
||||
"test-node-consumer": "grunt testnodeconsumer",
|
||||
"testui": "grunt testui",
|
||||
"docs": "grunt docs",
|
||||
"testuidev": "npx nightwatch --env=dev",
|
||||
"lint": "grunt lint",
|
||||
"newop": "node --experimental-modules src/core/config/scripts/newOperation.mjs"
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
@@ -235,6 +235,10 @@ class Dish {
|
||||
case Dish.JSON:
|
||||
title = "application/json";
|
||||
break;
|
||||
case Dish.NUMBER:
|
||||
case Dish.BIG_NUMBER:
|
||||
title = this.value.toString();
|
||||
break;
|
||||
case Dish.ARRAY_BUFFER:
|
||||
case Dish.BYTE_ARRAY:
|
||||
title = this.detectDishType();
|
||||
@@ -283,7 +287,21 @@ class Dish {
|
||||
case Dish.ARRAY_BUFFER:
|
||||
return this.value instanceof ArrayBuffer;
|
||||
case Dish.BIG_NUMBER:
|
||||
return BigNumber.isBigNumber(this.value);
|
||||
if (BigNumber.isBigNumber(this.value)) return true;
|
||||
/*
|
||||
If a BigNumber is passed between WebWorkers it is serialised as a JSON
|
||||
object with a coefficient (c), exponent (e) and sign (s). We detect this
|
||||
and reinitialise it as a BigNumber object.
|
||||
*/
|
||||
if (Object.keys(this.value).sort().equals(["c", "e", "s"])) {
|
||||
const temp = new BigNumber();
|
||||
temp.c = this.value.c;
|
||||
temp.e = this.value.e;
|
||||
temp.s = this.value.s;
|
||||
this.value = temp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case Dish.JSON:
|
||||
// All values can be serialised in some manner, so we return true in all cases
|
||||
return true;
|
||||
|
||||
@@ -1303,6 +1303,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",
|
||||
@@ -122,7 +124,8 @@
|
||||
"PGP Encrypt",
|
||||
"PGP Decrypt",
|
||||
"PGP Encrypt and Sign",
|
||||
"PGP Decrypt and Verify"
|
||||
"PGP Decrypt and Verify",
|
||||
"Parse SSH Host Key"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -166,6 +169,8 @@
|
||||
"Parse IP range",
|
||||
"Parse IPv6 address",
|
||||
"Parse IPv4 header",
|
||||
"Parse UDP",
|
||||
"Parse SSH Host Key",
|
||||
"Parse URI",
|
||||
"URL Encode",
|
||||
"URL Decode",
|
||||
@@ -177,7 +182,8 @@
|
||||
"Group IP addresses",
|
||||
"Encode NetBIOS Name",
|
||||
"Decode NetBIOS Name",
|
||||
"Defang URL"
|
||||
"Defang URL",
|
||||
"Defang IP Addresses"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -224,6 +230,7 @@
|
||||
"Convert speed",
|
||||
"Convert data units",
|
||||
"Convert co-ordinate format",
|
||||
"Show on map",
|
||||
"Parse UNIX file permissions",
|
||||
"Swap endianness",
|
||||
"Parse colour code",
|
||||
@@ -353,7 +360,8 @@
|
||||
"BSON serialise",
|
||||
"BSON deserialise",
|
||||
"To MessagePack",
|
||||
"From MessagePack"
|
||||
"From MessagePack",
|
||||
"Render Markdown"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -363,7 +371,11 @@
|
||||
"Scan for Embedded Files",
|
||||
"Extract Files",
|
||||
"Remove EXIF",
|
||||
"Extract EXIF"
|
||||
"Extract EXIF",
|
||||
"Extract RGBA",
|
||||
"View Bit Plane",
|
||||
"Randomize Colour Palette",
|
||||
"Extract LSB"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -371,6 +383,7 @@
|
||||
"ops": [
|
||||
"Render Image",
|
||||
"Play Media",
|
||||
"Optical Character Recognition",
|
||||
"Remove EXIF",
|
||||
"Extract EXIF",
|
||||
"Split Colour Channels",
|
||||
|
||||
@@ -109,7 +109,7 @@ export function mean(data) {
|
||||
*/
|
||||
export function median(data) {
|
||||
if ((data.length % 2) === 0 && data.length > 0) {
|
||||
data.sort(function(a, b){
|
||||
data.sort(function(a, b) {
|
||||
return a.minus(b);
|
||||
});
|
||||
const first = data[Math.floor(data.length / 2)];
|
||||
|
||||
66
src/core/lib/Bacon.mjs
Normal file
66
src/core/lib/Bacon.mjs
Normal file
@@ -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];
|
||||
});
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import Utils from "../Utils.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,6 +25,9 @@ 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);
|
||||
}
|
||||
@@ -63,7 +66,7 @@ export function toBase64(data, alphabet="A-Za-z0-9+/=") {
|
||||
/**
|
||||
* UnBase64's the input string using the given alphabet, returning a byte array.
|
||||
*
|
||||
* @param {byteArray} data
|
||||
* @param {string} data
|
||||
* @param {string} [alphabet="A-Za-z0-9+/="]
|
||||
* @param {string} [returnType="string"] - Either "string" or "byteArray"
|
||||
* @param {boolean} [removeNonAlphChars=true]
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
/**
|
||||
* Runs bitwise operations across the input data.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {byteArray|Uint8Array} input
|
||||
* @param {byteArray} key
|
||||
* @param {function} func - The bitwise calculation to carry out
|
||||
* @param {boolean} nullPreserving
|
||||
|
||||
@@ -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?");
|
||||
}
|
||||
@@ -327,13 +340,13 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
|
||||
* @param {string} input - The input data to be split
|
||||
* @returns {number[]} An array of the different items in the string, stored as floats
|
||||
*/
|
||||
function splitInput (input){
|
||||
function splitInput (input) {
|
||||
const split = [];
|
||||
|
||||
input.split(/\s+/).forEach(item => {
|
||||
// Remove any character that isn't a digit, decimal point or negative sign
|
||||
item = item.replace(/[^0-9.-]/g, "");
|
||||
if (item.length > 0){
|
||||
if (item.length > 0) {
|
||||
// Turn the item into a float
|
||||
split.push(parseFloat(item));
|
||||
}
|
||||
@@ -350,7 +363,7 @@ function splitInput (input){
|
||||
* @param {number} precision - The precision the result should be rounded to
|
||||
* @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string)
|
||||
*/
|
||||
function convDMSToDD (degrees, minutes, seconds, precision){
|
||||
function convDMSToDD (degrees, minutes, seconds, precision) {
|
||||
const absDegrees = Math.abs(degrees);
|
||||
let conv = absDegrees + (minutes / 60) + (seconds / 3600);
|
||||
let outString = round(conv, precision) + "°";
|
||||
@@ -566,7 +579,7 @@ export function findFormat (input, delim) {
|
||||
// Test DMS/DDM/DD formats
|
||||
if (testData !== undefined) {
|
||||
const split = splitInput(testData);
|
||||
switch (split.length){
|
||||
switch (split.length) {
|
||||
case 3:
|
||||
return "Degrees Minutes Seconds";
|
||||
case 2:
|
||||
|
||||
@@ -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"}
|
||||
];
|
||||
|
||||
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 [];
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -241,7 +241,7 @@ export function ipv6ListedRange(match, includeNetworkInfo) {
|
||||
ipv6List = ipv6List.filter(function(str) {
|
||||
return str.trim();
|
||||
});
|
||||
for (let i =0; i < ipv6List.length; i++){
|
||||
for (let i =0; i < ipv6List.length; i++) {
|
||||
ipv6List[i] = ipv6List[i].trim();
|
||||
}
|
||||
const ipv6CidrList = ipv6List.filter(function(a) {
|
||||
@@ -502,8 +502,8 @@ export function ipv6Compare(a, b) {
|
||||
const a_ = strToIpv6(a),
|
||||
b_ = strToIpv6(b);
|
||||
|
||||
for (let i = 0; i < a_.length; i++){
|
||||
if (a_[i] !== b_[i]){
|
||||
for (let i = 0; i < a_.length; i++) {
|
||||
if (a_[i] !== b_[i]) {
|
||||
return a_[i] - b_[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ function getWords(length=3) {
|
||||
const words = [];
|
||||
let word;
|
||||
let previousWord;
|
||||
while (words.length < length){
|
||||
while (words.length < length) {
|
||||
do {
|
||||
word = wordList[Math.floor(Math.random() * wordList.length)];
|
||||
} while (previousWord === word);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,14 +16,14 @@ class Protobuf {
|
||||
/**
|
||||
* Protobuf constructor
|
||||
*
|
||||
* @param {byteArray} data
|
||||
* @param {byteArray|Uint8Array} data
|
||||
*/
|
||||
constructor(data) {
|
||||
// Check we have a byteArray
|
||||
if (data instanceof Array) {
|
||||
// Check we have a byteArray or Uint8Array
|
||||
if (data instanceof Array || data instanceof Uint8Array) {
|
||||
this.data = data;
|
||||
} else {
|
||||
throw new Error("Protobuf input must be a byteArray");
|
||||
throw new Error("Protobuf input must be a byteArray or Uint8Array");
|
||||
}
|
||||
|
||||
// Set up masks
|
||||
@@ -205,7 +205,7 @@ class Protobuf {
|
||||
(this.data[this.offset] & this.VALUE) << shift :
|
||||
(this.data[this.offset] & this.VALUE) * Math.pow(2, shift);
|
||||
shift += 7;
|
||||
} while ((this.data[this.offset++] & this.MSD) === this.MSB);
|
||||
} while ((this.data[this.offset++] & this.MSB) === this.MSB);
|
||||
return fieldNumber;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ export default class TLVParser {
|
||||
/**
|
||||
* TLVParser constructor
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {byteArray|Uint8Array} input
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(input, options) {
|
||||
|
||||
@@ -71,8 +71,8 @@ class AESDecrypt extends Operation {
|
||||
* @throws {OperationError} if cannot decrypt input or invalid key length
|
||||
*/
|
||||
run(input, args) {
|
||||
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
||||
const key = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteString(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4],
|
||||
@@ -91,7 +91,7 @@ The following algorithms will be used based on the size of the key:
|
||||
|
||||
const decipher = forge.cipher.createDecipher("AES-" + mode, key);
|
||||
decipher.start({
|
||||
iv: iv,
|
||||
iv: iv.length === 0 ? "" : iv,
|
||||
tag: gcmTag
|
||||
});
|
||||
decipher.update(forge.util.createBuffer(input));
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@ class Adler32Checksum extends Operation {
|
||||
this.module = "Crypto";
|
||||
this.description = "Adler-32 is a checksum algorithm which was invented by Mark Adler in 1995, and is a modification of the Fletcher checksum. Compared to a cyclic redundancy check of the same length, it trades reliability for speed (preferring the latter).<br><br>Adler-32 is more reliable than Fletcher-16, and slightly less reliable than Fletcher-32.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Adler-32";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -36,6 +36,7 @@ class Adler32Checksum extends Operation {
|
||||
const MOD_ADLER = 65521;
|
||||
let a = 1,
|
||||
b = 0;
|
||||
input = new Uint8Array(input);
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
a += input[i];
|
||||
|
||||
107
src/core/operations/BaconCipherDecode.mjs
Normal file
107
src/core/operations/BaconCipherDecode.mjs
Normal file
@@ -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;
|
||||
101
src/core/operations/BaconCipherEncode.mjs
Normal file
101
src/core/operations/BaconCipherEncode.mjs
Normal file
@@ -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;
|
||||
@@ -21,8 +21,8 @@ class BitShiftLeft extends Operation {
|
||||
this.module = "Default";
|
||||
this.description = "Shifts the bits in each byte towards the left by the specified amount.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bit_shifts";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Amount",
|
||||
@@ -33,16 +33,17 @@ class BitShiftLeft extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
run(input, args) {
|
||||
const amount = args[0];
|
||||
input = new Uint8Array(input);
|
||||
|
||||
return input.map(b => {
|
||||
return (b << amount) & 0xff;
|
||||
});
|
||||
}).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,8 +21,8 @@ class BitShiftRight extends Operation {
|
||||
this.module = "Default";
|
||||
this.description = "Shifts the bits in each byte towards the right by the specified amount.<br><br><i>Logical shifts</i> replace the leftmost bits with zeros.<br><i>Arithmetic shifts</i> preserve the most significant bit (MSB) of the original byte keeping the sign the same (positive or negative).";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bit_shifts";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Amount",
|
||||
@@ -38,18 +38,19 @@ class BitShiftRight extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
run(input, args) {
|
||||
const amount = args[0],
|
||||
type = args[1],
|
||||
mask = type === "Logical shift" ? 0 : 0x80;
|
||||
input = new Uint8Array(input);
|
||||
|
||||
return input.map(b => {
|
||||
return (b >>> amount) ^ (b & mask);
|
||||
});
|
||||
}).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class BlurImage extends Operation {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
try {
|
||||
switch (blurType){
|
||||
switch (blurType) {
|
||||
case "Fast":
|
||||
if (isWorkerEnvironment())
|
||||
self.sendStatusMessage("Fast blurring image...");
|
||||
|
||||
@@ -29,12 +29,12 @@ class ChangeIPFormat extends Operation {
|
||||
{
|
||||
"name": "Input format",
|
||||
"type": "option",
|
||||
"value": ["Dotted Decimal", "Decimal", "Hex"]
|
||||
"value": ["Dotted Decimal", "Decimal", "Octal", "Hex"]
|
||||
},
|
||||
{
|
||||
"name": "Output format",
|
||||
"type": "option",
|
||||
"value": ["Dotted Decimal", "Decimal", "Hex"]
|
||||
"value": ["Dotted Decimal", "Decimal", "Octal", "Hex"]
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -54,7 +54,6 @@ class ChangeIPFormat extends Operation {
|
||||
if (lines[i] === "") continue;
|
||||
let baIp = [];
|
||||
let octets;
|
||||
let decimal;
|
||||
|
||||
if (inFormat === outFormat) {
|
||||
output += lines[i] + "\n";
|
||||
@@ -70,11 +69,10 @@ class ChangeIPFormat extends Operation {
|
||||
}
|
||||
break;
|
||||
case "Decimal":
|
||||
decimal = lines[i].toString();
|
||||
baIp.push(decimal >> 24 & 255);
|
||||
baIp.push(decimal >> 16 & 255);
|
||||
baIp.push(decimal >> 8 & 255);
|
||||
baIp.push(decimal & 255);
|
||||
baIp = this.fromNumber(lines[i].toString(), 10);
|
||||
break;
|
||||
case "Octal":
|
||||
baIp = this.fromNumber(lines[i].toString(), 8);
|
||||
break;
|
||||
case "Hex":
|
||||
baIp = fromHex(lines[i]);
|
||||
@@ -100,6 +98,10 @@ class ChangeIPFormat extends Operation {
|
||||
decIp = ((baIp[0] << 24) | (baIp[1] << 16) | (baIp[2] << 8) | baIp[3]) >>> 0;
|
||||
output += decIp.toString() + "\n";
|
||||
break;
|
||||
case "Octal":
|
||||
decIp = ((baIp[0] << 24) | (baIp[1] << 16) | (baIp[2] << 8) | baIp[3]) >>> 0;
|
||||
output += "0" + decIp.toString(8) + "\n";
|
||||
break;
|
||||
case "Hex":
|
||||
hexIp = "";
|
||||
for (j = 0; j < baIp.length; j++) {
|
||||
@@ -115,6 +117,22 @@ class ChangeIPFormat extends Operation {
|
||||
return output.slice(0, output.length-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an array of IP address octets from a numerical value.
|
||||
* @param {string} value The value of the IP address
|
||||
* @param {number} radix The numeral system to be used
|
||||
* @returns {number[]}
|
||||
*/
|
||||
fromNumber(value, radix) {
|
||||
const decimal = parseInt(value, radix);
|
||||
const baIp = [];
|
||||
baIp.push(decimal >> 24 & 255);
|
||||
baIp.push(decimal >> 16 & 255);
|
||||
baIp.push(decimal >> 8 & 255);
|
||||
baIp.push(decimal & 255);
|
||||
return baIp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ChangeIPFormat;
|
||||
|
||||
@@ -23,17 +23,18 @@ class CitrixCTX1Decode extends Operation {
|
||||
this.module = "Encodings";
|
||||
this.description = "Decodes strings in a Citrix CTX1 password format to plaintext.";
|
||||
this.infoURL = "https://www.reddit.com/r/AskNetsec/comments/1s3r6y/citrix_ctx1_hash_decoding/";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
if (input.length % 4 !== 0) {
|
||||
throw new OperationError("Incorrect hash length");
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ class DNSOverHTTPS extends Operation {
|
||||
* @returns {JSON}
|
||||
*/
|
||||
function extractData(data) {
|
||||
if (typeof(data) == "undefined"){
|
||||
if (typeof(data) == "undefined") {
|
||||
return [];
|
||||
} else {
|
||||
const dataValues = [];
|
||||
|
||||
@@ -30,7 +30,7 @@ class DecodeText extends Operation {
|
||||
"</ul>",
|
||||
].join("\n");
|
||||
this.infoURL = "https://wikipedia.org/wiki/Character_encoding";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
@@ -42,13 +42,13 @@ class DecodeText extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const format = IO_FORMAT[args[0]];
|
||||
return cptable.utils.decode(format, input);
|
||||
return cptable.utils.decode(format, new Uint8Array(input));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
61
src/core/operations/DefangIPAddresses.mjs
Normal file
61
src/core/operations/DefangIPAddresses.mjs
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @author h345983745
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
|
||||
/**
|
||||
* Defang IP Addresses operation
|
||||
*/
|
||||
class DefangIPAddresses extends Operation {
|
||||
|
||||
/**
|
||||
* DefangIPAddresses constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Defang IP Addresses";
|
||||
this.module = "Default";
|
||||
this.description = "Takes a IPv4 or IPv6 address and 'Defangs' it, meaning the IP becomes invalid, removing the risk of accidentally utilising it as an IP address.";
|
||||
this.infoURL = "https://isc.sans.edu/forums/diary/Defang+all+the+things/22744/";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = input.replace(IPV4_REGEX, x => {
|
||||
return x.replace(/\./g, "[.]");
|
||||
});
|
||||
|
||||
input = input.replace(IPV6_REGEX, x => {
|
||||
return x.replace(/:/g, "[:]");
|
||||
});
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
export default DefangIPAddresses;
|
||||
|
||||
|
||||
/**
|
||||
* IPV4 regular expression
|
||||
*/
|
||||
const IPV4_REGEX = new RegExp("(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?", "g");
|
||||
|
||||
|
||||
/**
|
||||
* IPV6 regular expression
|
||||
*/
|
||||
const IPV6_REGEX = new RegExp("((?=.*::)(?!.*::.+::)(::)?([\\dA-Fa-f]{1,4}:(:|\\b)|){5}|([\\dA-Fa-f]{1,4}:){6})((([\\dA-Fa-f]{1,4}((?!\\3)::|:\\b|(?![\\dA-Fa-f])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})", "g");
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class EncodeText extends Operation {
|
||||
].join("\n");
|
||||
this.infoURL = "https://wikipedia.org/wiki/Character_encoding";
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Encoding",
|
||||
@@ -44,13 +44,12 @@ class EncodeText extends Operation {
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
run(input, args) {
|
||||
const format = IO_FORMAT[args[0]];
|
||||
let encoded = cptable.utils.encode(format, input);
|
||||
encoded = Array.from(encoded);
|
||||
return encoded;
|
||||
const encoded = cptable.utils.encode(format, input);
|
||||
return new Uint8Array(encoded).buffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class ExtractDomains extends Operation {
|
||||
|
||||
this.name = "Extract domains";
|
||||
this.module = "Regex";
|
||||
this.description = "Extracts domain names.<br>Note that this will not include paths. Use <strong>Extract URLs</strong> to find entire URLs.";
|
||||
this.description = "Extracts fully qualified domain names.<br>Note that this will not include paths. Use <strong>Extract URLs</strong> to find entire URLs.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
|
||||
114
src/core/operations/ExtractLSB.mjs
Normal file
114
src/core/operations/ExtractLSB.mjs
Normal file
@@ -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;
|
||||
65
src/core/operations/ExtractRGBA.mjs
Normal file
65
src/core/operations/ExtractRGBA.mjs
Normal file
@@ -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;
|
||||
@@ -22,19 +22,20 @@ class Fletcher16Checksum extends Operation {
|
||||
this.module = "Crypto";
|
||||
this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.<br><br>The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum#Fletcher-16";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
let a = 0,
|
||||
b = 0;
|
||||
input = new Uint8Array(input);
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
a = (a + input[i]) % 0xff;
|
||||
|
||||
@@ -22,19 +22,20 @@ class Fletcher32Checksum extends Operation {
|
||||
this.module = "Crypto";
|
||||
this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.<br><br>The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum#Fletcher-32";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
let a = 0,
|
||||
b = 0;
|
||||
input = new Uint8Array(input);
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
a = (a + input[i]) % 0xffff;
|
||||
|
||||
@@ -22,19 +22,20 @@ class Fletcher64Checksum extends Operation {
|
||||
this.module = "Crypto";
|
||||
this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.<br><br>The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum#Fletcher-64";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
let a = 0,
|
||||
b = 0;
|
||||
input = new Uint8Array(input);
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
a = (a + input[i]) % 0xffffffff;
|
||||
|
||||
@@ -22,19 +22,20 @@ class Fletcher8Checksum extends Operation {
|
||||
this.module = "Crypto";
|
||||
this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.<br><br>The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
let a = 0,
|
||||
b = 0;
|
||||
input = new Uint8Array(input);
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
a = (a + input[i]) % 0xf;
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ class FlipImage extends Operation {
|
||||
try {
|
||||
if (isWorkerEnvironment())
|
||||
self.sendStatusMessage("Flipping image...");
|
||||
switch (flipAxis){
|
||||
switch (flipAxis) {
|
||||
case "Horizontal":
|
||||
image.flip(true, false);
|
||||
break;
|
||||
|
||||
@@ -106,9 +106,9 @@ class FromBase64 extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [alphabet, removeNonAlphChars] = args;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -23,7 +23,7 @@ class GenerateHOTP extends Operation {
|
||||
this.module = "Default";
|
||||
this.description = "The HMAC-based One-Time Password algorithm (HOTP) is an algorithm that computes a one-time password from a shared secret key and an incrementing counter. It has been adopted as Internet Engineering Task Force standard RFC 4226, is the cornerstone of Initiative For Open Authentication (OATH), and is used in a number of two-factor authentication systems.<br><br>Enter the secret as the input or leave it blank for a random secret to be generated.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/HMAC-based_One-time_Password_algorithm";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
@@ -50,7 +50,7 @@ class GenerateHOTP extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
@@ -47,7 +47,7 @@ class GenerateLoremIpsum extends Operation {
|
||||
*/
|
||||
run(input, args) {
|
||||
const [length, lengthType] = args;
|
||||
if (length < 1){
|
||||
if (length < 1) {
|
||||
throw new OperationError("Length must be greater than 0");
|
||||
}
|
||||
switch (lengthType) {
|
||||
|
||||
@@ -23,7 +23,7 @@ class GenerateTOTP extends Operation {
|
||||
this.module = "Default";
|
||||
this.description = "The Time-based One-Time Password algorithm (TOTP) is an algorithm that computes a one-time password from a shared secret key and the current time. It has been adopted as Internet Engineering Task Force standard RFC 6238, is the cornerstone of Initiative For Open Authentication (OATH), and is used in a number of two-factor authentication systems. A TOTP is an HOTP where the counter is the current time.<br><br>Enter the secret as the input or leave it blank for a random secret to be generated. T0 and T1 are in seconds.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Time-based_One-time_Password_algorithm";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
@@ -55,7 +55,7 @@ class GenerateTOTP extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class MicrosoftScriptDecoder extends Operation {
|
||||
run(input, args) {
|
||||
const matcher = /#@~\^.{6}==(.+).{6}==\^#~@/;
|
||||
const encodedData = matcher.exec(input);
|
||||
if (encodedData){
|
||||
if (encodedData) {
|
||||
return MicrosoftScriptDecoder._decode(encodedData[1]);
|
||||
} else {
|
||||
return "";
|
||||
|
||||
@@ -22,17 +22,18 @@ class NOT extends Operation {
|
||||
this.module = "Default";
|
||||
this.description = "Returns the inverse of each byte.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#NOT";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
return bitOp(input, null, not);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class OR extends Operation {
|
||||
this.module = "Default";
|
||||
this.description = "OR the input with the given key.<br>e.g. <code>fe023da5</code>";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#OR";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [
|
||||
{
|
||||
@@ -36,12 +36,13 @@ class OR extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
|
||||
input = new Uint8Array(input);
|
||||
|
||||
return bitOp(input, key, or);
|
||||
}
|
||||
|
||||
87
src/core/operations/OpticalCharacterRecognition.mjs
Normal file
87
src/core/operations/OpticalCharacterRecognition.mjs
Normal file
@@ -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;
|
||||
@@ -113,7 +113,7 @@ CMYK: ${cmyk}
|
||||
}).on('colorpickerChange', function(e) {
|
||||
var color = e.color.string('rgba');
|
||||
document.getElementById('input-text').value = color;
|
||||
window.app.autoBake();
|
||||
window.app.manager.input.debounceInputChange(new Event("keyup"));
|
||||
});
|
||||
</script>`;
|
||||
}
|
||||
@@ -134,7 +134,7 @@ CMYK: ${cmyk}
|
||||
static _hslToRgb(h, s, l) {
|
||||
let r, g, b;
|
||||
|
||||
if (s === 0){
|
||||
if (s === 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
const hue2rgb = function hue2rgb(p, q, t) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
150
src/core/operations/ParseSSHHostKey.mjs
Normal file
150
src/core/operations/ParseSSHHostKey.mjs
Normal file
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Utils from "../Utils";
|
||||
import { fromBase64 } from "../lib/Base64";
|
||||
import { fromHex, toHexFast } from "../lib/Hex";
|
||||
|
||||
/**
|
||||
* Parse SSH Host Key operation
|
||||
*/
|
||||
class ParseSSHHostKey extends Operation {
|
||||
|
||||
/**
|
||||
* ParseSSHHostKey constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Parse SSH Host Key";
|
||||
this.module = "Default";
|
||||
this.description = "Parses a SSH host key and extracts fields from it.<br>The key type can be:<ul><li>ssh-rsa</li><li>ssh-dss</li><li>ecdsa-sha2</li></ul>The key format can be either Hex or Base64.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Secure_Shell";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Input Format",
|
||||
type: "option",
|
||||
value: [
|
||||
"Auto",
|
||||
"Base64",
|
||||
"Hex"
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [inputFormat] = args,
|
||||
inputKey = this.convertKeyToBinary(input.trim(), inputFormat),
|
||||
fields = this.parseKey(inputKey),
|
||||
keyType = Utils.byteArrayToChars(fromHex(fields[0]), "");
|
||||
|
||||
let output = `Key type: ${keyType}`;
|
||||
|
||||
if (keyType === "ssh-rsa") {
|
||||
output += `\nExponent: 0x${fields[1]}`;
|
||||
output += `\nModulus: 0x${fields[2]}`;
|
||||
} else if (keyType === "ssh-dss") {
|
||||
output += `\np: 0x${fields[1]}`;
|
||||
output += `\nq: 0x${fields[2]}`;
|
||||
output += `\ng: 0x${fields[3]}`;
|
||||
output += `\ny: 0x${fields[4]}`;
|
||||
} else if (keyType.startsWith("ecdsa-sha2")) {
|
||||
output += `\nCurve: ${Utils.byteArrayToChars(fromHex(fields[1]))}`;
|
||||
output += `\nPoint: 0x${fields.slice(2)}`;
|
||||
} else {
|
||||
output += "\nUnsupported key type.";
|
||||
output += `\nParameters: ${fields.slice(1)}`;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the key to binary format from either hex or base64
|
||||
*
|
||||
* @param {string} inputKey
|
||||
* @param {string} inputFormat
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
convertKeyToBinary(inputKey, inputFormat) {
|
||||
const keyPattern = new RegExp(/^(?:[ssh]|[ecdsa-sha2])\S+\s+(\S*)/),
|
||||
keyMatch = inputKey.match(keyPattern);
|
||||
|
||||
if (keyMatch) {
|
||||
inputKey = keyMatch[1];
|
||||
}
|
||||
|
||||
if (inputFormat === "Auto") {
|
||||
inputFormat = this.detectKeyFormat(inputKey);
|
||||
}
|
||||
if (inputFormat === "Hex") {
|
||||
return fromHex(inputKey);
|
||||
} else if (inputFormat === "Base64") {
|
||||
return fromBase64(inputKey, null, "byteArray");
|
||||
} else {
|
||||
throw new OperationError("Invalid input format.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Detects if the key is base64 or hex encoded
|
||||
*
|
||||
* @param {string} inputKey
|
||||
* @returns {string}
|
||||
*/
|
||||
detectKeyFormat(inputKey) {
|
||||
const hexPattern = new RegExp(/^(?:[\dA-Fa-f]{2}[ ,;:]?)+$/);
|
||||
const b64Pattern = new RegExp(/^\s*(?:[A-Za-z\d+/]{4})+(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?\s*$/);
|
||||
|
||||
if (hexPattern.test(inputKey)) {
|
||||
return "Hex";
|
||||
} else if (b64Pattern.test(inputKey)) {
|
||||
return "Base64";
|
||||
} else {
|
||||
throw new OperationError("Unable to detect input key format.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses fields from the key
|
||||
*
|
||||
* @param {byteArray} key
|
||||
*/
|
||||
parseKey(key) {
|
||||
const fields = [];
|
||||
while (key.length > 0) {
|
||||
const lengthField = key.slice(0, 4);
|
||||
let decodedLength = 0;
|
||||
for (let i = 0; i < lengthField.length; i++) {
|
||||
decodedLength += lengthField[i];
|
||||
decodedLength = decodedLength << 8;
|
||||
}
|
||||
decodedLength = decodedLength >> 8;
|
||||
// Break if length wasn't decoded correctly
|
||||
if (decodedLength <= 0) break;
|
||||
|
||||
fields.push(toHexFast(key.slice(4, 4 + decodedLength)));
|
||||
key = key.slice(4 + decodedLength);
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ParseSSHHostKey;
|
||||
@@ -24,7 +24,7 @@ class ParseTLV extends Operation {
|
||||
this.module = "Default";
|
||||
this.description = "Converts a Type-Length-Value (TLV) encoded string into a JSON object. Can optionally include a <code>Key</code> / <code>Type</code> entry. <br><br>Tags: Key-Length-Value, KLV, Length-Value, LV";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Type-length-value";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "JSON";
|
||||
this.args = [
|
||||
{
|
||||
@@ -46,12 +46,13 @@ class ParseTLV extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [bytesInKey, bytesInLength, basicEncodingRules] = args;
|
||||
input = new Uint8Array(input);
|
||||
|
||||
if (bytesInKey <= 0 && bytesInLength <= 0)
|
||||
throw new OperationError("Type or Length size must be greater than 0");
|
||||
|
||||
84
src/core/operations/ParseUDP.mjs
Normal file
84
src/core/operations/ParseUDP.mjs
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @author h345983745 []
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Stream from "../lib/Stream.mjs";
|
||||
import {toHex} from "../lib/Hex.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Parse UDP operation
|
||||
*/
|
||||
class ParseUDP extends Operation {
|
||||
|
||||
/**
|
||||
* ParseUDP constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Parse UDP";
|
||||
this.module = "Default";
|
||||
this.description = "Parses a UDP header and payload (if present).";
|
||||
this.infoURL = "https://wikipedia.org/wiki/User_Datagram_Protocol";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "json";
|
||||
this.presentType = "html";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @returns {Object}
|
||||
*/
|
||||
run(input, args) {
|
||||
if (input.byteLength < 8) {
|
||||
throw new OperationError("Need 8 bytes for a UDP Header");
|
||||
}
|
||||
|
||||
const s = new Stream(new Uint8Array(input));
|
||||
// Parse Header
|
||||
const UDPPacket = {
|
||||
"Source port": s.readInt(2),
|
||||
"Destination port": s.readInt(2),
|
||||
"Length": s.readInt(2),
|
||||
"Checksum": toHex(s.getBytes(2), "")
|
||||
};
|
||||
// Parse data if present
|
||||
if (s.hasMore()) {
|
||||
UDPPacket.Data = toHex(s.getBytes(UDPPacket.Length - 8), "");
|
||||
}
|
||||
|
||||
return UDPPacket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the UDP Packet in a table style
|
||||
* @param {Object} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
const html = [];
|
||||
html.push("<table class='table table-hover table-sm table-bordered table-nonfluid' style='table-layout: fixed'>");
|
||||
html.push("<tr>");
|
||||
html.push("<th>Field</th>");
|
||||
html.push("<th>Value</th>");
|
||||
html.push("</tr>");
|
||||
|
||||
for (const key in data) {
|
||||
html.push("<tr>");
|
||||
html.push("<td style=\"word-wrap:break-word\">" + key + "</td>");
|
||||
html.push("<td>" + data[key] + "</td>");
|
||||
html.push("</tr>");
|
||||
}
|
||||
html.push("</table>");
|
||||
return html.join("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default ParseUDP;
|
||||
@@ -23,17 +23,18 @@ class ProtobufDecode extends Operation {
|
||||
this.module = "Default";
|
||||
this.description = "Decodes any Protobuf encoded data to a JSON representation of the data using the field number as the field key.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Protocol_Buffers";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "JSON";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {JSON}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
try {
|
||||
return Protobuf.decode(input);
|
||||
} catch (err) {
|
||||
|
||||
83
src/core/operations/RandomizeColourPalette.mjs
Normal file
83
src/core/operations/RandomizeColourPalette.mjs
Normal file
@@ -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;
|
||||
@@ -27,17 +27,18 @@ class RemoveEXIF extends Operation {
|
||||
"EXIF data embedded in photos usually contains information about the image file itself as well as the device used to create it.",
|
||||
].join("\n");
|
||||
this.infoURL = "https://wikipedia.org/wiki/Exif";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
// Do nothing if input is empty
|
||||
if (input.length === 0) return input;
|
||||
|
||||
|
||||
@@ -20,17 +20,18 @@ class RemoveNullBytes extends Operation {
|
||||
this.name = "Remove null bytes";
|
||||
this.module = "Default";
|
||||
this.description = "Removes all null bytes (<code>0x00</code>) from the input.";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
const output = [];
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
if (input[i] !== 0) output.push(input[i]);
|
||||
|
||||
69
src/core/operations/RenderMarkdown.mjs
Normal file
69
src/core/operations/RenderMarkdown.mjs
Normal file
@@ -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.");
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ class ScanForEmbeddedFiles extends Operation {
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
let output = "Scanning data for 'magic bytes' which may indicate embedded files. The following results may be false positives and should not be treat as reliable. Any suffiently long file is likely to contain these magic bytes coincidentally.\n",
|
||||
let output = "Scanning data for 'magic bytes' which may indicate embedded files. The following results may be false positives and should not be treat as reliable. Any sufficiently long file is likely to contain these magic bytes coincidentally.\n",
|
||||
numFound = 0;
|
||||
const categories = [],
|
||||
data = new Uint8Array(input);
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
|
||||
113
src/core/operations/ShowOnMap.mjs
Normal file
113
src/core/operations/ShowOnMap.mjs
Normal file
@@ -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;
|
||||
@@ -26,18 +26,19 @@ class SplitColourChannels extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Splits the given image into its red, green and blue colour channels.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Channel_(digital_image)";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "List<File>";
|
||||
this.presentType = "html";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {List<File>}
|
||||
*/
|
||||
async run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
// Make sure that the input is an image
|
||||
if (!isImage(input)) throw new OperationError("Invalid file type.");
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ class SwapEndianness extends Operation {
|
||||
const word = data.slice(i, i + wordLength);
|
||||
|
||||
// Pad word if too short
|
||||
if (padIncompleteWords && word.length < wordLength){
|
||||
if (padIncompleteWords && word.length < wordLength) {
|
||||
for (j = word.length; j < wordLength; j++) {
|
||||
word.push(0);
|
||||
}
|
||||
|
||||
@@ -22,17 +22,18 @@ class TCPIPChecksum extends Operation {
|
||||
this.module = "Crypto";
|
||||
this.description = "Calculates the checksum for a TCP (Transport Control Protocol) or IP (Internet Protocol) header from an input of raw bytes.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/IPv4_header_checksum";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
let csum = 0;
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
|
||||
@@ -22,7 +22,7 @@ class Tar extends Operation {
|
||||
this.module = "Compression";
|
||||
this.description = "Packs the input into a tarball.<br><br>No support for multiple files at this time.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Tar_(computing)";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "File";
|
||||
this.args = [
|
||||
{
|
||||
@@ -34,11 +34,13 @@ class Tar extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
|
||||
const Tarball = function() {
|
||||
this.bytes = new Array(512);
|
||||
this.position = 0;
|
||||
|
||||
@@ -22,7 +22,7 @@ class ToBase32 extends Operation {
|
||||
this.module = "Default";
|
||||
this.description = "Base32 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers. It uses a smaller set of characters than Base64, usually the uppercase alphabet and the numbers 2 to 7.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Base32";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
@@ -34,12 +34,13 @@ class ToBase32 extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
if (!input) return "";
|
||||
input = new Uint8Array(input);
|
||||
|
||||
const alphabet = args[0] ? Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=";
|
||||
let output = "",
|
||||
|
||||
@@ -24,7 +24,7 @@ class ToBase58 extends Operation {
|
||||
this.module = "Default";
|
||||
this.description = "Base58 (similar to Base64) is a notation for encoding arbitrary byte data. It differs from Base64 by removing easily misread characters (i.e. l, I, 0 and O) to improve human readability.<br><br>This operation encodes data in an ASCII string (with an alphabet of your choosing, presets included).<br><br>e.g. <code>hello world</code> becomes <code>StV1DL6CwTryKyV</code><br><br>Base58 is commonly used in cryptocurrencies (Bitcoin, Ripple, etc).";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Base58";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
@@ -36,11 +36,12 @@ class ToBase58 extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
let alphabet = args[0] || ALPHABET_OPTIONS[0].value,
|
||||
result = [0];
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class ToBase62 extends Operation {
|
||||
this.module = "Default";
|
||||
this.description = "Base62 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers. The high number base results in shorter strings than with the decimal or hexadecimal system.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
@@ -36,11 +36,12 @@ class ToBase62 extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
if (input.length < 1) return "";
|
||||
|
||||
const ALPHABET = Utils.expandAlphRange(args[0]).join("");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user