mirror of
https://github.com/gchq/CyberChef
synced 2025-12-15 15:53:30 +00:00
Compare commits
307 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57bb8fbc45 | ||
|
|
9175624210 | ||
|
|
289a417dfb | ||
|
|
8379a9b275 | ||
|
|
5b1fad118f | ||
|
|
5e8985810e | ||
|
|
d2568e2a29 | ||
|
|
6dfc21ef06 | ||
|
|
1f19f2f58c | ||
|
|
1728cc7a85 | ||
|
|
fa2fc2ba33 | ||
|
|
9a33498fed | ||
|
|
a3b873fd96 | ||
|
|
97bd03799e | ||
|
|
ffaaaae2b4 | ||
|
|
ff88d30d2f | ||
|
|
88e3c2ccb2 | ||
|
|
5029356514 | ||
|
|
e57d5a7e75 | ||
|
|
2bbe54cdcd | ||
|
|
0e2423c390 | ||
|
|
8fadad5891 | ||
|
|
32455cd20f | ||
|
|
1e0e7f16a7 | ||
|
|
95884d77cf | ||
|
|
b69373f5e7 | ||
|
|
61e85474d3 | ||
|
|
3a9bdc58af | ||
|
|
59c1c45d78 | ||
|
|
b5f6cedd30 | ||
|
|
c879af6860 | ||
|
|
22fe5a6ae7 | ||
|
|
57714c86a6 | ||
|
|
70cd375049 | ||
|
|
e27e1dd42f | ||
|
|
8ad18bc7db | ||
|
|
5893ac1a37 | ||
|
|
83c3ab97f9 | ||
|
|
9b6be140fa | ||
|
|
a6a60392c2 | ||
|
|
ccfa0b991e | ||
|
|
73b0e68993 | ||
|
|
31a4eef001 | ||
|
|
d6e2c9a6b9 | ||
|
|
e069f5db13 | ||
|
|
96b59cf0df | ||
|
|
c1e1d4b7e3 | ||
|
|
32d869231e | ||
|
|
6f95f01dda | ||
|
|
61a1c44f26 | ||
|
|
e6c7899569 | ||
|
|
a74a14145e | ||
|
|
04022b22be | ||
|
|
4f3010691c | ||
|
|
672b477751 | ||
|
|
19360391a6 | ||
|
|
447be8af34 | ||
|
|
0989550e5c | ||
|
|
4d9b48b4d8 | ||
|
|
979652387d | ||
|
|
de84fbdd1c | ||
|
|
170e564319 | ||
|
|
530836876f | ||
|
|
1abc46058c | ||
|
|
892a3716ed | ||
|
|
5d982a9c8d | ||
|
|
6206224f1e | ||
|
|
766310e2c7 | ||
|
|
02a397d2ae | ||
|
|
4563c86acd | ||
|
|
0a59f8068e | ||
|
|
24548e3a48 | ||
|
|
f4784d49e7 | ||
|
|
14d5069c6e | ||
|
|
9fdd55c5c6 | ||
|
|
5bc523aeff | ||
|
|
3ae2e2e2c8 | ||
|
|
83e49da7f6 | ||
|
|
fe6df8778f | ||
|
|
69073c9d99 | ||
|
|
d5a0adea0c | ||
|
|
1bcb8e433d | ||
|
|
fa05cf1d78 | ||
|
|
63dff0d34d | ||
|
|
e228b197f9 | ||
|
|
4bbeb6caa3 | ||
|
|
139d25dff9 | ||
|
|
6984258404 | ||
|
|
ba66fd6546 | ||
|
|
47bbefd81f | ||
|
|
50f796049c | ||
|
|
618da545b1 | ||
|
|
21236f1938 | ||
|
|
4169a15066 | ||
|
|
6b10f61e11 | ||
|
|
83f119f7e4 | ||
|
|
041c899a35 | ||
|
|
5412fc01b3 | ||
|
|
76926d9252 | ||
|
|
3270961574 | ||
|
|
9a1ef71aec | ||
|
|
ba878925ad | ||
|
|
8d6b71bfaa | ||
|
|
b6845aa03c | ||
|
|
4a673bd92a | ||
|
|
fdffabfdd4 | ||
|
|
ba8591293b | ||
|
|
9b3aae10cf | ||
|
|
5c85c4df63 | ||
|
|
8ece2603fb | ||
|
|
1b54584820 | ||
|
|
3ce3866000 | ||
|
|
becc258b6c | ||
|
|
16c9e7119d | ||
|
|
f5888fea9c | ||
|
|
b5162c7549 | ||
|
|
1baea1da3d | ||
|
|
40899a6fe4 | ||
|
|
66c533431d | ||
|
|
74ae77f17a | ||
|
|
99eb1cced5 | ||
|
|
c880ecf3c4 | ||
|
|
c54c34d88e | ||
|
|
60b3c597a7 | ||
|
|
372ab32539 | ||
|
|
e14745a973 | ||
|
|
afffe584cf | ||
|
|
cf532f1e30 | ||
|
|
46425ba552 | ||
|
|
9f65fac4e6 | ||
|
|
af98feff51 | ||
|
|
339c741a2c | ||
|
|
d1bde23f00 | ||
|
|
be544faf0f | ||
|
|
eff77fd3bb | ||
|
|
3df57ba3dd | ||
|
|
4bae662357 | ||
|
|
357c90546e | ||
|
|
54769a90ac | ||
|
|
0550aedd54 | ||
|
|
5947ed21fc | ||
|
|
0a0949246f | ||
|
|
09c6e181fb | ||
|
|
02cf394bcd | ||
|
|
46afbf9888 | ||
|
|
7cf19d22a8 | ||
|
|
46d708360f | ||
|
|
7ec91e2366 | ||
|
|
a74ee47bf0 | ||
|
|
7b68b92498 | ||
|
|
274f3acd45 | ||
|
|
53dff8b30f | ||
|
|
9892ee273e | ||
|
|
7dccecb336 | ||
|
|
aa09da0403 | ||
|
|
98d7f1481c | ||
|
|
7d8bdbcf7e | ||
|
|
db009d3689 | ||
|
|
d7bc529a95 | ||
|
|
3f035294a6 | ||
|
|
36282e362f | ||
|
|
223353cf4d | ||
|
|
ded32da632 | ||
|
|
d6fc21cc34 | ||
|
|
b86e960456 | ||
|
|
7747bfe0f2 | ||
|
|
fabea8cc61 | ||
|
|
de4cd2eebc | ||
|
|
e16ce1d9c2 | ||
|
|
345ad741b3 | ||
|
|
e53108c493 | ||
|
|
6129378854 | ||
|
|
11a1416dcc | ||
|
|
9025538544 | ||
|
|
46929e1844 | ||
|
|
bf023cad48 | ||
|
|
f649236bad | ||
|
|
54b1454c0a | ||
|
|
a41b1c2f5e | ||
|
|
0327d7cb7a | ||
|
|
621d7c3683 | ||
|
|
ae7c3fca31 | ||
|
|
1b3295ff59 | ||
|
|
e40e7a0e4e | ||
|
|
cf5fd7cbf2 | ||
|
|
ec37a676a8 | ||
|
|
f33193e122 | ||
|
|
7c40204e4f | ||
|
|
2b2ffb3346 | ||
|
|
39b7e4ff9e | ||
|
|
a1109c43f6 | ||
|
|
887ea0cf06 | ||
|
|
3e0525ee9e | ||
|
|
e6eafc2843 | ||
|
|
3e8dea73d2 | ||
|
|
bbf19ee944 | ||
|
|
f6c8c9e76c | ||
|
|
9dba1232b7 | ||
|
|
bf14c8983f | ||
|
|
3ab95384df | ||
|
|
953a581a94 | ||
|
|
3bfddd708c | ||
|
|
13a54ec318 | ||
|
|
2781640a2a | ||
|
|
7989f119d3 | ||
|
|
2e0aa7ae87 | ||
|
|
0b5ee7c79f | ||
|
|
23cbe1c426 | ||
|
|
de727bcddc | ||
|
|
c9d9730726 | ||
|
|
a380aed878 | ||
|
|
4dafa50799 | ||
|
|
d4ae241758 | ||
|
|
53e69835ff | ||
|
|
939208903a | ||
|
|
7526f4d7b1 | ||
|
|
616b38c6fb | ||
|
|
a302df8f91 | ||
|
|
093512a55a | ||
|
|
007224c92e | ||
|
|
738ee33959 | ||
|
|
d720a6b250 | ||
|
|
5ce3cc17bb | ||
|
|
5c35205315 | ||
|
|
10751934e4 | ||
|
|
d658f91106 | ||
|
|
fae96af17d | ||
|
|
57c1a03c4f | ||
|
|
cb8fe42c66 | ||
|
|
7f4b2574b0 | ||
|
|
fad163e0eb | ||
|
|
7ad3992bd1 | ||
|
|
e7b5c0e37c | ||
|
|
cc35127459 | ||
|
|
1c0ecd29c2 | ||
|
|
1f0fddd0e9 | ||
|
|
18c6b9bc09 | ||
|
|
2233b9a094 | ||
|
|
e0f000b913 | ||
|
|
73864e0809 | ||
|
|
cd8a85975c | ||
|
|
2f94ec20b0 | ||
|
|
09d9deae43 | ||
|
|
209fc07eac | ||
|
|
ae70cb89ed | ||
|
|
4c3324aea1 | ||
|
|
ac2fcee90f | ||
|
|
94e00115fe | ||
|
|
29255d2338 | ||
|
|
bda36e508a | ||
|
|
d2ea1273da | ||
|
|
39278cfce7 | ||
|
|
46cc48cfb9 | ||
|
|
eb4009949d | ||
|
|
57c48a4bd2 | ||
|
|
45011de494 | ||
|
|
5e51ed0a5f | ||
|
|
875802ef2a | ||
|
|
bbc255ef83 | ||
|
|
fc155ec3fc | ||
|
|
3a0c8a199a | ||
|
|
9c729c4490 | ||
|
|
19bdbd66e5 | ||
|
|
ea090f79ee | ||
|
|
1be6c54be2 | ||
|
|
a4eeb226b1 | ||
|
|
d136717636 | ||
|
|
ad0a2e6f58 | ||
|
|
0212bfb46e | ||
|
|
5e0d661542 | ||
|
|
4c5f529ef4 | ||
|
|
b765534b8b | ||
|
|
7c672c5ee9 | ||
|
|
090bf3f8ec | ||
|
|
9f4ef9cdad | ||
|
|
26fa66ef64 | ||
|
|
b69e4567c0 | ||
|
|
26b19350f2 | ||
|
|
2018b7e247 | ||
|
|
ff585584f6 | ||
|
|
2e0af64ac3 | ||
|
|
8a029e5147 | ||
|
|
4251089687 | ||
|
|
dbcd670ca8 | ||
|
|
30bc8dfbe9 | ||
|
|
5b6a53be3e | ||
|
|
5b5105c864 | ||
|
|
53a579028c | ||
|
|
570a84b67a | ||
|
|
fd7176a445 | ||
|
|
0a06472639 | ||
|
|
3f3a7cd4f6 | ||
|
|
99415359d0 | ||
|
|
20d0ae5304 | ||
|
|
2ba37af109 | ||
|
|
728f8e65d6 | ||
|
|
3a2580fbc2 | ||
|
|
462f619f43 | ||
|
|
123a0ccd70 | ||
|
|
4c737475d4 | ||
|
|
4e0d97f2c1 | ||
|
|
85906cafbb | ||
|
|
a8dc691033 | ||
|
|
4d7988b78e | ||
|
|
841e760b04 | ||
|
|
31e758ca45 | ||
|
|
f81ca3ba60 |
@@ -63,7 +63,8 @@
|
||||
}],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"quotes": ["error", "double", {
|
||||
"avoidEscape": true
|
||||
"avoidEscape": true,
|
||||
"allowTemplateLiterals": true
|
||||
}],
|
||||
"camelcase": ["error", {
|
||||
"properties": "always"
|
||||
|
||||
32
.github/workflows/codeql.yml
vendored
Normal file
32
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: "CodeQL Analysis"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '22 17 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
54
.github/workflows/master.yml
vendored
Normal file
54
.github/workflows/master.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: "Master Build, Test & Deploy"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set node version
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
npm install
|
||||
npm run setheapsize
|
||||
|
||||
- name: Lint
|
||||
run: npx grunt lint
|
||||
|
||||
- name: Unit Tests
|
||||
run: |
|
||||
npm test
|
||||
npx grunt testnodeconsumer
|
||||
|
||||
- name: Production Build
|
||||
if: success()
|
||||
run: npx grunt prod
|
||||
|
||||
- name: Generate sitemap
|
||||
run: npx grunt exec:sitemap
|
||||
|
||||
# - name: UI Tests
|
||||
# if: success()
|
||||
# run: xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
|
||||
|
||||
- name: Prepare for GitHub Pages
|
||||
if: success()
|
||||
run: npx grunt copy:ghPages
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
if: success() && github.ref == 'refs/heads/master'
|
||||
uses: crazy-max/ghaction-github-pages@v2
|
||||
with:
|
||||
target_branch: gh-pages
|
||||
build_dir: ./build/prod
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
37
.github/workflows/pull_requests.yml
vendored
Normal file
37
.github/workflows/pull_requests.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: "Pull Requests"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [synchronize, opened, reopened]
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set node version
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
npm install
|
||||
npm run setheapsize
|
||||
|
||||
- name: Lint
|
||||
run: npx grunt lint
|
||||
|
||||
- name: Unit Tests
|
||||
run: |
|
||||
npm test
|
||||
npx grunt testnodeconsumer
|
||||
|
||||
- name: Production Build
|
||||
if: success()
|
||||
run: npx grunt prod
|
||||
|
||||
# - name: UI Tests
|
||||
# if: success()
|
||||
# run: xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
|
||||
56
.github/workflows/releases.yml
vendored
Normal file
56
.github/workflows/releases.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: "Releases"
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set node version
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
npm install
|
||||
npm run setheapsize
|
||||
|
||||
- name: Lint
|
||||
run: npx grunt lint
|
||||
|
||||
- name: Unit Tests
|
||||
run: |
|
||||
npm test
|
||||
npx grunt testnodeconsumer
|
||||
|
||||
- name: Production Build
|
||||
if: success()
|
||||
run: npx grunt prod
|
||||
|
||||
# - name: UI Tests
|
||||
# if: success()
|
||||
# run: xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
|
||||
|
||||
- name: Upload Release Assets
|
||||
if: success()
|
||||
id: upload-release-assets
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/prod/*.zip
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
body: "See the [CHANGELOG](https://github.com/gchq/CyberChef/blob/master/CHANGELOG.md) and [commit messages](https://github.com/gchq/CyberChef/commits/master) for details."
|
||||
|
||||
- name: Publish to NPM
|
||||
if: success()
|
||||
uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
55
.travis.yml
55
.travis.yml
@@ -1,55 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- lts/dubnium
|
||||
cache: npm
|
||||
os: linux
|
||||
addons:
|
||||
chrome: stable
|
||||
install: npm install
|
||||
before_script:
|
||||
- npm install -g grunt
|
||||
- export NODE_OPTIONS=--max_old_space_size=2048
|
||||
script:
|
||||
- grunt lint
|
||||
- npm test
|
||||
- grunt testnodeconsumer
|
||||
- grunt prod --msg="$COMPILE_MSG"
|
||||
- xvfb-run --server-args="-screen 0 1200x800x24" grunt testui
|
||||
before_deploy:
|
||||
- grunt exec:sitemap
|
||||
- grunt copy:ghPages
|
||||
deploy:
|
||||
- provider: pages
|
||||
edge: true
|
||||
token: $GITHUB_TOKEN
|
||||
local_dir: build/prod/
|
||||
target_branch: gh-pages
|
||||
on:
|
||||
repo: gchq/CyberChef
|
||||
branch: master
|
||||
- provider: releases
|
||||
edge: true
|
||||
token:
|
||||
secure: "HV1WSKv4l/0Y2bKKs1iBJocBcmLj08PCRUeEM/jTwA4jqJ8EiLHWiXtER/D5sEg2iibRVKd2OQjfrmS6bo4AiwdeVgAKmv0FtS2Jw+391N8Nd5AkEANHa5Om/IpHLTL2YRAjpJTsDpY72bMUTJIwjQA3TFJkgrpOw6KYfohOcgbxLpZ4XuNJRU3VL4Hsxdv5V9aOVmfFOmMOVPQlakXy7NgtW5POp1f2WJwgcZxylkR1CjwaqMyXmSoVl46pyH3tr5+dptsQoKSGdi6sIHGA60oDotFPcm+0ifa47wZw+vapuuDi4tdNxhrHGaDMG8xiE0WFDHwQUDlk2/+W7j9SEX0H3Em7us371JXRp56EDwEcDa34VpVkC6i8HGcHK55hnxVbMZXGf3qhOFD8wY7qMbjMRvIpucrMHBi86OfkDfv0vDj2LyvIl5APj/AX50BrE0tfH1MZbH26Jkx4NdlkcxQ14GumarmUqfmVvbX/fsoA6oUuAAE9ZgRRi3KHO4wci6KUcRfdm+XOeUkaBFsL86G3EEYIvrtBTuaypdz+Cx7nd1iPZyWMx5Y1gXnVzha4nBdV4+7l9JIsFggD8QVpw2uHXQiS1KXFjOeqA3DBD8tjMB7q26Fl2fD3jkOo4BTbQ2NrRIZUu/iL+fOmMPsyMt2qulB0yaSBCfkbEq8xrUA="
|
||||
file_glob: true
|
||||
file:
|
||||
- build/prod/*.zip
|
||||
- src/node/cjs.js
|
||||
on:
|
||||
repo: gchq/CyberChef
|
||||
tags: true
|
||||
- provider: npm
|
||||
edge: true
|
||||
email: "n1474335@gmail.com"
|
||||
api_token:
|
||||
secure: "UnDQL3Kh+GK2toL0TK3FObO0ujVssU3Eg4BBuYdjwLB81GhiGE5/DTh7THdZPOpbLo6wQeOwfZDuMeKC1OU+0Uf4NsdYFu1aq6xMO20qBQ4qUfgsyiK4Qgywj9gk0p1+OFZdGAZ/j1CNRAaF71XQIY6iV84c+SO4WoizXYrNT0Jh4sr2DA4/97G2xmJtPi0qOzYrJ09R56ZUozmqeik5G0pMRIuJRbpjS/7bZXV+N7WV0ombZc9RkUaetbabEVOLQ+Xx5YAIVq+VuEeMe9VBSnxY/FfCLmy1wJsjGzpLCyBI9nbrG4nw8Wgc2m8NfK9rcpIvBTGner9r2j60NVDkZ8kLZPrqXhq6AZMwa+oz6K5UQCqRo2RRQzSGwXxg67HY5Tcq+oNmjd+DqpPg4LZ3eGlluyP5XfG+hpSr9Ya4d8q8SrUWLxkoLHI6ZKMtoKFbTCSSQPiluW5hsZxjz3yDkkjsJw64M/EM8UyJrgaXqDklQu+7rBGKLfsK6os7RDiqjBWpQ7gwpo8HvY0O8yqEAabPz+QGkanpjcCOZCXFbSkzWxYy37RMAPu88iINVZVlZE4l+WJenCpZY95ueyy0mG9cyMSzVRPyX6A+/n4H6VMFPFjpGDLTD588ACEjY1lmHfS/eXwXJcgqPPD2gW0XdRdUheU/ssqlfCfGWQMTDXs="
|
||||
on:
|
||||
tags: true
|
||||
branch: master
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/83c143a6822e218d5b34
|
||||
on_success: change
|
||||
on_failure: always
|
||||
on_start: never
|
||||
75
CHANGELOG.md
75
CHANGELOG.md
@@ -1,7 +1,54 @@
|
||||
# Changelog
|
||||
|
||||
## Versioning
|
||||
|
||||
CyberChef uses the [semver](https://semver.org/) system to manage versioning: `<MAJOR>.<MINOR>.<PATCH>`.
|
||||
|
||||
- MAJOR version changes represent a significant change to the fundamental architecture of CyberChef and may (but don't always) make breaking changes that are not backwards compatible.
|
||||
- MINOR version changes usually mean the addition of new operations or reasonably significant new features.
|
||||
- PATCH versions are used for bug fixes and any other small tweaks that modify or improve existing capabilities.
|
||||
|
||||
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).
|
||||
|
||||
|
||||
## Details
|
||||
|
||||
### [9.30.0] - 2021-08-10
|
||||
- 'JA3S Fingerprint' operation added [@n1474335] | [289a417d]
|
||||
|
||||
### [9.29.0] - 2021-07-28
|
||||
- 'JA3 Fingerprint' operation added [@n1474335] | [9a33498]
|
||||
|
||||
### [9.28.0] - 2021-03-26
|
||||
- 'CBOR Encode' and 'CBOR Decode' operations added [@Danh4] | [#999]
|
||||
|
||||
### [9.27.0] - 2021-02-12
|
||||
- 'Fuzzy Match' operation added [@n1474335] | [8ad18b]
|
||||
|
||||
### [9.26.0] - 2021-02-11
|
||||
- 'Get Time' operation added [@n1073645] [@n1474335] | [#1045]
|
||||
|
||||
### [9.25.0] - 2021-02-11
|
||||
- 'Extract ID3' operation added [@n1073645] [@n1474335] | [#1006]
|
||||
|
||||
### [9.24.0] - 2021-02-02
|
||||
- 'SM3' hashing function added along with more configuration options for other hashing operations [@n1073645] [@n1474335] | [#1022]
|
||||
|
||||
### [9.23.0] - 2021-02-01
|
||||
- Various RSA operations added to encrypt, decrypt, sign, verify and generate keys [@mattnotmitt] [@GCHQ77703] | [#652]
|
||||
|
||||
### [9.22.0] - 2021-02-01
|
||||
- 'Unicode Text Format' operation added [@mattnotmitt] | [#1083]
|
||||
|
||||
### [9.21.0] - 2020-06-12
|
||||
- Node API now exports `magic` operation [@d98762625] | [#1049]
|
||||
|
||||
### [9.20.0] - 2020-03-27
|
||||
- 'Parse ObjectID Timestamp' operation added [@dmfj] | [#987]
|
||||
|
||||
### [9.19.0] - 2020-03-24
|
||||
- Improvements to the 'Magic' operation, allowing it to recognise more data formats and provide more accurate results [@n1073645] [@n1474335] | [#966] [b765534b](https://github.com/gchq/CyberChef/commit/b765534b8b2a0454a5132a0a52d1d8844bcbdaaa)
|
||||
|
||||
### [9.18.0] - 2020-03-13
|
||||
- 'Convert to NATO alphabet' operation added [@MarvinJWendt] | [#674]
|
||||
|
||||
@@ -218,6 +265,18 @@ All major and minor version changes will be documented in this file. Details of
|
||||
|
||||
|
||||
|
||||
[9.30.0]: https://github.com/gchq/CyberChef/releases/tag/v9.30.0
|
||||
[9.29.0]: https://github.com/gchq/CyberChef/releases/tag/v9.29.0
|
||||
[9.28.0]: https://github.com/gchq/CyberChef/releases/tag/v9.28.0
|
||||
[9.27.0]: https://github.com/gchq/CyberChef/releases/tag/v9.27.0
|
||||
[9.26.0]: https://github.com/gchq/CyberChef/releases/tag/v9.26.0
|
||||
[9.25.0]: https://github.com/gchq/CyberChef/releases/tag/v9.25.0
|
||||
[9.24.0]: https://github.com/gchq/CyberChef/releases/tag/v9.24.0
|
||||
[9.23.0]: https://github.com/gchq/CyberChef/releases/tag/v9.23.0
|
||||
[9.22.0]: https://github.com/gchq/CyberChef/releases/tag/v9.22.0
|
||||
[9.21.0]: https://github.com/gchq/CyberChef/releases/tag/v9.21.0
|
||||
[9.20.0]: https://github.com/gchq/CyberChef/releases/tag/v9.20.0
|
||||
[9.19.0]: https://github.com/gchq/CyberChef/releases/tag/v9.19.0
|
||||
[9.18.0]: https://github.com/gchq/CyberChef/releases/tag/v9.18.0
|
||||
[9.17.0]: https://github.com/gchq/CyberChef/releases/tag/v9.17.0
|
||||
[9.16.0]: https://github.com/gchq/CyberChef/releases/tag/v9.16.0
|
||||
@@ -314,6 +373,13 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[@Flavsditz]: https://github.com/Flavsditz
|
||||
[@pointhi]: https://github.com/pointhi
|
||||
[@MarvinJWendt]: https://github.com/MarvinJWendt
|
||||
[@dmfj]: https://github.com/dmfj
|
||||
[@mattnotmitt]: https://github.com/mattnotmitt
|
||||
[@Danh4]: https://github.com/Danh4
|
||||
|
||||
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7
|
||||
[9a33498]: https://github.com/gchq/CyberChef/commit/9a33498fed26a8df9c9f35f39a78a174bf50a513
|
||||
[289a417d]: https://github.com/gchq/CyberChef/commit/289a417d
|
||||
|
||||
[#95]: https://github.com/gchq/CyberChef/pull/299
|
||||
[#173]: https://github.com/gchq/CyberChef/pull/173
|
||||
@@ -375,6 +441,7 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[#625]: https://github.com/gchq/CyberChef/pull/625
|
||||
[#627]: https://github.com/gchq/CyberChef/pull/627
|
||||
[#632]: https://github.com/gchq/CyberChef/pull/632
|
||||
[#652]: https://github.com/gchq/CyberChef/pull/652
|
||||
[#653]: https://github.com/gchq/CyberChef/pull/653
|
||||
[#674]: https://github.com/gchq/CyberChef/pull/674
|
||||
[#683]: https://github.com/gchq/CyberChef/pull/683
|
||||
@@ -384,3 +451,11 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[#948]: https://github.com/gchq/CyberChef/pull/948
|
||||
[#952]: https://github.com/gchq/CyberChef/pull/952
|
||||
[#965]: https://github.com/gchq/CyberChef/pull/965
|
||||
[#966]: https://github.com/gchq/CyberChef/pull/966
|
||||
[#987]: https://github.com/gchq/CyberChef/pull/987
|
||||
[#999]: https://github.com/gchq/CyberChef/pull/999
|
||||
[#1006]: https://github.com/gchq/CyberChef/pull/1006
|
||||
[#1022]: https://github.com/gchq/CyberChef/pull/1022
|
||||
[#1045]: https://github.com/gchq/CyberChef/pull/1045
|
||||
[#1049]: https://github.com/gchq/CyberChef/pull/1049
|
||||
[#1083]: https://github.com/gchq/CyberChef/pull/1083
|
||||
31
Gruntfile.js
31
Gruntfile.js
@@ -37,7 +37,7 @@ module.exports = function (grunt) {
|
||||
]);
|
||||
|
||||
grunt.registerTask("configTests",
|
||||
"A task which configures config files in preparation for tests to be run. Use `npm tests` to run tests.",
|
||||
"A task which configures config files in preparation for tests to be run. Use `npm test` to run tests.",
|
||||
[
|
||||
"clean:config", "clean:nodeConfig", "exec:generateConfig", "exec:generateNodeIndex"
|
||||
]);
|
||||
@@ -78,7 +78,6 @@ module.exports = function (grunt) {
|
||||
grunt.loadNpmTasks("grunt-contrib-watch");
|
||||
grunt.loadNpmTasks("grunt-chmod");
|
||||
grunt.loadNpmTasks("grunt-exec");
|
||||
grunt.loadNpmTasks("grunt-accessibility");
|
||||
grunt.loadNpmTasks("grunt-concurrent");
|
||||
grunt.loadNpmTasks("grunt-contrib-connect");
|
||||
grunt.loadNpmTasks("grunt-zip");
|
||||
@@ -175,7 +174,7 @@ module.exports = function (grunt) {
|
||||
// previous one fails. & would coninue on a fail
|
||||
.join("&&")
|
||||
// Windows does not support \n properly
|
||||
.replace("\n", "\\n");
|
||||
.replace(/\n/g, "\\n");
|
||||
}
|
||||
|
||||
grunt.initConfig({
|
||||
@@ -197,18 +196,6 @@ module.exports = function (grunt) {
|
||||
node: ["src/node/**/*.{js,mjs}"],
|
||||
tests: ["tests/**/*.{js,mjs}"],
|
||||
},
|
||||
accessibility: {
|
||||
options: {
|
||||
accessibilityLevel: "WCAG2A",
|
||||
verbose: false,
|
||||
ignore: [
|
||||
"WCAG2A.Principle1.Guideline1_3.1_3_1.H42.2"
|
||||
]
|
||||
},
|
||||
test: {
|
||||
src: ["build/**/*.html"]
|
||||
}
|
||||
},
|
||||
webpack: {
|
||||
options: webpackConfig,
|
||||
web: webpackProdConf(),
|
||||
@@ -302,7 +289,7 @@ module.exports = function (grunt) {
|
||||
},
|
||||
files: [
|
||||
{
|
||||
src: "build/prod/index.html",
|
||||
src: ["build/prod/index.html"],
|
||||
dest: "build/prod/index.html"
|
||||
}
|
||||
]
|
||||
@@ -324,7 +311,7 @@ module.exports = function (grunt) {
|
||||
},
|
||||
files: [
|
||||
{
|
||||
src: "build/prod/index.html",
|
||||
src: ["build/prod/index.html"],
|
||||
dest: `build/prod/CyberChef_v${pkg.version}.html`
|
||||
}
|
||||
]
|
||||
@@ -424,6 +411,16 @@ module.exports = function (grunt) {
|
||||
]),
|
||||
stdout: false,
|
||||
},
|
||||
fixCryptoApiImports: {
|
||||
command: [
|
||||
`[[ "$OSTYPE" == "darwin"* ]]`,
|
||||
"&&",
|
||||
`find ./node_modules/crypto-api/src/ \\( -type d -name .git -prune \\) -o -type f -print0 | xargs -0 sed -i '' -e '/\\.mjs/!s/\\(from "\\.[^"]*\\)";/\\1.mjs";/g'`,
|
||||
"||",
|
||||
`find ./node_modules/crypto-api/src/ \\( -type d -name .git -prune \\) -o -type f -print0 | xargs -0 sed -i -e '/\\.mjs/!s/\\(from "\\.[^"]*\\)";/\\1.mjs";/g'`
|
||||
].join(" "),
|
||||
stdout: false
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# CyberChef
|
||||
|
||||
[](https://travis-ci.org/gchq/CyberChef)
|
||||
[](https://github.com/gchq/CyberChef/actions?query=workflow%3A%22Master+Build%2C+Test+%26+Deploy%22)
|
||||
[](https://lgtm.com/projects/g/gchq/CyberChef/context:javascript)
|
||||
[](https://david-dm.org/gchq/CyberChef)
|
||||
[](https://www.npmjs.com/package/cyberchef)
|
||||
[](https://github.com/gchq/CyberChef/blob/master/LICENSE)
|
||||
|
||||
12051
package-lock.json
generated
12051
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
177
package.json
177
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "9.18.1",
|
||||
"version": "9.30.0",
|
||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||
"author": "n1474335 <n1474335@gmail.com>",
|
||||
"homepage": "https://gchq.github.io/CyberChef",
|
||||
@@ -36,135 +36,146 @@
|
||||
"node >= 10"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.8.7",
|
||||
"@babel/plugin-transform-runtime": "^7.8.3",
|
||||
"@babel/preset-env": "^7.8.7",
|
||||
"autoprefixer": "^9.7.4",
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/plugin-transform-runtime": "^7.12.15",
|
||||
"@babel/preset-env": "^7.12.16",
|
||||
"autoprefixer": "^10.2.4",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.0",
|
||||
"chromedriver": "^80.0.1",
|
||||
"cli-progress": "^3.6.0",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"chromedriver": "^92.0.0",
|
||||
"cli-progress": "^3.9.0",
|
||||
"colors": "^1.4.0",
|
||||
"copy-webpack-plugin": "^5.1.1",
|
||||
"css-loader": "^3.4.2",
|
||||
"eslint": "^6.8.0",
|
||||
"exports-loader": "^0.7.0",
|
||||
"file-loader": "^6.0.0",
|
||||
"grunt": "^1.1.0",
|
||||
"grunt-accessibility": "~6.0.0",
|
||||
"copy-webpack-plugin": "^7.0.0",
|
||||
"css-loader": "^5.0.2",
|
||||
"eslint": "^7.20.0",
|
||||
"exports-loader": "^2.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"grunt": "^1.3.0",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
"grunt-concurrent": "^3.0.0",
|
||||
"grunt-contrib-clean": "~2.0.0",
|
||||
"grunt-contrib-connect": "^2.1.0",
|
||||
"grunt-contrib-connect": "^3.0.0",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-eslint": "^22.0.0",
|
||||
"grunt-eslint": "^23.0.0",
|
||||
"grunt-exec": "~3.0.0",
|
||||
"grunt-webpack": "^3.1.3",
|
||||
"grunt-webpack": "^4.0.2",
|
||||
"grunt-zip": "^0.18.2",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"imports-loader": "^0.8.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"nightwatch": "^1.3.4",
|
||||
"node-sass": "^4.13.1",
|
||||
"postcss-css-variables": "^0.14.0",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"prompt": "^1.0.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
"sitemap": "^6.1.0",
|
||||
"style-loader": "^1.1.3",
|
||||
"svg-url-loader": "^5.0.0",
|
||||
"url-loader": "^4.0.0",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-bundle-analyzer": "^3.6.1",
|
||||
"webpack-dev-server": "^3.10.3",
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"worker-loader": "^2.0.0"
|
||||
"html-webpack-plugin": "^5.1.0",
|
||||
"imports-loader": "^2.0.0",
|
||||
"mini-css-extract-plugin": "^1.3.7",
|
||||
"nightwatch": "^1.7.7",
|
||||
"node-sass": "^5.0.0",
|
||||
"postcss": "^8.2.6",
|
||||
"postcss-css-variables": "^0.17.0",
|
||||
"postcss-import": "^14.0.0",
|
||||
"postcss-loader": "^5.0.0",
|
||||
"prompt": "^1.1.0",
|
||||
"sass-loader": "^11.0.1",
|
||||
"sitemap": "^6.3.6",
|
||||
"style-loader": "^2.0.0",
|
||||
"svg-url-loader": "^7.1.1",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.22.0",
|
||||
"webpack-bundle-analyzer": "^4.4.0",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
"worker-loader": "^3.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.8.7",
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"arrive": "^2.4.1",
|
||||
"avsc": "^5.4.19",
|
||||
"avsc": "^5.5.3",
|
||||
"babel-plugin-transform-builtin-extend": "1.1.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"bignumber.js": "^9.0.1",
|
||||
"blakejs": "^1.1.0",
|
||||
"bootstrap": "4.4.1",
|
||||
"bootstrap": "4.6.0",
|
||||
"bootstrap-colorpicker": "^3.2.0",
|
||||
"bootstrap-material-design": "^4.1.2",
|
||||
"bson": "^4.0.3",
|
||||
"bootstrap-material-design": "^4.1.3",
|
||||
"browserify-zlib": "^0.2.0",
|
||||
"bson": "^4.2.2",
|
||||
"buffer": "^6.0.3",
|
||||
"cbor": "^5.0.1",
|
||||
"chi-squared": "^1.1.0",
|
||||
"codepage": "^1.14.0",
|
||||
"core-js": "^3.6.4",
|
||||
"core-js": "^3.8.3",
|
||||
"crypto-api": "^0.8.5",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"crypto-js": "^4.0.0",
|
||||
"ctph.js": "0.0.5",
|
||||
"d3": "^5.15.0",
|
||||
"d3": "^6.5.0",
|
||||
"d3-hexbin": "^0.2.2",
|
||||
"diff": "^4.0.2",
|
||||
"es6-promisify": "^6.1.0",
|
||||
"escodegen": "^1.14.1",
|
||||
"diff": "^5.0.0",
|
||||
"es6-promisify": "^6.1.1",
|
||||
"escodegen": "^2.0.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.18.1",
|
||||
"jimp": "^0.9.5",
|
||||
"jquery": "3.4.1",
|
||||
"file-saver": "^2.0.5",
|
||||
"flat": "^5.0.2",
|
||||
"geodesy": "1.1.3",
|
||||
"highlight.js": "^10.6.0",
|
||||
"jimp": "^0.16.1",
|
||||
"jquery": "3.5.1",
|
||||
"js-crc": "^0.2.0",
|
||||
"js-sha3": "^0.8.0",
|
||||
"jsesc": "^2.5.2",
|
||||
"jsonpath": "^1.0.2",
|
||||
"jsesc": "^3.0.2",
|
||||
"jsonpath": "^1.1.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jsqr": "^1.2.0",
|
||||
"jsrsasign": "8.0.12",
|
||||
"kbpgp": "2.1.13",
|
||||
"jsqr": "^1.3.1",
|
||||
"jsrsasign": "^10.3.0",
|
||||
"kbpgp": "2.1.15",
|
||||
"libbzip2-wasm": "0.0.4",
|
||||
"libyara-wasm": "^1.0.1",
|
||||
"lodash": "^4.17.15",
|
||||
"loglevel": "^1.6.7",
|
||||
"libyara-wasm": "^1.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"loglevel": "^1.7.1",
|
||||
"loglevel-message-prefix": "^3.0.0",
|
||||
"markdown-it": "^10.0.0",
|
||||
"moment": "^2.24.0",
|
||||
"moment-timezone": "^0.5.28",
|
||||
"markdown-it": "^12.0.4",
|
||||
"moment": "^2.29.1",
|
||||
"moment-timezone": "^0.5.33",
|
||||
"ngeohash": "^0.6.3",
|
||||
"node-forge": "^0.9.1",
|
||||
"node-forge": "^0.10.0",
|
||||
"node-md6": "^0.1.0",
|
||||
"nodom": "^2.4.0",
|
||||
"notepack.io": "^2.3.0",
|
||||
"nwmatcher": "^1.4.4",
|
||||
"otp": "^0.1.3",
|
||||
"otp": "0.1.3",
|
||||
"path": "^0.12.7",
|
||||
"popper.js": "^1.16.1",
|
||||
"process": "^0.11.10",
|
||||
"qr-image": "^3.2.0",
|
||||
"scryptsy": "^2.1.0",
|
||||
"snackbarjs": "^1.1.0",
|
||||
"sortablejs": "^1.10.2",
|
||||
"split.js": "^1.5.11",
|
||||
"sortablejs": "^1.13.0",
|
||||
"split.js": "^1.6.2",
|
||||
"ssdeep.js": "0.0.2",
|
||||
"tesseract.js": "^2.0.2",
|
||||
"ua-parser-js": "^0.7.21",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"terser": "^5.6.0",
|
||||
"tesseract.js": "2.1.1",
|
||||
"ua-parser-js": "^0.7.24",
|
||||
"unorm": "^1.6.0",
|
||||
"utf8": "^3.0.0",
|
||||
"vkbeautify": "^0.99.3",
|
||||
"xmldom": "^0.3.0",
|
||||
"xpath": "0.0.27",
|
||||
"xregexp": "^4.3.0",
|
||||
"xmldom": "^0.6.0",
|
||||
"xpath": "0.0.32",
|
||||
"xregexp": "^5.0.1",
|
||||
"zlibjs": "^0.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "grunt dev",
|
||||
"build": "grunt prod",
|
||||
"start": "npx grunt dev",
|
||||
"build": "npx grunt prod",
|
||||
"repl": "node src/node/repl.js",
|
||||
"test": "grunt configTests && node --experimental-modules --no-warnings --no-deprecation tests/node/index.mjs && node --experimental-modules --no-warnings --no-deprecation tests/operations/index.mjs",
|
||||
"test-node-consumer": "grunt testnodeconsumer",
|
||||
"testui": "grunt testui",
|
||||
"test": "npx grunt configTests && node --experimental-modules --no-warnings --no-deprecation tests/node/index.mjs && node --experimental-modules --no-warnings --no-deprecation tests/operations/index.mjs",
|
||||
"test-node-consumer": "npx grunt testnodeconsumer",
|
||||
"testui": "npx grunt testui",
|
||||
"testuidev": "npx nightwatch --env=dev",
|
||||
"lint": "grunt lint",
|
||||
"newop": "node --experimental-modules src/core/config/scripts/newOperation.mjs"
|
||||
"lint": "npx grunt lint",
|
||||
"postinstall": "npx grunt exec:fixCryptoApiImports",
|
||||
"newop": "node --experimental-modules src/core/config/scripts/newOperation.mjs",
|
||||
"getheapsize": "node -e 'console.log(`node heap limit = ${require(\"v8\").getHeapStatistics().heap_size_limit / (1024 * 1024)} Mb`)'",
|
||||
"setheapsize": "export NODE_OPTIONS=--max_old_space_size=2048"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ self.loadRequiredModules = function(recipeConfig) {
|
||||
if (!(module in OpModules)) {
|
||||
log.info(`Loading ${module} module`);
|
||||
self.sendStatusMessage(`Loading ${module} module`);
|
||||
self.importScripts(`${self.docURL}/modules/${module}.js`);
|
||||
self.importScripts(`${self.docURL}/modules/${module}.js`); // lgtm [js/client-side-unvalidated-url-redirection]
|
||||
self.sendStatusMessage("");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -207,7 +207,7 @@ class Dish {
|
||||
const data = new Uint8Array(this.value.slice(0, 2048)),
|
||||
types = detectFileType(data);
|
||||
|
||||
if (!types.length || !types[0].mime || !types[0].mime === "text/plain") {
|
||||
if (!types.length || !types[0].mime || !(types[0].mime === "text/plain")) {
|
||||
return null;
|
||||
} else {
|
||||
return types[0].mime;
|
||||
|
||||
@@ -46,7 +46,7 @@ class Recipe {
|
||||
module: OperationConfig[c.op].module,
|
||||
ingValues: c.args,
|
||||
breakpoint: c.breakpoint,
|
||||
disabled: c.disabled,
|
||||
disabled: c.disabled || c.op === "Comment",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -170,13 +170,18 @@ class Utils {
|
||||
*
|
||||
* @param {string} str - The input string to display.
|
||||
* @param {boolean} [preserveWs=false] - Whether or not to print whitespace.
|
||||
* @param {boolean} [onlyAscii=false] - Whether or not to replace non ASCII characters.
|
||||
* @returns {string}
|
||||
*/
|
||||
static printable(str, preserveWs=false) {
|
||||
static printable(str, preserveWs=false, onlyAscii=false) {
|
||||
if (isWebEnvironment() && window.app && !window.app.options.treatAsUtf8) {
|
||||
str = Utils.byteArrayToChars(Utils.strToByteArray(str));
|
||||
}
|
||||
|
||||
if (onlyAscii) {
|
||||
return str.replace(/[^\x20-\x7f]/g, ".");
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-misleading-character-class
|
||||
const re = /[\0-\x08\x0B-\x0C\x0E-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uD7FF\uE000-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/g;
|
||||
const wsRe = /[\x09-\x10\x0D\u2028\u2029]/g;
|
||||
@@ -704,8 +709,21 @@ class Utils {
|
||||
* Utils.stripHtmlTags("<div>Test</div>");
|
||||
*/
|
||||
static stripHtmlTags(htmlStr, removeScriptAndStyle=false) {
|
||||
/**
|
||||
* Recursively remove a pattern from a string until there are no more matches.
|
||||
* Avoids incomplete sanitization e.g. "aabcbc".replace(/abc/g, "") === "abc"
|
||||
*
|
||||
* @param {RegExp} pattern
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function recursiveRemove(pattern, str) {
|
||||
const newStr = str.replace(pattern, "");
|
||||
return newStr.length === str.length ? newStr : recursiveRemove(pattern, newStr);
|
||||
}
|
||||
|
||||
if (removeScriptAndStyle) {
|
||||
htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, "");
|
||||
htmlStr = recursiveRemove(/<(script|style)[^>]*>.*?<\/(script|style)>/gi, htmlStr);
|
||||
}
|
||||
return htmlStr.replace(/<[^>]+>/g, "");
|
||||
}
|
||||
@@ -729,11 +747,10 @@ class Utils {
|
||||
">": ">",
|
||||
'"': """,
|
||||
"'": "'", // ' not recommended because it's not in the HTML spec
|
||||
"/": "/", // forward slash is included as it helps end an HTML entity
|
||||
"`": "`"
|
||||
};
|
||||
|
||||
return str.replace(/[&<>"'/`]/g, function (match) {
|
||||
return str.replace(/[&<>"'`]/g, function (match) {
|
||||
return HTML_CHARS[match];
|
||||
});
|
||||
}
|
||||
@@ -878,7 +895,7 @@ class Utils {
|
||||
|
||||
while ((m = recipeRegex.exec(recipe))) {
|
||||
// Translate strings in args back to double-quotes
|
||||
args = m[2]
|
||||
args = m[2] // lgtm [js/incomplete-sanitization]
|
||||
.replace(/"/g, '\\"') // Escape double quotes
|
||||
.replace(/(^|,|{|:)'/g, '$1"') // Replace opening ' with "
|
||||
.replace(/([^\\]|(?:\\\\)+)'(,|:|}|$)/g, '$1"$2') // Replace closing ' with "
|
||||
@@ -1182,6 +1199,7 @@ class Utils {
|
||||
"CRLF": /\r\n/g,
|
||||
"Forward slash": /\//g,
|
||||
"Backslash": /\\/g,
|
||||
"0x with comma": /,?0x/g,
|
||||
"0x": /0x/g,
|
||||
"\\x": /\\x/g,
|
||||
"None": /\s+/g // Included here to remove whitespace when there shouldn't be any
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
"From Binary",
|
||||
"To Octal",
|
||||
"From Octal",
|
||||
"To Base64",
|
||||
"From Base64",
|
||||
"Show Base64 offsets",
|
||||
"To Base32",
|
||||
"From Base32",
|
||||
"To Base58",
|
||||
"From Base58",
|
||||
"To Base62",
|
||||
"From Base62",
|
||||
"To Base64",
|
||||
"From Base64",
|
||||
"Show Base64 offsets",
|
||||
"To Base85",
|
||||
"From Base85",
|
||||
"To Base",
|
||||
@@ -61,7 +61,9 @@
|
||||
"Parse TLV",
|
||||
"CSV to JSON",
|
||||
"JSON to CSV",
|
||||
"Avro to JSON"
|
||||
"Avro to JSON",
|
||||
"CBOR Encode",
|
||||
"CBOR Decode"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -134,6 +136,11 @@
|
||||
"PGP Verify",
|
||||
"PGP Encrypt and Sign",
|
||||
"PGP Decrypt and Verify",
|
||||
"Generate RSA Key Pair",
|
||||
"RSA Sign",
|
||||
"RSA Verify",
|
||||
"RSA Encrypt",
|
||||
"RSA Decrypt",
|
||||
"Parse SSH Host Key"
|
||||
]
|
||||
},
|
||||
@@ -186,6 +193,8 @@
|
||||
"Protobuf Decode",
|
||||
"VarInt Encode",
|
||||
"VarInt Decode",
|
||||
"JA3 Fingerprint",
|
||||
"JA3S Fingerprint",
|
||||
"Format MAC addresses",
|
||||
"Change IP format",
|
||||
"Group IP addresses",
|
||||
@@ -200,6 +209,7 @@
|
||||
"ops": [
|
||||
"Encode text",
|
||||
"Decode text",
|
||||
"Unicode Text Format",
|
||||
"Remove Diacritics",
|
||||
"Unescape Unicode Characters",
|
||||
"Convert to NATO alphabet"
|
||||
@@ -232,6 +242,7 @@
|
||||
"Pad lines",
|
||||
"Find / Replace",
|
||||
"Regular expression",
|
||||
"Fuzzy Match",
|
||||
"Offset checker",
|
||||
"Hamming Distance",
|
||||
"Convert distance",
|
||||
@@ -242,6 +253,7 @@
|
||||
"Convert co-ordinate format",
|
||||
"Show on map",
|
||||
"Parse UNIX file permissions",
|
||||
"Parse ObjectID timestamp",
|
||||
"Swap endianness",
|
||||
"Parse colour code",
|
||||
"Escape string",
|
||||
@@ -260,6 +272,7 @@
|
||||
"Windows Filetime to UNIX Timestamp",
|
||||
"UNIX Timestamp to Windows Filetime",
|
||||
"Extract dates",
|
||||
"Get Time",
|
||||
"Sleep"
|
||||
]
|
||||
},
|
||||
@@ -279,6 +292,7 @@
|
||||
"JPath expression",
|
||||
"CSS selector",
|
||||
"Extract EXIF",
|
||||
"Extract ID3",
|
||||
"Extract Files"
|
||||
]
|
||||
},
|
||||
@@ -312,6 +326,7 @@
|
||||
"SHA1",
|
||||
"SHA2",
|
||||
"SHA3",
|
||||
"SM3",
|
||||
"Keccak",
|
||||
"Shake",
|
||||
"RIPEMD",
|
||||
|
||||
@@ -42,13 +42,10 @@ for (const opObj in Ops) {
|
||||
outputType: op.presentType,
|
||||
flowControl: op.flowControl,
|
||||
manualBake: op.manualBake,
|
||||
args: op.args
|
||||
args: op.args,
|
||||
checks: op.checks
|
||||
};
|
||||
|
||||
if ("patterns" in op) {
|
||||
operationConfig[op.name].patterns = op.patterns;
|
||||
}
|
||||
|
||||
if (!(op.module in modules))
|
||||
modules[op.module] = {};
|
||||
modules[op.module][op.name] = opObj;
|
||||
|
||||
@@ -121,7 +121,7 @@ prompt.get(schema, (err, result) => {
|
||||
|
||||
const moduleName = result.opName.replace(/\w\S*/g, txt => {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1);
|
||||
}).replace(/[\s-()/./]/g, "");
|
||||
}).replace(/[\s-()./]/g, "");
|
||||
|
||||
|
||||
const template = `/**
|
||||
|
||||
@@ -17,7 +17,7 @@ class DishJSON extends DishType {
|
||||
*/
|
||||
static toArrayBuffer() {
|
||||
DishJSON.checkForValue(this.value);
|
||||
this.value = this.value ? Utils.strToArrayBuffer(JSON.stringify(this.value, null, 4)) : new ArrayBuffer;
|
||||
this.value = this.value !== undefined ? Utils.strToArrayBuffer(JSON.stringify(this.value, null, 4)) : new ArrayBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -148,4 +148,8 @@ export const ALPHABET_OPTIONS = [
|
||||
{name: "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r", value: "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"},
|
||||
{name: "ROT13: N-ZA-Mn-za-m0-9+/=", value: "N-ZA-Mn-za-m0-9+/="},
|
||||
{name: "UNIX crypt: ./0-9A-Za-z", value: "./0-9A-Za-z"},
|
||||
{name: "Atom128: /128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", value: "/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC"},
|
||||
{name: "Megan35: 3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", value: "3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5"},
|
||||
{name: "Zong22: ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", value: "ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2"},
|
||||
{name: "Hazz15: HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", value: "HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5"}
|
||||
];
|
||||
|
||||
@@ -32,9 +32,9 @@ export const ALPHABET_OPTIONS = [
|
||||
* @returns {string}
|
||||
*/
|
||||
export function alphabetName(alphabet) {
|
||||
alphabet = alphabet.replace("'", "'");
|
||||
alphabet = alphabet.replace("\"", """);
|
||||
alphabet = alphabet.replace("\\", "\");
|
||||
alphabet = alphabet.replace(/'/g, "'");
|
||||
alphabet = alphabet.replace(/"/g, """);
|
||||
alphabet = alphabet.replace(/\\/g, "\");
|
||||
let name;
|
||||
|
||||
ALPHABET_OPTIONS.forEach(function(a) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
|
||||
/**
|
||||
@@ -58,6 +59,9 @@ export function toBinary(data, delim="Space", padding=8) {
|
||||
* fromBinary("00010000:00100000:00110000", "Colon");
|
||||
*/
|
||||
export function fromBinary(data, delim="Space", byteLen=8) {
|
||||
if (byteLen < 1 || Math.round(byteLen) !== byteLen)
|
||||
throw new OperationError("Byte length must be a positive integer");
|
||||
|
||||
const delimRegex = Utils.regexRep(delim);
|
||||
data = data.replace(delimRegex, "");
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
const crypto = {};
|
||||
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
|
||||
|
||||
/* dojo-release-1.8.1/dojo/_base/lang.js.uncompressed.js */
|
||||
|
||||
@@ -86,8 +86,8 @@ export function getScatterValues(input, recordDelimiter, fieldDelimiter, columnH
|
||||
}
|
||||
|
||||
values = values.map(row => {
|
||||
const x = parseFloat(row[0], 10),
|
||||
y = parseFloat(row[1], 10);
|
||||
const x = parseFloat(row[0]),
|
||||
y = parseFloat(row[1]);
|
||||
|
||||
if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
|
||||
if (Number.isNaN(y)) throw new OperationError("Values must be numbers in base 10.");
|
||||
@@ -121,8 +121,8 @@ export function getScatterValuesWithColour(input, recordDelimiter, fieldDelimite
|
||||
}
|
||||
|
||||
values = values.map(row => {
|
||||
const x = parseFloat(row[0], 10),
|
||||
y = parseFloat(row[1], 10),
|
||||
const x = parseFloat(row[0]),
|
||||
y = parseFloat(row[1]),
|
||||
colour = row[2];
|
||||
|
||||
if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
|
||||
@@ -157,7 +157,7 @@ export function getSeriesValues(input, recordDelimiter, fieldDelimiter, columnHe
|
||||
values.forEach(row => {
|
||||
const serie = row[0],
|
||||
xVal = row[1],
|
||||
val = parseFloat(row[2], 10);
|
||||
val = parseFloat(row[2]);
|
||||
|
||||
if (Number.isNaN(val)) throw new OperationError("Values must be numbers in base 10.");
|
||||
|
||||
|
||||
9
src/core/lib/Crypt.mjs
Normal file
9
src/core/lib/Crypt.mjs
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Crypt resources.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
export const cryptNotice = "WARNING: Cryptographic operations in CyberChef should not be relied upon to provide security in any situation. No guarantee is offered for their correctness. We advise you not to use keys generated from CyberChef in operational contexts.";
|
||||
@@ -468,6 +468,34 @@ export const FILE_SIGNATURES = {
|
||||
],
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Targa Image",
|
||||
extension: "tga",
|
||||
mime: "image/x-targa",
|
||||
description: "",
|
||||
signature: [
|
||||
{ // This signature is not at the beginning of the file. The extractor works backwards.
|
||||
0: 0x54,
|
||||
1: 0x52,
|
||||
2: 0x55,
|
||||
3: 0x45,
|
||||
4: 0x56,
|
||||
5: 0x49,
|
||||
6: 0x53,
|
||||
7: 0x49,
|
||||
8: 0x4f,
|
||||
9: 0x4e,
|
||||
10: 0x2d,
|
||||
11: 0x58,
|
||||
12: 0x46,
|
||||
13: 0x49,
|
||||
14: 0x4c,
|
||||
15: 0x45,
|
||||
16: 0x2e
|
||||
}
|
||||
],
|
||||
extractor: extractTARGA
|
||||
}
|
||||
],
|
||||
"Video": [
|
||||
{ // Place before webm
|
||||
@@ -780,7 +808,7 @@ export const FILE_SIGNATURES = {
|
||||
1: 0xfb
|
||||
}
|
||||
],
|
||||
extractor: null
|
||||
extractor: extractMP3
|
||||
},
|
||||
{
|
||||
name: "MPEG-4 Part 14 audio",
|
||||
@@ -2169,14 +2197,14 @@ export const FILE_SIGNATURES = {
|
||||
mime: "application/octet-stream",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x6b, // keych
|
||||
0: 0x6b, // kych
|
||||
1: 0x79,
|
||||
2: 0x63,
|
||||
3: 0x68,
|
||||
4: 0x00,
|
||||
5: 0x01
|
||||
},
|
||||
extractor: null
|
||||
extractor: extractMacOSXKeychain
|
||||
},
|
||||
{
|
||||
name: "TCP Packet",
|
||||
@@ -2327,6 +2355,12 @@ export const FILE_SIGNATURES = {
|
||||
1: 0x03,
|
||||
2: 0xc6,
|
||||
3: 0x04
|
||||
},
|
||||
{
|
||||
0: 0x95,
|
||||
1: 0x05,
|
||||
2: 0x86,
|
||||
3: 0x04
|
||||
}
|
||||
],
|
||||
extractor: null
|
||||
@@ -3047,6 +3081,90 @@ export function extractICO(bytes, offset) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TARGA extractor.
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {number} offset
|
||||
*/
|
||||
export function extractTARGA(bytes, offset) {
|
||||
// Need all the bytes since we do not know how far up the image goes.
|
||||
const stream = new Stream(bytes);
|
||||
stream.moveTo(offset - 8);
|
||||
|
||||
// Read in the offsets of the possible areas.
|
||||
const extensionOffset = stream.readInt(4, "le");
|
||||
const developerOffset = stream.readInt(4, "le");
|
||||
|
||||
stream.moveBackwardsBy(8);
|
||||
|
||||
/**
|
||||
* Moves backwards in the stream until it meet bytes that are the same as the amount of bytes moved.
|
||||
*
|
||||
* @param {number} sizeOfSize
|
||||
* @param {number} maxSize
|
||||
*/
|
||||
function moveBackwardsUntilSize(maxSize, sizeOfSize) {
|
||||
for (let i = 0; i < maxSize; i++) {
|
||||
stream.moveBackwardsBy(1);
|
||||
|
||||
// Read in sizeOfSize amount of bytes in.
|
||||
const size = stream.readInt(sizeOfSize, "le") - 1;
|
||||
stream.moveBackwardsBy(sizeOfSize);
|
||||
|
||||
// If the size matches.
|
||||
if (size === i)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves backwards in the stream until we meet bytes(when calculated) that are the same as the amount of bytes moved.
|
||||
*/
|
||||
function moveBackwardsUntilImageSize() {
|
||||
stream.moveBackwardsBy(5);
|
||||
|
||||
// The documentation said that 0x100000 was the largest the file could be.
|
||||
for (let i = 0; i < 0x100000; i++) {
|
||||
|
||||
// (Height * Width * pixel depth in bits)/8
|
||||
const total = (stream.readInt(2, "le") * stream.readInt(2, "le") * stream.readInt(1))/8;
|
||||
if (total === i-1)
|
||||
break;
|
||||
|
||||
stream.moveBackwardsBy(6);
|
||||
}
|
||||
}
|
||||
|
||||
if (extensionOffset || developerOffset) {
|
||||
if (extensionOffset) {
|
||||
// Size is stored in two bytes hence the maximum is 0xffff.
|
||||
moveBackwardsUntilSize(0xffff, 2);
|
||||
|
||||
// Move to where we think the start of the file is.
|
||||
stream.moveBackwardsBy(extensionOffset);
|
||||
} else if (developerOffset) {
|
||||
// Size is stored in 4 bytes hence the maxiumum is 0xffffffff.
|
||||
moveBackwardsUntilSize(0xffffffff, 4);
|
||||
|
||||
// Size is stored in byte position 6 so have to move back.
|
||||
stream.moveBackwardsBy(6);
|
||||
|
||||
// Move to where we think the start of the file is.
|
||||
stream.moveBackwardsBy(developerOffset);
|
||||
}
|
||||
} else {
|
||||
// Move backwards until size === number of bytes passed.
|
||||
moveBackwardsUntilImageSize();
|
||||
|
||||
// Move backwards over the reaminder of the header + the 5 we borrowed in moveBackwardsUntilImageSize().
|
||||
stream.moveBackwardsBy(0xc+5);
|
||||
}
|
||||
|
||||
return stream.carve(stream.position, offset+0x12);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* WAV extractor.
|
||||
*
|
||||
@@ -3067,6 +3185,79 @@ export function extractWAV(bytes, offset) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MP3 extractor.
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {Number} offset
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function extractMP3(bytes, offset) {
|
||||
const stream = new Stream(bytes.slice(offset));
|
||||
|
||||
// Constants for flag byte.
|
||||
const bitRateIndexes = ["free", 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, "bad"];
|
||||
|
||||
const samplingRateFrequencyIndex = [44100, 48000, 32000, "reserved"];
|
||||
|
||||
// ID3 tag, move over it.
|
||||
if ((stream.getBytes(3).toString() === [0x49, 0x44, 0x33].toString())) {
|
||||
stream.moveTo(6);
|
||||
const tagSize = (stream.readInt(1) << 21) | (stream.readInt(1) << 14) | (stream.readInt(1) << 7) | stream.readInt(1);
|
||||
stream.moveForwardsBy(tagSize);
|
||||
} else {
|
||||
stream.moveTo(0);
|
||||
}
|
||||
|
||||
// Loop over all the frame headers in the file.
|
||||
while (stream.hasMore()) {
|
||||
|
||||
// If it has an old TAG frame at the end of it, fixed size, 128 bytes.
|
||||
if (stream.getBytes(3) === [0x54, 0x41, 0x47].toString()) {
|
||||
stream.moveForwardsBy(125);
|
||||
break;
|
||||
}
|
||||
|
||||
// If not start of frame.
|
||||
if (stream.getBytes(2).toString() !== [0xff, 0xfb].toString()) {
|
||||
stream.moveBackwardsBy(2);
|
||||
break;
|
||||
}
|
||||
|
||||
// Read flag byte.
|
||||
const flags = stream.readInt(1);
|
||||
|
||||
// Extract frame bit rate from flag byte.
|
||||
const bitRate = bitRateIndexes[flags >> 4];
|
||||
|
||||
// Extract frame sample rate from flag byte.
|
||||
const sampleRate = samplingRateFrequencyIndex[(flags & 0x0f) >> 2];
|
||||
|
||||
// Padding if the frame size is not a multiple of the bitrate.
|
||||
const padding = (flags & 0x02) >> 1;
|
||||
|
||||
// Things that are either not standard or undocumented.
|
||||
if (bitRate === "free" || bitRate === "bad" || sampleRate === "reserved") {
|
||||
stream.moveBackwardsBy(1);
|
||||
break;
|
||||
}
|
||||
|
||||
// Formula: FrameLength = (144 * BitRate / SampleRate ) + Padding
|
||||
const frameSize = Math.floor(((144 * bitRate) / sampleRate) + padding);
|
||||
|
||||
// If the next move goes past the end of the bytestream then extract the entire bytestream.
|
||||
// We assume complete frames in the above formula because there is no field that suggests otherwise.
|
||||
if ((stream.position + frameSize) > stream.length) {
|
||||
stream.moveTo(stream.length);
|
||||
break;
|
||||
} else {
|
||||
stream.moveForwardsBy(frameSize - 3);
|
||||
}
|
||||
}
|
||||
return stream.carve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* FLV extractor.
|
||||
*
|
||||
@@ -3221,6 +3412,26 @@ export function extractPListXML(bytes, offset) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MacOS X Keychain Extactor.
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {number} offset
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function extractMacOSXKeychain(bytes, offset) {
|
||||
const stream = new Stream(bytes.slice(offset));
|
||||
|
||||
// Move to size field.
|
||||
stream.moveTo(0x14);
|
||||
|
||||
// Move forwards by size.
|
||||
stream.moveForwardsBy(stream.readInt(4));
|
||||
|
||||
return stream.carve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* OLE2 extractor.
|
||||
*
|
||||
|
||||
@@ -213,7 +213,7 @@ function locatePotentialSig(buf, sig, offset) {
|
||||
export function isType(type, buf) {
|
||||
const types = detectFileType(buf);
|
||||
|
||||
if (!(types && types.length)) return false;
|
||||
if (!types.length) return false;
|
||||
|
||||
if (typeof type === "string") {
|
||||
return types.reduce((acc, t) => {
|
||||
|
||||
253
src/core/lib/FuzzyMatch.mjs
Normal file
253
src/core/lib/FuzzyMatch.mjs
Normal file
@@ -0,0 +1,253 @@
|
||||
/**
|
||||
* LICENSE
|
||||
*
|
||||
* This software is dual-licensed to the public domain and under the following
|
||||
* license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
* publish, and distribute this file as you see fit.
|
||||
*
|
||||
* VERSION
|
||||
* 0.1.0 (2016-03-28) Initial release
|
||||
*
|
||||
* AUTHOR
|
||||
* Forrest Smith
|
||||
*
|
||||
* CONTRIBUTORS
|
||||
* J<>rgen Tjern<72> - async helper
|
||||
* Anurag Awasthi - updated to 0.2.0
|
||||
*/
|
||||
|
||||
export const DEFAULT_WEIGHTS = {
|
||||
sequentialBonus: 15, // bonus for adjacent matches
|
||||
separatorBonus: 30, // bonus if match occurs after a separator
|
||||
camelBonus: 30, // bonus if match is uppercase and prev is lower
|
||||
firstLetterBonus: 15, // bonus if the first letter is matched
|
||||
|
||||
leadingLetterPenalty: -5, // penalty applied for every letter in str before the first match
|
||||
maxLeadingLetterPenalty: -15, // maximum penalty for leading letters
|
||||
unmatchedLetterPenalty: -1
|
||||
};
|
||||
|
||||
/**
|
||||
* Does a fuzzy search to find pattern inside a string.
|
||||
* @param {string} pattern pattern to search for
|
||||
* @param {string} str string which is being searched
|
||||
* @param {boolean} global whether to search for all matches or just one
|
||||
* @returns [boolean, number] a boolean which tells if pattern was
|
||||
* found or not and a search score
|
||||
*/
|
||||
export function fuzzyMatch(pattern, str, global=false, weights=DEFAULT_WEIGHTS) {
|
||||
const recursionCount = 0;
|
||||
const recursionLimit = 10;
|
||||
const matches = [];
|
||||
const maxMatches = 256;
|
||||
|
||||
if (!global) {
|
||||
return fuzzyMatchRecursive(
|
||||
pattern,
|
||||
str,
|
||||
0 /* patternCurIndex */,
|
||||
0 /* strCurrIndex */,
|
||||
null /* srcMatches */,
|
||||
matches,
|
||||
maxMatches,
|
||||
0 /* nextMatch */,
|
||||
recursionCount,
|
||||
recursionLimit,
|
||||
weights
|
||||
);
|
||||
}
|
||||
|
||||
// Return all matches
|
||||
let foundMatch = true,
|
||||
score,
|
||||
idxs,
|
||||
strCurrIndex = 0;
|
||||
const results = [];
|
||||
|
||||
while (foundMatch) {
|
||||
[foundMatch, score, idxs] = fuzzyMatchRecursive(
|
||||
pattern,
|
||||
str,
|
||||
0 /* patternCurIndex */,
|
||||
strCurrIndex,
|
||||
null /* srcMatches */,
|
||||
matches,
|
||||
maxMatches,
|
||||
0 /* nextMatch */,
|
||||
recursionCount,
|
||||
recursionLimit,
|
||||
weights
|
||||
);
|
||||
if (foundMatch) results.push([foundMatch, score, [...idxs]]);
|
||||
strCurrIndex = idxs[idxs.length - 1] + 1;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive helper function
|
||||
*/
|
||||
function fuzzyMatchRecursive(
|
||||
pattern,
|
||||
str,
|
||||
patternCurIndex,
|
||||
strCurrIndex,
|
||||
srcMatches,
|
||||
matches,
|
||||
maxMatches,
|
||||
nextMatch,
|
||||
recursionCount,
|
||||
recursionLimit,
|
||||
weights
|
||||
) {
|
||||
let outScore = 0;
|
||||
|
||||
// Return if recursion limit is reached.
|
||||
if (++recursionCount >= recursionLimit) {
|
||||
return [false, outScore, []];
|
||||
}
|
||||
|
||||
// Return if we reached ends of strings.
|
||||
if (patternCurIndex === pattern.length || strCurrIndex === str.length) {
|
||||
return [false, outScore, []];
|
||||
}
|
||||
|
||||
// Recursion params
|
||||
let recursiveMatch = false;
|
||||
let bestRecursiveMatches = [];
|
||||
let bestRecursiveScore = 0;
|
||||
|
||||
// Loop through pattern and str looking for a match.
|
||||
let firstMatch = true;
|
||||
while (patternCurIndex < pattern.length && strCurrIndex < str.length) {
|
||||
// Match found.
|
||||
if (
|
||||
pattern[patternCurIndex].toLowerCase() === str[strCurrIndex].toLowerCase()
|
||||
) {
|
||||
if (nextMatch >= maxMatches) {
|
||||
return [false, outScore, []];
|
||||
}
|
||||
|
||||
if (firstMatch && srcMatches) {
|
||||
matches = [...srcMatches];
|
||||
firstMatch = false;
|
||||
}
|
||||
|
||||
const [matched, recursiveScore, recursiveMatches] = fuzzyMatchRecursive(
|
||||
pattern,
|
||||
str,
|
||||
patternCurIndex,
|
||||
strCurrIndex + 1,
|
||||
matches,
|
||||
recursiveMatches,
|
||||
maxMatches,
|
||||
nextMatch,
|
||||
recursionCount,
|
||||
recursionLimit,
|
||||
weights
|
||||
);
|
||||
|
||||
if (matched) {
|
||||
// Pick best recursive score.
|
||||
if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
|
||||
bestRecursiveMatches = [...recursiveMatches];
|
||||
bestRecursiveScore = recursiveScore;
|
||||
}
|
||||
recursiveMatch = true;
|
||||
}
|
||||
|
||||
matches[nextMatch++] = strCurrIndex;
|
||||
++patternCurIndex;
|
||||
}
|
||||
++strCurrIndex;
|
||||
}
|
||||
|
||||
const matched = patternCurIndex === pattern.length;
|
||||
|
||||
if (matched) {
|
||||
outScore = 100;
|
||||
|
||||
// Apply leading letter penalty
|
||||
let penalty = weights.leadingLetterPenalty * matches[0];
|
||||
penalty =
|
||||
penalty < weights.maxLeadingLetterPenalty ?
|
||||
weights.maxLeadingLetterPenalty :
|
||||
penalty;
|
||||
outScore += penalty;
|
||||
|
||||
// Apply unmatched penalty
|
||||
const unmatched = str.length - nextMatch;
|
||||
outScore += weights.unmatchedLetterPenalty * unmatched;
|
||||
|
||||
// Apply ordering bonuses
|
||||
for (let i = 0; i < nextMatch; i++) {
|
||||
const currIdx = matches[i];
|
||||
|
||||
if (i > 0) {
|
||||
const prevIdx = matches[i - 1];
|
||||
if (currIdx === prevIdx + 1) {
|
||||
outScore += weights.sequentialBonus;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for bonuses based on neighbor character value.
|
||||
if (currIdx > 0) {
|
||||
// Camel case
|
||||
const neighbor = str[currIdx - 1];
|
||||
const curr = str[currIdx];
|
||||
if (
|
||||
neighbor !== neighbor.toUpperCase() &&
|
||||
curr !== curr.toLowerCase()
|
||||
) {
|
||||
outScore += weights.camelBonus;
|
||||
}
|
||||
const isNeighbourSeparator = neighbor === "_" || neighbor === " ";
|
||||
if (isNeighbourSeparator) {
|
||||
outScore += weights.separatorBonus;
|
||||
}
|
||||
} else {
|
||||
// First letter
|
||||
outScore += weights.firstLetterBonus;
|
||||
}
|
||||
}
|
||||
|
||||
// Return best result
|
||||
if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
|
||||
// Recursive score is better than "this"
|
||||
matches = bestRecursiveMatches;
|
||||
outScore = bestRecursiveScore;
|
||||
return [true, outScore, matches];
|
||||
} else if (matched) {
|
||||
// "this" score is better than recursive
|
||||
return [true, outScore, matches];
|
||||
} else {
|
||||
return [false, outScore, matches];
|
||||
}
|
||||
}
|
||||
return [false, outScore, matches];
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a list of match indexes into a list of match ranges
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @param [number] matches
|
||||
* @returns [[number]]
|
||||
*/
|
||||
export function calcMatchRanges(matches) {
|
||||
const ranges = [];
|
||||
let start = matches[0],
|
||||
curr = start;
|
||||
|
||||
matches.forEach(m => {
|
||||
if (m === curr || m === curr + 1) curr = m;
|
||||
else {
|
||||
ranges.push([start, curr - start + 1]);
|
||||
start = m;
|
||||
curr = m;
|
||||
}
|
||||
});
|
||||
|
||||
ranges.push([start, curr - start + 1]);
|
||||
return ranges;
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
|
||||
/**
|
||||
@@ -100,6 +101,9 @@ export function toHexFast(data) {
|
||||
* fromHex("0a:14:1e", "Colon");
|
||||
*/
|
||||
export function fromHex(data, delim="Auto", byteLen=2) {
|
||||
if (byteLen < 1 || Math.round(byteLen) !== byteLen)
|
||||
throw new OperationError("Byte length must be a positive integer");
|
||||
|
||||
if (delim !== "None") {
|
||||
const delimRegex = delim === "Auto" ? /[^a-f\d]|(0x)/gi : Utils.regexRep(delim);
|
||||
data = data.replace(delimRegex, "");
|
||||
|
||||
@@ -26,7 +26,7 @@ export function ipv4CidrRange(cidr, includeNetworkInfo, enumerateAddresses, allo
|
||||
let output = "";
|
||||
|
||||
if (cidrRange < 0 || cidrRange > 31) {
|
||||
return "IPv4 CIDR must be less than 32";
|
||||
throw new OperationError("IPv4 CIDR must be less than 32");
|
||||
}
|
||||
|
||||
const mask = ~(0xFFFFFFFF >>> cidrRange),
|
||||
@@ -64,7 +64,7 @@ export function ipv6CidrRange(cidr, includeNetworkInfo) {
|
||||
cidrRange = parseInt(cidr[cidr.length-1], 10);
|
||||
|
||||
if (cidrRange < 0 || cidrRange > 127) {
|
||||
return "IPv6 CIDR must be less than 128";
|
||||
throw new OperationError("IPv6 CIDR must be less than 128");
|
||||
}
|
||||
|
||||
const ip1 = new Array(8),
|
||||
@@ -211,7 +211,7 @@ export function ipv4ListedRange(match, includeNetworkInfo, enumerateAddresses, a
|
||||
const network = strToIpv4(ipv4CidrList[i].split("/")[0]);
|
||||
const cidrRange = parseInt(ipv4CidrList[i].split("/")[1], 10);
|
||||
if (cidrRange < 0 || cidrRange > 31) {
|
||||
return "IPv4 CIDR must be less than 32";
|
||||
throw new OperationError("IPv4 CIDR must be less than 32");
|
||||
}
|
||||
const mask = ~(0xFFFFFFFF >>> cidrRange),
|
||||
cidrIp1 = network & mask,
|
||||
@@ -254,7 +254,7 @@ export function ipv6ListedRange(match, includeNetworkInfo) {
|
||||
const cidrRange = parseInt(ipv6CidrList[i].split("/")[1], 10);
|
||||
|
||||
if (cidrRange < 0 || cidrRange > 127) {
|
||||
return "IPv6 CIDR must be less than 128";
|
||||
throw new OperationError("IPv6 CIDR must be less than 128");
|
||||
}
|
||||
|
||||
const cidrIp1 = new Array(8),
|
||||
|
||||
24
src/core/lib/JWT.mjs
Normal file
24
src/core/lib/JWT.mjs
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* JWT resources
|
||||
*
|
||||
* @author mt3571 [mt3571@protonmail.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* List of the JWT algorithms that can be used
|
||||
*/
|
||||
export const JWT_ALGORITHMS = [
|
||||
"HS256",
|
||||
"HS384",
|
||||
"HS512",
|
||||
"RS256",
|
||||
"RS384",
|
||||
"RS512",
|
||||
"ES256",
|
||||
"ES384",
|
||||
"ES512",
|
||||
"None"
|
||||
];
|
||||
@@ -2,7 +2,7 @@ import OperationConfig from "../config/OperationConfig.json";
|
||||
import Utils, { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import Recipe from "../Recipe.mjs";
|
||||
import Dish from "../Dish.mjs";
|
||||
import {detectFileType} from "./FileType.mjs";
|
||||
import {detectFileType, isType} from "./FileType.mjs";
|
||||
import chiSquared from "chi-squared";
|
||||
|
||||
/**
|
||||
@@ -19,31 +19,38 @@ class Magic {
|
||||
* Magic constructor.
|
||||
*
|
||||
* @param {ArrayBuffer} buf
|
||||
* @param {Object[]} [opPatterns]
|
||||
* @param {Object[]} [opCriteria]
|
||||
* @param {Object} [prevOp]
|
||||
*/
|
||||
constructor(buf, opPatterns) {
|
||||
constructor(buf, opCriteria=Magic._generateOpCriteria(), prevOp=null) {
|
||||
this.inputBuffer = new Uint8Array(buf);
|
||||
this.inputStr = Utils.arrayBufferToStr(buf);
|
||||
this.opPatterns = opPatterns || Magic._generateOpPatterns();
|
||||
this.opCriteria = opCriteria;
|
||||
this.prevOp = prevOp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds operations that claim to be able to decode the input based on regular
|
||||
* expression matches.
|
||||
* Finds operations that claim to be able to decode the input based on various criteria.
|
||||
*
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
findMatchingOps() {
|
||||
const matches = [];
|
||||
findMatchingInputOps() {
|
||||
const matches = [],
|
||||
inputEntropy = this.calcEntropy();
|
||||
|
||||
for (let i = 0; i < this.opPatterns.length; i++) {
|
||||
const pattern = this.opPatterns[i],
|
||||
regex = new RegExp(pattern.match, pattern.flags);
|
||||
this.opCriteria.forEach(check => {
|
||||
// If the input doesn't lie in the required entropy range, move on
|
||||
if (check.entropyRange &&
|
||||
(inputEntropy < check.entropyRange[0] ||
|
||||
inputEntropy > check.entropyRange[1]))
|
||||
return;
|
||||
// If the input doesn't match the pattern, move on
|
||||
if (check.pattern &&
|
||||
!check.pattern.test(this.inputStr))
|
||||
return;
|
||||
|
||||
if (regex.test(this.inputStr)) {
|
||||
matches.push(pattern);
|
||||
}
|
||||
}
|
||||
matches.push(check);
|
||||
});
|
||||
|
||||
return matches;
|
||||
}
|
||||
@@ -185,8 +192,10 @@ class Magic {
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
calcEntropy() {
|
||||
const prob = this._freqDist();
|
||||
calcEntropy(data=this.inputBuffer, standalone=false) {
|
||||
if (!standalone && this.inputEntropy) return this.inputEntropy;
|
||||
|
||||
const prob = this._freqDist(data, standalone);
|
||||
let entropy = 0,
|
||||
p;
|
||||
|
||||
@@ -195,6 +204,8 @@ class Magic {
|
||||
if (p === 0) continue;
|
||||
entropy += p * Math.log(p) / Math.log(2);
|
||||
}
|
||||
|
||||
if (!standalone) this.inputEntropy = -entropy;
|
||||
return -entropy;
|
||||
}
|
||||
|
||||
@@ -264,25 +275,59 @@ class Magic {
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the data passes output criteria for an operation check
|
||||
*
|
||||
* @param {ArrayBuffer} data
|
||||
* @param {Object} criteria
|
||||
* @returns {boolean}
|
||||
*/
|
||||
outputCheckPasses(data, criteria) {
|
||||
if (criteria.pattern) {
|
||||
const dataStr = Utils.arrayBufferToStr(data),
|
||||
regex = new RegExp(criteria.pattern, criteria.flags);
|
||||
if (!regex.test(dataStr))
|
||||
return false;
|
||||
}
|
||||
if (criteria.entropyRange) {
|
||||
const dataEntropy = this.calcEntropy(data, true);
|
||||
if (dataEntropy < criteria.entropyRange[0] || dataEntropy > criteria.entropyRange[1])
|
||||
return false;
|
||||
}
|
||||
if (criteria.mime &&
|
||||
!isType(criteria.mime, data))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Speculatively executes matching operations, recording metadata of each result.
|
||||
*
|
||||
* @param {number} [depth=0] - How many levels to try to execute
|
||||
* @param {boolean} [extLang=false] - Extensive language support (false = only check the most
|
||||
* common Internet languages)
|
||||
* common Internet languages)
|
||||
* @param {boolean} [intensive=false] - Run brute-forcing on each branch (significantly affects
|
||||
* performance)
|
||||
* performance)
|
||||
* @param {Object[]} [recipeConfig=[]] - The recipe configuration up to this point
|
||||
* @param {boolean} [useful=false] - Whether the current recipe should be scored highly
|
||||
* @param {string} [crib=null] - The regex crib provided by the user, for filtering the operation output
|
||||
* @param {string} [crib=null] - The regex crib provided by the user, for filtering the operation
|
||||
* output
|
||||
* @returns {Object[]} - A sorted list of the recipes most likely to result in correct decoding
|
||||
*/
|
||||
async speculativeExecution(depth=0, extLang=false, intensive=false, recipeConfig=[], useful=false, crib=null) {
|
||||
async speculativeExecution(
|
||||
depth=0,
|
||||
extLang=false,
|
||||
intensive=false,
|
||||
recipeConfig=[],
|
||||
useful=false,
|
||||
crib=null) {
|
||||
|
||||
// If we have reached the recursion depth, return
|
||||
if (depth < 0) return [];
|
||||
|
||||
// Find any operations that can be run on this data
|
||||
const matchingOps = this.findMatchingOps();
|
||||
|
||||
const matchingOps = this.findMatchingInputOps();
|
||||
let results = [];
|
||||
|
||||
// Record the properties of the current data
|
||||
@@ -308,17 +353,21 @@ class Magic {
|
||||
},
|
||||
output = await this._runRecipe([opConfig]);
|
||||
|
||||
// If the recipe is repeating and returning the same data, do not continue
|
||||
if (prevOp && op.op === prevOp.op && _buffersEqual(output, this.inputBuffer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the recipe returned an empty buffer, do not continue
|
||||
if (_buffersEqual(output, new ArrayBuffer())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const magic = new Magic(output, this.opPatterns),
|
||||
// If the recipe is repeating and returning the same data, do not continue
|
||||
if (prevOp && op.op === prevOp.op && _buffersEqual(output, this.inputBuffer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the output criteria for this op doesn't match the output, do not continue
|
||||
if (op.output && !this.outputCheckPasses(output, op.output))
|
||||
return;
|
||||
|
||||
const magic = new Magic(output, this.opCriteria, OperationConfig[op.op]),
|
||||
speculativeResults = await magic.speculativeExecution(
|
||||
depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful, crib);
|
||||
|
||||
@@ -330,7 +379,7 @@ class Magic {
|
||||
const bfEncodings = await this.bruteForce();
|
||||
|
||||
await Promise.all(bfEncodings.map(async enc => {
|
||||
const magic = new Magic(enc.data, this.opPatterns),
|
||||
const magic = new Magic(enc.data, this.opCriteria, undefined),
|
||||
bfResults = await magic.speculativeExecution(
|
||||
depth-1, extLang, false, [...recipeConfig, enc.conf], false, crib);
|
||||
|
||||
@@ -345,7 +394,8 @@ class Magic {
|
||||
r.languageScores[0].probability > 0 || // Some kind of language was found
|
||||
r.fileType || // A file was found
|
||||
r.isUTF8 || // UTF-8 was found
|
||||
r.matchingOps.length // A matching op was found
|
||||
r.matchingOps.length || // A matching op was found
|
||||
r.matchesCrib // The crib matches
|
||||
)
|
||||
);
|
||||
|
||||
@@ -376,9 +426,10 @@ class Magic {
|
||||
bScore += b.entropy;
|
||||
|
||||
// A result with no recipe but matching ops suggests there are better options
|
||||
if ((!a.recipe.length && a.matchingOps.length) &&
|
||||
b.recipe.length)
|
||||
if ((!a.recipe.length && a.matchingOps.length) && b.recipe.length)
|
||||
return 1;
|
||||
if ((!b.recipe.length && b.matchingOps.length) && a.recipe.length)
|
||||
return -1;
|
||||
|
||||
return aScore - bScore;
|
||||
});
|
||||
@@ -417,14 +468,16 @@ class Magic {
|
||||
* Calculates the number of times each byte appears in the input as a percentage
|
||||
*
|
||||
* @private
|
||||
* @param {ArrayBuffer} [data]
|
||||
* @param {boolean} [standalone]
|
||||
* @returns {number[]}
|
||||
*/
|
||||
_freqDist() {
|
||||
if (this.freqDist) return this.freqDist;
|
||||
_freqDist(data=this.inputBuffer, standalone=false) {
|
||||
if (!standalone && this.freqDist) return this.freqDist;
|
||||
|
||||
const len = this.inputBuffer.length;
|
||||
const len = data.length,
|
||||
counts = new Array(256).fill(0);
|
||||
let i = len;
|
||||
const counts = new Array(256).fill(0);
|
||||
|
||||
if (!len) {
|
||||
this.freqDist = counts;
|
||||
@@ -432,13 +485,15 @@ class Magic {
|
||||
}
|
||||
|
||||
while (i--) {
|
||||
counts[this.inputBuffer[i]]++;
|
||||
counts[data[i]]++;
|
||||
}
|
||||
|
||||
this.freqDist = counts.map(c => {
|
||||
const result = counts.map(c => {
|
||||
return c / len * 100;
|
||||
});
|
||||
return this.freqDist;
|
||||
|
||||
if (!standalone) this.freqDist = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,24 +502,29 @@ class Magic {
|
||||
* @private
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
static _generateOpPatterns() {
|
||||
const opPatterns = [];
|
||||
static _generateOpCriteria() {
|
||||
const opCriteria = [];
|
||||
|
||||
for (const op in OperationConfig) {
|
||||
if (!("patterns" in OperationConfig[op])) continue;
|
||||
if (!("checks" in OperationConfig[op]))
|
||||
continue;
|
||||
|
||||
OperationConfig[op].patterns.forEach(pattern => {
|
||||
opPatterns.push({
|
||||
OperationConfig[op].checks.forEach(check => {
|
||||
// Add to the opCriteria list.
|
||||
// Compile the regex here and cache the compiled version so we
|
||||
// don't have to keep calculating it.
|
||||
opCriteria.push({
|
||||
op: op,
|
||||
match: pattern.match,
|
||||
flags: pattern.flags,
|
||||
args: pattern.args,
|
||||
useful: pattern.useful || false
|
||||
pattern: check.pattern ? new RegExp(check.pattern, check.flags) : null,
|
||||
args: check.args,
|
||||
useful: check.useful,
|
||||
entropyRange: check.entropyRange,
|
||||
output: check.output
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return opPatterns;
|
||||
return opCriteria;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,7 @@ import { toHex, fromHex } from "./Hex.mjs";
|
||||
* @param {number} indent
|
||||
* @returns {string}
|
||||
*/
|
||||
export function formatDnStr (dnStr, indent) {
|
||||
export function formatDnStr(dnStr, indent) {
|
||||
const fields = dnStr.substr(1).replace(/([^\\])\//g, "$1$1/").split(/[^\\]\//);
|
||||
let output = "",
|
||||
maxKeyLen = 0,
|
||||
@@ -54,7 +54,7 @@ export function formatDnStr (dnStr, indent) {
|
||||
* @param {number} indent
|
||||
* @returns {string}
|
||||
*/
|
||||
export function formatByteStr (byteStr, length, indent) {
|
||||
export function formatByteStr(byteStr, length, indent) {
|
||||
byteStr = toHex(fromHex(byteStr), ":");
|
||||
length = length * 3;
|
||||
let output = "";
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import jsQR from "jsqr";
|
||||
import qr from "qr-image";
|
||||
import jimp from "jimp";
|
||||
import Utils from "../Utils.mjs";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Parses a QR code image from an image
|
||||
|
||||
17
src/core/lib/RSA.mjs
Normal file
17
src/core/lib/RSA.mjs
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* RSA resources.
|
||||
*
|
||||
* @author Matt C [me@mitt.dev]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import forge from "node-forge";
|
||||
|
||||
export const MD_ALGORITHMS = {
|
||||
"SHA-1": forge.md.sha1,
|
||||
"MD5": forge.md.md5,
|
||||
"SHA-256": forge.md.sha256,
|
||||
"SHA-384": forge.md.sha384,
|
||||
"SHA-512": forge.md.sha512,
|
||||
};
|
||||
@@ -177,7 +177,7 @@ export default class Stream {
|
||||
|
||||
// Get the skip table.
|
||||
const skiptable = preprocess(val, length);
|
||||
let found = true;
|
||||
let found;
|
||||
|
||||
while (this.position < this.length) {
|
||||
// Until we hit the final element of val in the stream.
|
||||
@@ -303,11 +303,13 @@ export default class Stream {
|
||||
/**
|
||||
* Returns a slice of the stream up to the current position.
|
||||
*
|
||||
* @param {number} [start=0]
|
||||
* @param {number} [finish=this.position]
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
carve() {
|
||||
if (this.bitPos > 0) this.position++;
|
||||
return this.bytes.slice(0, this.position);
|
||||
carve(start=0, finish=this.position) {
|
||||
if (this.bitPos > 0) finish++;
|
||||
return this.bytes.slice(start, finish);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,38 @@ class A1Z26CipherDecode extends Operation {
|
||||
value: DELIM_OPTIONS
|
||||
}
|
||||
];
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "^\\s*([12]?[0-9] )+[12]?[0-9]\\s*$",
|
||||
flags: "",
|
||||
args: ["Space"]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([12]?[0-9],)+[12]?[0-9]\\s*$",
|
||||
flags: "",
|
||||
args: ["Comma"]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([12]?[0-9];)+[12]?[0-9]\\s*$",
|
||||
flags: "",
|
||||
args: ["Semi-colon"]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([12]?[0-9]:)+[12]?[0-9]\\s*$",
|
||||
flags: "",
|
||||
args: ["Colon"]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([12]?[0-9]\\n)+[12]?[0-9]\\s*$",
|
||||
flags: "",
|
||||
args: ["Line feed"]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([12]?[0-9]\\r\\n)+[12]?[0-9]\\s*$",
|
||||
flags: "",
|
||||
args: ["CRLF"]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
@@ -41,8 +41,33 @@ class AESDecrypt extends Operation {
|
||||
},
|
||||
{
|
||||
"name": "Mode",
|
||||
"type": "option",
|
||||
"value": ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"]
|
||||
"type": "argSelector",
|
||||
"value": [
|
||||
{
|
||||
name: "CBC",
|
||||
off: [5, 6]
|
||||
},
|
||||
{
|
||||
name: "CFB",
|
||||
off: [5, 6]
|
||||
},
|
||||
{
|
||||
name: "OFB",
|
||||
off: [5, 6]
|
||||
},
|
||||
{
|
||||
name: "CTR",
|
||||
off: [5, 6]
|
||||
},
|
||||
{
|
||||
name: "GCM",
|
||||
on: [5, 6]
|
||||
},
|
||||
{
|
||||
name: "ECB",
|
||||
off: [5, 6]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Input",
|
||||
@@ -59,6 +84,12 @@ class AESDecrypt extends Operation {
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
},
|
||||
{
|
||||
"name": "Additional Authenticated Data",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -76,7 +107,8 @@ class AESDecrypt extends Operation {
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4],
|
||||
gcmTag = Utils.convertToByteString(args[5].string, args[5].option);
|
||||
gcmTag = Utils.convertToByteString(args[5].string, args[5].option),
|
||||
aad = Utils.convertToByteString(args[6].string, args[6].option);
|
||||
|
||||
if ([16, 24, 32].indexOf(key.length) < 0) {
|
||||
throw new OperationError(`Invalid key length: ${key.length} bytes
|
||||
@@ -92,7 +124,8 @@ 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.length === 0 ? "" : iv,
|
||||
tag: gcmTag
|
||||
tag: mode === "GCM" ? gcmTag : undefined,
|
||||
additionalData: mode === "GCM" ? aad : undefined
|
||||
});
|
||||
decipher.update(forge.util.createBuffer(input));
|
||||
const result = decipher.finish();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
@@ -41,8 +41,33 @@ class AESEncrypt extends Operation {
|
||||
},
|
||||
{
|
||||
"name": "Mode",
|
||||
"type": "option",
|
||||
"value": ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"]
|
||||
"type": "argSelector",
|
||||
"value": [
|
||||
{
|
||||
name: "CBC",
|
||||
off: [5]
|
||||
},
|
||||
{
|
||||
name: "CFB",
|
||||
off: [5]
|
||||
},
|
||||
{
|
||||
name: "OFB",
|
||||
off: [5]
|
||||
},
|
||||
{
|
||||
name: "CTR",
|
||||
off: [5]
|
||||
},
|
||||
{
|
||||
name: "GCM",
|
||||
on: [5]
|
||||
},
|
||||
{
|
||||
name: "ECB",
|
||||
off: [5]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Input",
|
||||
@@ -53,6 +78,12 @@ class AESEncrypt extends Operation {
|
||||
"name": "Output",
|
||||
"type": "option",
|
||||
"value": ["Hex", "Raw"]
|
||||
},
|
||||
{
|
||||
"name": "Additional Authenticated Data",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -69,7 +100,8 @@ class AESEncrypt extends Operation {
|
||||
iv = Utils.convertToByteString(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4];
|
||||
outputType = args[4],
|
||||
aad = Utils.convertToByteString(args[5].string, args[5].option);
|
||||
|
||||
if ([16, 24, 32].indexOf(key.length) < 0) {
|
||||
throw new OperationError(`Invalid key length: ${key.length} bytes
|
||||
@@ -83,7 +115,10 @@ The following algorithms will be used based on the size of the key:
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
const cipher = forge.cipher.createCipher("AES-" + mode, key);
|
||||
cipher.start({iv: iv});
|
||||
cipher.start({
|
||||
iv: iv,
|
||||
additionalData: mode === "GCM" ? aad : undefined
|
||||
});
|
||||
cipher.update(forge.util.createBuffer(input));
|
||||
cipher.finish();
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Add Text To Image operation
|
||||
|
||||
@@ -44,6 +44,48 @@ class BaconCipherDecode extends Operation {
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "^\\s*([01]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Standard (I=J and U=V)", "0/1", false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([01]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Standard (I=J and U=V)", "0/1", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([AB]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Standard (I=J and U=V)", "A/B", false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([AB]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Standard (I=J and U=V)", "A/B", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([01]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Complete", "0/1", false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([01]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Complete", "0/1", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([AB]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Complete", "A/B", false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([AB]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Complete", "A/B", true]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { Blowfish } from "../lib/Blowfish.mjs";
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { Blowfish } from "../lib/Blowfish.mjs";
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import jimp from "jimp";
|
||||
import { gaussianBlur } from "../lib/ImageManipulation.mjs";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Blur Image operation
|
||||
|
||||
@@ -33,9 +33,9 @@ class Bzip2Decompress extends Operation {
|
||||
value: false
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
"match": "^\\x42\\x5a\\x68",
|
||||
"pattern": "^\\x42\\x5a\\x68",
|
||||
"flags": "",
|
||||
"args": []
|
||||
}
|
||||
|
||||
41
src/core/operations/CBORDecode.mjs
Normal file
41
src/core/operations/CBORDecode.mjs
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @author Danh4 [dan.h4@ncsc.gov.uk]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Cbor from "cbor";
|
||||
|
||||
/**
|
||||
* CBOR Decode operation
|
||||
*/
|
||||
class CBORDecode extends Operation {
|
||||
|
||||
/**
|
||||
* CBORDecode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "CBOR Decode";
|
||||
this.module = "Serialise";
|
||||
this.description = "Concise Binary Object Representation (CBOR) is a binary data serialization format loosely based on JSON. Like JSON it allows the transmission of data objects that contain name–value pairs, but in a more concise manner. This increases processing and transfer speeds at the cost of human readability. It is defined in IETF RFC 8949.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/CBOR";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "JSON";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {JSON}
|
||||
*/
|
||||
run(input, args) {
|
||||
return Cbor.decodeFirstSync(Buffer.from(input).toString("hex"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default CBORDecode;
|
||||
41
src/core/operations/CBOREncode.mjs
Normal file
41
src/core/operations/CBOREncode.mjs
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @author Danh4 [dan.h4@ncsc.gov.uk]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Cbor from "cbor";
|
||||
|
||||
/**
|
||||
* CBOR Encode operation
|
||||
*/
|
||||
class CBOREncode extends Operation {
|
||||
|
||||
/**
|
||||
* CBOREncode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "CBOR Encode";
|
||||
this.module = "Serialise";
|
||||
this.description = "Concise Binary Object Representation (CBOR) is a binary data serialization format loosely based on JSON. Like JSON it allows the transmission of data objects that contain name–value pairs, but in a more concise manner. This increases processing and transfer speeds at the cost of human readability. It is defined in IETF RFC 8949.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/CBOR";
|
||||
this.inputType = "JSON";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {JSON} input
|
||||
* @param {Object[]} args
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
run(input, args) {
|
||||
return new Uint8Array(Cbor.encodeCanonical(input)).buffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default CBOREncode;
|
||||
@@ -21,7 +21,7 @@ class CTPH extends Operation {
|
||||
this.name = "CTPH";
|
||||
this.module = "Crypto";
|
||||
this.description = "Context Triggered Piecewise Hashing, also called Fuzzy Hashing, can match inputs that have homologies. Such inputs have sequences of identical bytes in the same order, although bytes in between these sequences may be different in both content and length.<br><br>CTPH was originally based on the work of Dr. Andrew Tridgell and a spam email detector called SpamSum. This method was adapted by Jesse Kornblum and published at the DFRWS conference in 2006 in a paper 'Identifying Almost Identical Files Using Context Triggered Piecewise Hashing'.";
|
||||
this.infoURL = "https://forensicswiki.org/wiki/Context_Triggered_Piecewise_Hashing";
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Context_Triggered_Piecewise_Hashing";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
|
||||
@@ -125,7 +125,8 @@ class Colossus extends Operation {
|
||||
},
|
||||
{
|
||||
name: "R1-Negate",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "R1-Counter",
|
||||
@@ -164,7 +165,8 @@ class Colossus extends Operation {
|
||||
},
|
||||
{
|
||||
name: "R2-Negate",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "R2-Counter",
|
||||
@@ -203,7 +205,8 @@ class Colossus extends Operation {
|
||||
},
|
||||
{
|
||||
name: "R3-Negate",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "R3-Counter",
|
||||
@@ -212,7 +215,8 @@ class Colossus extends Operation {
|
||||
},
|
||||
{
|
||||
name: "Negate All",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "K Rack: Addition",
|
||||
@@ -220,23 +224,28 @@ class Colossus extends Operation {
|
||||
},
|
||||
{
|
||||
name: "Add-Q1",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Add-Q2",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Add-Q3",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Add-Q4",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Add-Q5",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Add-Equals",
|
||||
@@ -246,11 +255,13 @@ class Colossus extends Operation {
|
||||
},
|
||||
{
|
||||
name: "Add-Counter1",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Add Negate All",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Total Motor",
|
||||
|
||||
@@ -24,7 +24,7 @@ class CompareCTPHHashes extends Operation {
|
||||
this.name = "Compare CTPH hashes";
|
||||
this.module = "Crypto";
|
||||
this.description = "Compares two Context Triggered Piecewise Hashing (CTPH) fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
|
||||
this.infoURL = "https://forensicswiki.org/wiki/Context_Triggered_Piecewise_Hashing";
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Context_Triggered_Piecewise_Hashing";
|
||||
this.inputType = "string";
|
||||
this.outputType = "Number";
|
||||
this.args = [
|
||||
|
||||
@@ -24,7 +24,7 @@ class CompareSSDEEPHashes extends Operation {
|
||||
this.name = "Compare SSDEEP hashes";
|
||||
this.module = "Crypto";
|
||||
this.description = "Compares two SSDEEP fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
|
||||
this.infoURL = "https://forensicswiki.org/wiki/Ssdeep";
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Ssdeep";
|
||||
this.inputType = "string";
|
||||
this.outputType = "Number";
|
||||
this.args = [
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Contain Image operation
|
||||
|
||||
@@ -8,7 +8,8 @@ import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Convert Image Format operation
|
||||
@@ -88,7 +89,7 @@ class ConvertImageFormat extends Operation {
|
||||
"Sub": jimp.PNG_FILTER_SUB,
|
||||
"Up": jimp.PNG_FILTER_UP,
|
||||
"Average": jimp.PNG_FILTER_AVERAGE,
|
||||
"Paeth": jimp.PNG_FILTER_PATH // Incorrect spelling in Jimp library
|
||||
"Paeth": jimp.PNG_FILTER_PATH
|
||||
};
|
||||
|
||||
const mime = formatMap[format];
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Cover Image operation
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Crop Image operation
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
|
||||
/**
|
||||
* DES Decrypt operation
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
|
||||
/**
|
||||
* DES Encrypt operation
|
||||
|
||||
@@ -24,6 +24,13 @@ class DechunkHTTPResponse extends Operation {
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "^[0-9A-F]+\r\n",
|
||||
flags: "i",
|
||||
args: []
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,13 @@ class DecodeNetBIOSName extends Operation {
|
||||
"value": 65
|
||||
}
|
||||
];
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "^\\s*\\S{32}$",
|
||||
flags: "",
|
||||
args: [65]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,17 @@ class DefangIPAddresses extends Operation {
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "^\\s*(([0-9]{1,3}\\.){3}[0-9]{1,3}|([0-9a-f]{4}:){7}[0-9a-f]{4})\\s*$",
|
||||
flags: "i",
|
||||
args: [],
|
||||
output: {
|
||||
pattern: "^\\s*(([0-9]{1,3}\\[\\.\\]){3}[0-9]{1,3}|([0-9a-f]{4}\\[\\:\\]){7}[0-9a-f]{4})\\s*$",
|
||||
flags: "i"
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
|
||||
/**
|
||||
* Derive PBKDF2 key operation
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Image Dither operation
|
||||
|
||||
@@ -44,22 +44,22 @@ class EscapeUnicodeCharacters extends Operation {
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "\\\\u(?:[\\da-f]{4,6})",
|
||||
pattern: "\\\\u(?:[\\da-f]{4,6})",
|
||||
flags: "i",
|
||||
args: ["\\u"]
|
||||
},
|
||||
{
|
||||
match: "%u(?:[\\da-f]{4,6})",
|
||||
pattern: "%u(?:[\\da-f]{4,6})",
|
||||
flags: "i",
|
||||
args: ["%u"]
|
||||
},
|
||||
{
|
||||
match: "U\\+(?:[\\da-f]{4,6})",
|
||||
pattern: "U\\+(?:[\\da-f]{4,6})",
|
||||
flags: "i",
|
||||
args: ["U+"]
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class ExtractDomains extends Operation {
|
||||
{
|
||||
"name": "Display total",
|
||||
"type": "boolean",
|
||||
"value": "Extract.DISPLAY_TOTAL"
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ class ExtractEmailAddresses extends Operation {
|
||||
*/
|
||||
run(input, args) {
|
||||
const displayTotal = args[0],
|
||||
// email regex from: https://www.regextester.com/98066
|
||||
regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/ig;
|
||||
// email regex from: https://www.regextester.com/98066
|
||||
regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}\])/ig;
|
||||
return search(input, regex, null, displayTotal);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,25 @@ class ExtractFiles extends Operation {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// Get the first extension for each signature that can be extracted
|
||||
let supportedExts = Object.keys(FILE_SIGNATURES).map(cat => {
|
||||
return FILE_SIGNATURES[cat]
|
||||
.filter(sig => sig.extractor)
|
||||
.map(sig => sig.extension.toUpperCase());
|
||||
});
|
||||
|
||||
// Flatten categories and remove duplicates
|
||||
supportedExts = [].concat(...supportedExts).unique();
|
||||
|
||||
this.name = "Extract Files";
|
||||
this.module = "Default";
|
||||
this.description = "Performs file carving to attempt to extract files from the input.<br><br>This operation is currently capable of carving out the following formats:<ul><li>JPG</li><li>EXE</li><li>ZIP</li><li>PDF</li><li>PNG</li><li>BMP</li><li>FLV</li><li>RTF</li><li>DOCX, PPTX, XLSX</li><li>EPUB</li><li>GZIP</li><li>ZLIB</li><li>ELF, BIN, AXF, O, PRX, SO</li></ul>";
|
||||
this.infoURL = "https://forensicswiki.org/wiki/File_Carving";
|
||||
this.description = `Performs file carving to attempt to extract files from the input.<br><br>This operation is currently capable of carving out the following formats:
|
||||
<ul>
|
||||
<li>
|
||||
${supportedExts.join("</li><li>")}
|
||||
</li>
|
||||
</ul>`;
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=File_Carving";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "List<File>";
|
||||
this.presentType = "html";
|
||||
@@ -38,7 +53,7 @@ class ExtractFiles extends Operation {
|
||||
{
|
||||
name: "Ignore failed extractions",
|
||||
type: "boolean",
|
||||
value: "true"
|
||||
value: true
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
324
src/core/operations/ExtractID3.mjs
Normal file
324
src/core/operations/ExtractID3.mjs
Normal file
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* @author n1073645 [n1073645@gmail.com]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Extract ID3 operation
|
||||
*/
|
||||
class ExtractID3 extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractID3 constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract ID3";
|
||||
this.module = "Default";
|
||||
this.description = "This operation extracts ID3 metadata from an MP3 file.<br><br>ID3 is a metadata container most often used in conjunction with the MP3 audio file format. It allows information such as the title, artist, album, track number, and other information about the file to be stored in the file itself.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/ID3";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "JSON";
|
||||
this.presentType = "html";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {JSON}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
|
||||
/**
|
||||
* Extracts the ID3 header fields.
|
||||
*/
|
||||
function extractHeader() {
|
||||
if (!Array.from(input.slice(0, 3)).equals([0x49, 0x44, 0x33]))
|
||||
throw new OperationError("No valid ID3 header.");
|
||||
|
||||
const header = {
|
||||
"Type": "ID3",
|
||||
// Tag version
|
||||
"Version": input[3].toString() + "." + input[4].toString(),
|
||||
// Header version
|
||||
"Flags": input[5].toString()
|
||||
};
|
||||
|
||||
input = input.slice(6);
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the size fields to a single integer.
|
||||
*
|
||||
* @param {number} num
|
||||
* @returns {string}
|
||||
*/
|
||||
function readSize(num) {
|
||||
let result = 0;
|
||||
|
||||
// The sizes are 7 bit numbers stored in 8 bit locations
|
||||
for (let i = (num) * 7; i; i -= 7) {
|
||||
result = (result << i) | input[0];
|
||||
input = input.slice(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads frame header based on ID.
|
||||
*
|
||||
* @param {string} id
|
||||
* @returns {number}
|
||||
*/
|
||||
function readFrame(id) {
|
||||
const frame = {};
|
||||
|
||||
// Size of frame
|
||||
const size = readSize(4);
|
||||
frame.Size = size.toString();
|
||||
frame.Description = FRAME_DESCRIPTIONS[id];
|
||||
input = input.slice(2);
|
||||
|
||||
// Read data from frame
|
||||
let data = "";
|
||||
for (let i = 1; i < size; i++)
|
||||
data += String.fromCharCode(input[i]);
|
||||
frame.Data = data;
|
||||
|
||||
// Move to next Frame
|
||||
input = input.slice(size);
|
||||
|
||||
return [frame, size];
|
||||
}
|
||||
|
||||
const result = extractHeader();
|
||||
|
||||
const headerTagSize = readSize(4);
|
||||
result.Size = headerTagSize.toString();
|
||||
|
||||
const tags = {};
|
||||
let pos = 10;
|
||||
|
||||
// While the current element is in the header
|
||||
while (pos < headerTagSize) {
|
||||
|
||||
// Frame Identifier of frame
|
||||
let id = String.fromCharCode(input[0]) + String.fromCharCode(input[1]) + String.fromCharCode(input[2]);
|
||||
input = input.slice(3);
|
||||
|
||||
// If the next character is non-zero it is an identifier
|
||||
if (input[0] !== 0) {
|
||||
id += String.fromCharCode(input[0]);
|
||||
}
|
||||
input = input.slice(1);
|
||||
|
||||
if (id in FRAME_DESCRIPTIONS) {
|
||||
const [frame, size] = readFrame(id);
|
||||
tags[id] = frame;
|
||||
pos += 10 + size;
|
||||
} else if (id === "\x00\x00\x00") { // end of header
|
||||
break;
|
||||
} else {
|
||||
throw new OperationError("Unknown Frame Identifier: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
result.Tags = tags;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the extracted data in a more accessible format for web apps.
|
||||
* @param {JSON} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data || !Object.prototype.hasOwnProperty.call(data, "Tags"))
|
||||
return JSON.stringify(data, null, 4);
|
||||
|
||||
let output = `<table class="table table-hover table-sm table-bordered table-nonfluid">
|
||||
<tr><th>Tag</th><th>Description</th><th>Data</th></tr>`;
|
||||
|
||||
for (const tagID in data.Tags) {
|
||||
const description = data.Tags[tagID].Description,
|
||||
contents = data.Tags[tagID].Data;
|
||||
output += `<tr><td>${tagID}</td><td>${Utils.escapeHtml(description)}</td><td>${Utils.escapeHtml(contents)}</td></tr>`;
|
||||
}
|
||||
output += "</table>";
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Borrowed from https://github.com/aadsm/jsmediatags
|
||||
const FRAME_DESCRIPTIONS = {
|
||||
// v2.2
|
||||
"BUF": "Recommended buffer size",
|
||||
"CNT": "Play counter",
|
||||
"COM": "Comments",
|
||||
"CRA": "Audio encryption",
|
||||
"CRM": "Encrypted meta frame",
|
||||
"ETC": "Event timing codes",
|
||||
"EQU": "Equalization",
|
||||
"GEO": "General encapsulated object",
|
||||
"IPL": "Involved people list",
|
||||
"LNK": "Linked information",
|
||||
"MCI": "Music CD Identifier",
|
||||
"MLL": "MPEG location lookup table",
|
||||
"PIC": "Attached picture",
|
||||
"POP": "Popularimeter",
|
||||
"REV": "Reverb",
|
||||
"RVA": "Relative volume adjustment",
|
||||
"SLT": "Synchronized lyric/text",
|
||||
"STC": "Synced tempo codes",
|
||||
"TAL": "Album/Movie/Show title",
|
||||
"TBP": "BPM (Beats Per Minute)",
|
||||
"TCM": "Composer",
|
||||
"TCO": "Content type",
|
||||
"TCR": "Copyright message",
|
||||
"TDA": "Date",
|
||||
"TDY": "Playlist delay",
|
||||
"TEN": "Encoded by",
|
||||
"TFT": "File type",
|
||||
"TIM": "Time",
|
||||
"TKE": "Initial key",
|
||||
"TLA": "Language(s)",
|
||||
"TLE": "Length",
|
||||
"TMT": "Media type",
|
||||
"TOA": "Original artist(s)/performer(s)",
|
||||
"TOF": "Original filename",
|
||||
"TOL": "Original Lyricist(s)/text writer(s)",
|
||||
"TOR": "Original release year",
|
||||
"TOT": "Original album/Movie/Show title",
|
||||
"TP1": "Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group",
|
||||
"TP2": "Band/Orchestra/Accompaniment",
|
||||
"TP3": "Conductor/Performer refinement",
|
||||
"TP4": "Interpreted, remixed, or otherwise modified by",
|
||||
"TPA": "Part of a set",
|
||||
"TPB": "Publisher",
|
||||
"TRC": "ISRC (International Standard Recording Code)",
|
||||
"TRD": "Recording dates",
|
||||
"TRK": "Track number/Position in set",
|
||||
"TSI": "Size",
|
||||
"TSS": "Software/hardware and settings used for encoding",
|
||||
"TT1": "Content group description",
|
||||
"TT2": "Title/Songname/Content description",
|
||||
"TT3": "Subtitle/Description refinement",
|
||||
"TXT": "Lyricist/text writer",
|
||||
"TXX": "User defined text information frame",
|
||||
"TYE": "Year",
|
||||
"UFI": "Unique file identifier",
|
||||
"ULT": "Unsychronized lyric/text transcription",
|
||||
"WAF": "Official audio file webpage",
|
||||
"WAR": "Official artist/performer webpage",
|
||||
"WAS": "Official audio source webpage",
|
||||
"WCM": "Commercial information",
|
||||
"WCP": "Copyright/Legal information",
|
||||
"WPB": "Publishers official webpage",
|
||||
"WXX": "User defined URL link frame",
|
||||
// v2.3
|
||||
"AENC": "Audio encryption",
|
||||
"APIC": "Attached picture",
|
||||
"ASPI": "Audio seek point index",
|
||||
"CHAP": "Chapter",
|
||||
"CTOC": "Table of contents",
|
||||
"COMM": "Comments",
|
||||
"COMR": "Commercial frame",
|
||||
"ENCR": "Encryption method registration",
|
||||
"EQU2": "Equalisation (2)",
|
||||
"EQUA": "Equalization",
|
||||
"ETCO": "Event timing codes",
|
||||
"GEOB": "General encapsulated object",
|
||||
"GRID": "Group identification registration",
|
||||
"IPLS": "Involved people list",
|
||||
"LINK": "Linked information",
|
||||
"MCDI": "Music CD identifier",
|
||||
"MLLT": "MPEG location lookup table",
|
||||
"OWNE": "Ownership frame",
|
||||
"PRIV": "Private frame",
|
||||
"PCNT": "Play counter",
|
||||
"POPM": "Popularimeter",
|
||||
"POSS": "Position synchronisation frame",
|
||||
"RBUF": "Recommended buffer size",
|
||||
"RVA2": "Relative volume adjustment (2)",
|
||||
"RVAD": "Relative volume adjustment",
|
||||
"RVRB": "Reverb",
|
||||
"SEEK": "Seek frame",
|
||||
"SYLT": "Synchronized lyric/text",
|
||||
"SYTC": "Synchronized tempo codes",
|
||||
"TALB": "Album/Movie/Show title",
|
||||
"TBPM": "BPM (beats per minute)",
|
||||
"TCOM": "Composer",
|
||||
"TCON": "Content type",
|
||||
"TCOP": "Copyright message",
|
||||
"TDAT": "Date",
|
||||
"TDLY": "Playlist delay",
|
||||
"TDRC": "Recording time",
|
||||
"TDRL": "Release time",
|
||||
"TDTG": "Tagging time",
|
||||
"TENC": "Encoded by",
|
||||
"TEXT": "Lyricist/Text writer",
|
||||
"TFLT": "File type",
|
||||
"TIME": "Time",
|
||||
"TIPL": "Involved people list",
|
||||
"TIT1": "Content group description",
|
||||
"TIT2": "Title/songname/content description",
|
||||
"TIT3": "Subtitle/Description refinement",
|
||||
"TKEY": "Initial key",
|
||||
"TLAN": "Language(s)",
|
||||
"TLEN": "Length",
|
||||
"TMCL": "Musician credits list",
|
||||
"TMED": "Media type",
|
||||
"TMOO": "Mood",
|
||||
"TOAL": "Original album/movie/show title",
|
||||
"TOFN": "Original filename",
|
||||
"TOLY": "Original lyricist(s)/text writer(s)",
|
||||
"TOPE": "Original artist(s)/performer(s)",
|
||||
"TORY": "Original release year",
|
||||
"TOWN": "File owner/licensee",
|
||||
"TPE1": "Lead performer(s)/Soloist(s)",
|
||||
"TPE2": "Band/orchestra/accompaniment",
|
||||
"TPE3": "Conductor/performer refinement",
|
||||
"TPE4": "Interpreted, remixed, or otherwise modified by",
|
||||
"TPOS": "Part of a set",
|
||||
"TPRO": "Produced notice",
|
||||
"TPUB": "Publisher",
|
||||
"TRCK": "Track number/Position in set",
|
||||
"TRDA": "Recording dates",
|
||||
"TRSN": "Internet radio station name",
|
||||
"TRSO": "Internet radio station owner",
|
||||
"TSOA": "Album sort order",
|
||||
"TSOP": "Performer sort order",
|
||||
"TSOT": "Title sort order",
|
||||
"TSIZ": "Size",
|
||||
"TSRC": "ISRC (international standard recording code)",
|
||||
"TSSE": "Software/Hardware and settings used for encoding",
|
||||
"TSST": "Set subtitle",
|
||||
"TYER": "Year",
|
||||
"TXXX": "User defined text information frame",
|
||||
"UFID": "Unique file identifier",
|
||||
"USER": "Terms of use",
|
||||
"USLT": "Unsychronized lyric/text transcription",
|
||||
"WCOM": "Commercial information",
|
||||
"WCOP": "Copyright/Legal information",
|
||||
"WOAF": "Official audio file webpage",
|
||||
"WOAR": "Official artist/performer webpage",
|
||||
"WOAS": "Official audio source webpage",
|
||||
"WORS": "Official internet radio station homepage",
|
||||
"WPAY": "Payment",
|
||||
"WPUB": "Publishers official webpage",
|
||||
"WXXX": "User defined URL link frame"
|
||||
};
|
||||
|
||||
export default ExtractID3;
|
||||
@@ -9,7 +9,8 @@ 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";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Extract LSB operation
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
import {RGBA_DELIM_OPTIONS} from "../lib/Delim.mjs";
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Flip Image operation
|
||||
|
||||
@@ -30,7 +30,12 @@ class FrequencyDistribution extends Operation {
|
||||
{
|
||||
"name": "Show 0%s",
|
||||
"type": "boolean",
|
||||
"value": "Entropy.FREQ_ZEROS"
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "Show ASCII",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -76,14 +81,14 @@ class FrequencyDistribution extends Operation {
|
||||
* @returns {html}
|
||||
*/
|
||||
present(freq, args) {
|
||||
const showZeroes = args[0];
|
||||
const [showZeroes, showAscii] = args;
|
||||
|
||||
// Print
|
||||
let output = `<canvas id='chart-area'></canvas><br>
|
||||
Total data length: ${freq.dataLength}
|
||||
Number of bytes represented: ${freq.bytesRepresented}
|
||||
Number of bytes not represented: ${256 - freq.bytesRepresented}
|
||||
|
||||
Byte Percentage
|
||||
<script>
|
||||
var canvas = document.getElementById("chart-area"),
|
||||
parentRect = canvas.parentNode.getBoundingClientRect(),
|
||||
@@ -93,16 +98,32 @@ Byte Percentage
|
||||
canvas.height = parentRect.height * 0.9;
|
||||
|
||||
CanvasComponents.drawBarChart(canvas, scores, "Byte", "Frequency %", 16, 6);
|
||||
</script>`;
|
||||
</script>
|
||||
<table class="table table-hover table-sm">
|
||||
<tr><th>Byte</th>${showAscii ? "<th>ASCII</th>" : ""}<th>Percentage</th><th></th></tr>`;
|
||||
|
||||
for (let i = 0; i < 256; i++) {
|
||||
if (freq.distribution[i] || showZeroes) {
|
||||
output += " " + Utils.hex(i, 2) + " (" +
|
||||
(freq.percentages[i].toFixed(2).replace(".00", "") + "%)").padEnd(8, " ") +
|
||||
Array(Math.ceil(freq.percentages[i])+1).join("|") + "\n";
|
||||
let c = "";
|
||||
if (showAscii) {
|
||||
if (i <= 32) {
|
||||
c = String.fromCharCode(0x2400 + i);
|
||||
} else if (i === 127) {
|
||||
c = String.fromCharCode(0x2421);
|
||||
} else {
|
||||
c = String.fromCharCode(i);
|
||||
}
|
||||
}
|
||||
const bite = `<td>${Utils.hex(i, 2)}</td>`,
|
||||
ascii = showAscii ? `<td>${c}</td>` : "",
|
||||
percentage = `<td>${(freq.percentages[i].toFixed(2).replace(".00", "") + "%").padEnd(8, " ")}</td>`,
|
||||
bars = `<td>${Array(Math.ceil(freq.percentages[i])+1).join("|")}</td>`;
|
||||
|
||||
output += `<tr>${bite}${ascii}${percentage}${bars}</tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
output += "</table>";
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,9 +49,9 @@ class FromBCD extends Operation {
|
||||
"value": FORMAT
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:\\d{4} ){3,}\\d{4}$",
|
||||
pattern: "^(?:\\d{4} ){3,}\\d{4}$",
|
||||
flags: "",
|
||||
args: ["8 4 2 1", true, false, "Nibbles"]
|
||||
},
|
||||
@@ -95,7 +95,7 @@ class FromBCD extends Operation {
|
||||
if (!packed) {
|
||||
// Discard each high nibble
|
||||
for (let i = 0; i < nibbles.length; i++) {
|
||||
nibbles.splice(i, 1);
|
||||
nibbles.splice(i, 1); // lgtm [js/loop-iteration-skipped-due-to-shifting]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class FromBase extends Operation {
|
||||
}
|
||||
|
||||
const number = input.replace(/\s/g, "").split(".");
|
||||
let result = new BigNumber(number[0], radix) || 0;
|
||||
let result = new BigNumber(number[0], radix);
|
||||
|
||||
if (number.length === 1) return result;
|
||||
|
||||
|
||||
@@ -36,12 +36,12 @@ class FromBase32 extends Operation {
|
||||
value: true
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:[A-Z2-7]{8})+(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}={1})?$",
|
||||
pattern: "^(?:[A-Z2-7]{8})+(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}={1})?$",
|
||||
flags: "",
|
||||
args: ["A-Z2-7=", false]
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -84,10 +84,10 @@ class FromBase32 extends Operation {
|
||||
chr5 = ((enc7 & 7) << 5) | enc8;
|
||||
|
||||
output.push(chr1);
|
||||
if (enc2 & 3 !== 0 || enc3 !== 32) output.push(chr2);
|
||||
if (enc4 & 15 !== 0 || enc5 !== 32) output.push(chr3);
|
||||
if (enc5 & 1 !== 0 || enc6 !== 32) output.push(chr4);
|
||||
if (enc7 & 7 !== 0 || enc8 !== 32) output.push(chr5);
|
||||
if ((enc2 & 3) !== 0 || enc3 !== 32) output.push(chr2);
|
||||
if ((enc4 & 15) !== 0 || enc5 !== 32) output.push(chr3);
|
||||
if ((enc5 & 1) !== 0 || enc6 !== 32) output.push(chr4);
|
||||
if ((enc7 & 7) !== 0 || enc8 !== 32) output.push(chr5);
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
@@ -38,14 +38,14 @@ class FromBase58 extends Operation {
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^[1-9A-HJ-NP-Za-km-z]{20,}$",
|
||||
pattern: "^[1-9A-HJ-NP-Za-km-z]{20,}$",
|
||||
flags: "",
|
||||
args: ["123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", false]
|
||||
},
|
||||
{
|
||||
match: "^[1-9A-HJ-NP-Za-km-z]{20,}$",
|
||||
pattern: "^[1-9A-HJ-NP-Za-km-z]{20,}$",
|
||||
flags: "",
|
||||
args: ["rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz", false]
|
||||
},
|
||||
|
||||
@@ -36,72 +36,92 @@ class FromBase64 extends Operation {
|
||||
value: true
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^\\s*(?:[A-Z\\d+/]{4})+(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
pattern: "^\\s*(?:[A-Z\\d+/]{4})+(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9+/=", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*[A-Z\\d\\-_]{20,}\\s*$",
|
||||
pattern: "^\\s*[A-Z\\d\\-_]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9-_", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*(?:[A-Z\\d+\\-]{4}){5,}(?:[A-Z\\d+\\-]{2}==|[A-Z\\d+\\-]{3}=)?\\s*$",
|
||||
pattern: "^\\s*(?:[A-Z\\d+\\-]{4}){5,}(?:[A-Z\\d+\\-]{2}==|[A-Z\\d+\\-]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9+\\-=", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*(?:[A-Z\\d./]{4}){5,}(?:[A-Z\\d./]{2}==|[A-Z\\d./]{3}=)?\\s*$",
|
||||
pattern: "^\\s*(?:[A-Z\\d./]{4}){5,}(?:[A-Z\\d./]{2}==|[A-Z\\d./]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["./0-9A-Za-z=", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*[A-Z\\d_.]{20,}\\s*$",
|
||||
pattern: "^\\s*[A-Z\\d_.]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9_.", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*(?:[A-Z\\d._]{4}){5,}(?:[A-Z\\d._]{2}--|[A-Z\\d._]{3}-)?\\s*$",
|
||||
pattern: "^\\s*(?:[A-Z\\d._]{4}){5,}(?:[A-Z\\d._]{2}--|[A-Z\\d._]{3}-)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9._-", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
pattern: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["0-9a-zA-Z+/=", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
pattern: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["0-9A-Za-z+/=", true]
|
||||
},
|
||||
{
|
||||
match: "^[ !\"#$%&'()*+,\\-./\\d:;<=>?@A-Z[\\\\\\]^_]{20,}$",
|
||||
pattern: "^[ !\"#$%&'()*+,\\-./\\d:;<=>?@A-Z[\\\\\\]^_]{20,}$",
|
||||
flags: "",
|
||||
args: [" -_", false]
|
||||
},
|
||||
{
|
||||
match: "^\\s*[A-Z\\d+\\-]{20,}\\s*$",
|
||||
pattern: "^\\s*[A-Z\\d+\\-]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["+\\-0-9A-Za-z", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*[!\"#$%&'()*+,\\-0-689@A-NP-VX-Z[`a-fh-mp-r]{20,}\\s*$",
|
||||
pattern: "^\\s*[!\"#$%&'()*+,\\-0-689@A-NP-VX-Z[`a-fh-mp-r]{20,}\\s*$",
|
||||
flags: "",
|
||||
args: ["!-,-0-689@A-NP-VX-Z[`a-fh-mp-r", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*(?:[N-ZA-M\\d+/]{4}){5,}(?:[N-ZA-M\\d+/]{2}==|[N-ZA-M\\d+/]{3}=)?\\s*$",
|
||||
pattern: "^\\s*(?:[N-ZA-M\\d+/]{4}){5,}(?:[N-ZA-M\\d+/]{2}==|[N-ZA-M\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["N-ZA-Mn-za-m0-9+/=", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*[A-Z\\d./]{20,}\\s*$",
|
||||
pattern: "^\\s*[A-Z\\d./]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["./0-9A-Za-z", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}CC|[A-Z=\\d\\+/]{3}C)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}22|[A-Z=\\d\\+/]{3}2)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", true]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -31,41 +31,47 @@ class FromBinary extends Operation {
|
||||
"name": "Delimiter",
|
||||
"type": "option",
|
||||
"value": BIN_DELIM_OPTIONS
|
||||
},
|
||||
{
|
||||
"name": "Byte Length",
|
||||
"type": "number",
|
||||
"value": 8,
|
||||
"min": 1
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:[01]{8})+$",
|
||||
pattern: "^(?:[01]{8})+$",
|
||||
flags: "",
|
||||
args: ["None"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[01]{8})(?: [01]{8})*$",
|
||||
pattern: "^(?:[01]{8})(?: [01]{8})*$",
|
||||
flags: "",
|
||||
args: ["Space"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[01]{8})(?:,[01]{8})*$",
|
||||
pattern: "^(?:[01]{8})(?:,[01]{8})*$",
|
||||
flags: "",
|
||||
args: ["Comma"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[01]{8})(?:;[01]{8})*$",
|
||||
pattern: "^(?:[01]{8})(?:;[01]{8})*$",
|
||||
flags: "",
|
||||
args: ["Semi-colon"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[01]{8})(?::[01]{8})*$",
|
||||
pattern: "^(?:[01]{8})(?::[01]{8})*$",
|
||||
flags: "",
|
||||
args: ["Colon"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[01]{8})(?:\\n[01]{8})*$",
|
||||
pattern: "^(?:[01]{8})(?:\\n[01]{8})*$",
|
||||
flags: "",
|
||||
args: ["Line feed"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[01]{8})(?:\\r\\n[01]{8})*$",
|
||||
pattern: "^(?:[01]{8})(?:\\r\\n[01]{8})*$",
|
||||
flags: "",
|
||||
args: ["CRLF"]
|
||||
},
|
||||
@@ -78,7 +84,8 @@ class FromBinary extends Operation {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
return fromBinary(input, args[0]);
|
||||
const byteLen = args[1] ? args[1] : 8;
|
||||
return fromBinary(input, args[0], byteLen);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,37 +36,37 @@ class FromDecimal extends Operation {
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?: (?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
pattern: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?: (?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
flags: "",
|
||||
args: ["Space", false]
|
||||
},
|
||||
{
|
||||
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:,(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
pattern: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:,(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
flags: "",
|
||||
args: ["Comma", false]
|
||||
},
|
||||
{
|
||||
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:;(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
pattern: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:;(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
flags: "",
|
||||
args: ["Semi-colon", false]
|
||||
},
|
||||
{
|
||||
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?::(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
pattern: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?::(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
flags: "",
|
||||
args: ["Colon", false]
|
||||
},
|
||||
{
|
||||
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:\\n(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
pattern: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:\\n(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
flags: "",
|
||||
args: ["Line feed", false]
|
||||
},
|
||||
{
|
||||
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:\\r\\n(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
pattern: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:\\r\\n(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
flags: "",
|
||||
args: ["CRLF", false]
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,12 @@ class FromHTMLEntity extends Operation {
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "&(?:#\\d{2,3}|#x[\\da-f]{2}|[a-z]{2,6});",
|
||||
pattern: "&(?:#\\d{2,3}|#x[\\da-f]{2}|[a-z]{2,6});",
|
||||
flags: "i",
|
||||
args: []
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -32,49 +32,54 @@ class FromHex extends Operation {
|
||||
value: FROM_HEX_DELIM_OPTIONS
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:[\\dA-F]{2})+$",
|
||||
pattern: "^(?:[\\dA-F]{2})+$",
|
||||
flags: "i",
|
||||
args: ["None"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?: [\\dA-F]{2})*$",
|
||||
pattern: "^[\\dA-F]{2}(?: [\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["Space"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?:,[\\dA-F]{2})*$",
|
||||
pattern: "^[\\dA-F]{2}(?:,[\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["Comma"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?:;[\\dA-F]{2})*$",
|
||||
pattern: "^[\\dA-F]{2}(?:;[\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["Semi-colon"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?::[\\dA-F]{2})*$",
|
||||
pattern: "^[\\dA-F]{2}(?::[\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["Colon"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?:\\n[\\dA-F]{2})*$",
|
||||
pattern: "^[\\dA-F]{2}(?:\\n[\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["Line feed"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?:\\r\\n[\\dA-F]{2})*$",
|
||||
pattern: "^[\\dA-F]{2}(?:\\r\\n[\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["CRLF"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?:0x[\\dA-F]{2})*$",
|
||||
pattern: "^(?:0x[\\dA-F]{2})+$",
|
||||
flags: "i",
|
||||
args: ["0x"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?:\\\\x[\\dA-F]{2})*$",
|
||||
pattern: "^0x[\\dA-F]{2}(?:,0x[\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["0x with comma"]
|
||||
},
|
||||
{
|
||||
pattern: "^(?:\\\\x[\\dA-F]{2})+$",
|
||||
flags: "i",
|
||||
args: ["\\x"]
|
||||
}
|
||||
|
||||
@@ -26,6 +26,13 @@ class FromHexContent extends Operation {
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [];
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "\\|([\\da-f]{2} ?)+\\|",
|
||||
flags: "i",
|
||||
args: []
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,9 +27,9 @@ class FromHexdump extends Operation {
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:(?:[\\dA-F]{4,16}h?:?)?[ \\t]*((?:[\\dA-F]{2} ){1,8}(?:[ \\t]|[\\dA-F]{2}-)(?:[\\dA-F]{2} ){1,8}|(?:[\\dA-F]{4} )*[\\dA-F]{4}|(?:[\\dA-F]{2} )*[\\dA-F]{2})[^\\n]*\\n?){2,}$",
|
||||
pattern: "^(?:(?:[\\dA-F]{4,16}h?:?)?[ \\t]*((?:[\\dA-F]{2} ){1,8}(?:[ \\t]|[\\dA-F]{2}-)(?:[\\dA-F]{2} ){1,8}|(?:[\\dA-F]{4} )*[\\dA-F]{4}|(?:[\\dA-F]{2} )*[\\dA-F]{2})[^\\n]*\\n?){2,}$",
|
||||
flags: "i",
|
||||
args: []
|
||||
},
|
||||
|
||||
@@ -37,12 +37,12 @@ class FromMorseCode extends Operation {
|
||||
"value": WORD_DELIM_OPTIONS
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "(?:^[-. \\n]{5,}$|^[_. \\n]{5,}$|^(?:dash|dot| |\\n){5,}$)",
|
||||
pattern: "(?:^[-. \\n]{5,}$|^[_. \\n]{5,}$|^(?:dash|dot| |\\n){5,}$)",
|
||||
flags: "i",
|
||||
args: ["Space", "Line feed"]
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -32,37 +32,37 @@ class FromOctal extends Operation {
|
||||
"value": DELIM_OPTIONS
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?: (?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
pattern: "^(?:[0-7]{1,2}|[123][0-7]{2})(?: (?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
flags: "",
|
||||
args: ["Space"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:,(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
pattern: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:,(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
flags: "",
|
||||
args: ["Comma"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:;(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
pattern: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:;(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
flags: "",
|
||||
args: ["Semi-colon"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?::(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
pattern: "^(?:[0-7]{1,2}|[123][0-7]{2})(?::(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
flags: "",
|
||||
args: ["Colon"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:\\n(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
pattern: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:\\n(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
flags: "",
|
||||
args: ["Line feed"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:\\r\\n(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
pattern: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:\\r\\n(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
flags: "",
|
||||
args: ["CRLF"]
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ class FromQuotedPrintable extends Operation {
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^[\\x21-\\x3d\\x3f-\\x7e \\t]{0,76}(?:=[\\da-f]{2}|=\\r?\\n)(?:[\\x21-\\x3d\\x3f-\\x7e \\t]|=[\\da-f]{2}|=\\r?\\n)*$",
|
||||
pattern: "^[\\x21-\\x3d\\x3f-\\x7e \\t]{0,76}(?:=[\\da-f]{2}|=\\r?\\n)(?:[\\x21-\\x3d\\x3f-\\x7e \\t]|=[\\da-f]{2}|=\\r?\\n)*$",
|
||||
flags: "i",
|
||||
args: []
|
||||
},
|
||||
|
||||
@@ -33,27 +33,27 @@ class FromUNIXTimestamp extends Operation {
|
||||
"value": UNITS
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^1?\\d{9}$",
|
||||
pattern: "^1?\\d{9}$",
|
||||
flags: "",
|
||||
args: ["Seconds (s)"]
|
||||
},
|
||||
{
|
||||
match: "^1?\\d{12}$",
|
||||
pattern: "^1?\\d{12}$",
|
||||
flags: "",
|
||||
args: ["Milliseconds (ms)"]
|
||||
},
|
||||
{
|
||||
match: "^1?\\d{15}$",
|
||||
pattern: "^1?\\d{15}$",
|
||||
flags: "",
|
||||
args: ["Microseconds (μs)"]
|
||||
},
|
||||
{
|
||||
match: "^1?\\d{18}$",
|
||||
pattern: "^1?\\d{18}$",
|
||||
flags: "",
|
||||
args: ["Nanoseconds (ns)"]
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
121
src/core/operations/FuzzyMatch.mjs
Normal file
121
src/core/operations/FuzzyMatch.mjs
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import {fuzzyMatch, calcMatchRanges, DEFAULT_WEIGHTS} from "../lib/FuzzyMatch.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Fuzzy Match operation
|
||||
*/
|
||||
class FuzzyMatch extends Operation {
|
||||
|
||||
/**
|
||||
* FuzzyMatch constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Fuzzy Match";
|
||||
this.module = "Default";
|
||||
this.description = "Conducts a fuzzy search to find a pattern within the input based on weighted criteria.<br><br>e.g. A search for <code>dpan</code> will match on <code><b>D</b>on't <b>Pan</b>ic</code>";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Fuzzy_matching_(computer-assisted_translation)";
|
||||
this.inputType = "string";
|
||||
this.outputType = "html";
|
||||
this.args = [
|
||||
{
|
||||
name: "Search",
|
||||
type: "binaryString",
|
||||
value: ""
|
||||
},
|
||||
{
|
||||
name: "Sequential bonus",
|
||||
type: "number",
|
||||
value: DEFAULT_WEIGHTS.sequentialBonus,
|
||||
hint: "Bonus for adjacent matches"
|
||||
},
|
||||
{
|
||||
name: "Separator bonus",
|
||||
type: "number",
|
||||
value: DEFAULT_WEIGHTS.separatorBonus,
|
||||
hint: "Bonus if match occurs after a separator"
|
||||
},
|
||||
{
|
||||
name: "Camel bonus",
|
||||
type: "number",
|
||||
value: DEFAULT_WEIGHTS.camelBonus,
|
||||
hint: "Bonus if match is uppercase and previous is lower"
|
||||
},
|
||||
{
|
||||
name: "First letter bonus",
|
||||
type: "number",
|
||||
value: DEFAULT_WEIGHTS.firstLetterBonus,
|
||||
hint: "Bonus if the first letter is matched"
|
||||
},
|
||||
{
|
||||
name: "Leading letter penalty",
|
||||
type: "number",
|
||||
value: DEFAULT_WEIGHTS.leadingLetterPenalty,
|
||||
hint: "Penalty applied for every letter in the input before the first match"
|
||||
},
|
||||
{
|
||||
name: "Max leading letter penalty",
|
||||
type: "number",
|
||||
value: DEFAULT_WEIGHTS.maxLeadingLetterPenalty,
|
||||
hint: "Maxiumum penalty for leading letters"
|
||||
},
|
||||
{
|
||||
name: "Unmatched letter penalty",
|
||||
type: "number",
|
||||
value: DEFAULT_WEIGHTS.unmatchedLetterPenalty
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
run(input, args) {
|
||||
const searchStr = args[0];
|
||||
const weights = {
|
||||
sequentialBonus: args[1],
|
||||
separatorBonus: args[2],
|
||||
camelBonus: args[3],
|
||||
firstLetterBonus: args[4],
|
||||
leadingLetterPenalty: args[5],
|
||||
maxLeadingLetterPenalty: args[6],
|
||||
unmatchedLetterPenalty: args[7]
|
||||
};
|
||||
const matches = fuzzyMatch(searchStr, input, true, weights);
|
||||
|
||||
if (!matches) {
|
||||
return "No matches.";
|
||||
}
|
||||
|
||||
let result = "", pos = 0, hlClass = "hl1";
|
||||
matches.forEach(([matches, score, idxs]) => {
|
||||
const matchRanges = calcMatchRanges(idxs);
|
||||
|
||||
matchRanges.forEach(([start, length], i) => {
|
||||
result += Utils.escapeHtml(input.slice(pos, start));
|
||||
if (i === 0) result += `<span class="${hlClass}">`;
|
||||
pos = start + length;
|
||||
result += `<b>${Utils.escapeHtml(input.slice(start, pos))}</b>`;
|
||||
});
|
||||
result += "</span>";
|
||||
hlClass = hlClass === "hl1" ? "hl2" : "hl1";
|
||||
});
|
||||
|
||||
result += Utils.escapeHtml(input.slice(pos, input.length));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default FuzzyMatch;
|
||||
@@ -21,7 +21,7 @@ class GenerateHOTP extends Operation {
|
||||
|
||||
this.name = "Generate HOTP";
|
||||
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.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 (OAUTH), 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 = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
@@ -59,7 +59,7 @@ class GenerateHOTP extends Operation {
|
||||
name: args[0],
|
||||
keySize: args[1],
|
||||
codeLength: args[2],
|
||||
secret: (new ToBase32).run(input, []),
|
||||
secret: (new ToBase32).run(input, []).split("=")[0],
|
||||
});
|
||||
const counter = args[3];
|
||||
return `URI: ${otpObj.hotpURL}\n\nPassword: ${otpObj.hotp(counter)}`;
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import {isImage} from "../lib/FileType";
|
||||
import {toBase64} from "../lib/Base64";
|
||||
import jimp from "jimp";
|
||||
import {isWorkerEnvironment} from "../Utils";
|
||||
import {isImage} from "../lib/FileType.mjs";
|
||||
import {toBase64} from "../lib/Base64.mjs";
|
||||
import {isWorkerEnvironment} from "../Utils.mjs";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Generate Image operation
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
import Operation from "../Operation.mjs";
|
||||
import kbpgp from "kbpgp";
|
||||
import { getSubkeySize, ASP } from "../lib/PGP.mjs";
|
||||
import { cryptNotice } from "../lib/Crypt.mjs";
|
||||
import * as es6promisify from "es6-promisify";
|
||||
const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
|
||||
|
||||
|
||||
/**
|
||||
* Generate PGP Key Pair operation
|
||||
*/
|
||||
@@ -25,7 +27,7 @@ class GeneratePGPKeyPair extends Operation {
|
||||
|
||||
this.name = "Generate PGP Key Pair";
|
||||
this.module = "PGP";
|
||||
this.description = "Generates a new public/private PGP key pair. Supports RSA and Eliptic Curve (EC) keys.";
|
||||
this.description = `Generates a new public/private PGP key pair. Supports RSA and Eliptic Curve (EC) keys.<br><br>${cryptNotice}`;
|
||||
this.infoURL = "https://wikipedia.org/wiki/Pretty_Good_Privacy";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
@@ -33,7 +35,7 @@ class GeneratePGPKeyPair extends Operation {
|
||||
{
|
||||
"name": "Key type",
|
||||
"type": "option",
|
||||
"value": ["RSA-1024", "RSA-2048", "RSA-4096", "ECC-256", "ECC-384"]
|
||||
"value": ["RSA-1024", "RSA-2048", "RSA-4096", "ECC-256", "ECC-384", "ECC-521"]
|
||||
},
|
||||
{
|
||||
"name": "Password (optional)",
|
||||
@@ -59,12 +61,15 @@ class GeneratePGPKeyPair extends Operation {
|
||||
* @returns {string}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [keyType, keySize] = args[0].split("-"),
|
||||
password = args[1],
|
||||
let [keyType, keySize] = args[0].split("-");
|
||||
const password = args[1],
|
||||
name = args[2],
|
||||
email = args[3];
|
||||
let userIdentifier = "";
|
||||
|
||||
keyType = keyType.toLowerCase();
|
||||
keySize = parseInt(keySize, 10);
|
||||
|
||||
if (name) userIdentifier += name;
|
||||
if (email) userIdentifier += ` <${email}>`;
|
||||
|
||||
|
||||
88
src/core/operations/GenerateRSAKeyPair.mjs
Normal file
88
src/core/operations/GenerateRSAKeyPair.mjs
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @author Matt C [me@mitt.dev]
|
||||
* @author gchq77703 []
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import forge from "node-forge";
|
||||
import { cryptNotice } from "../lib/Crypt.mjs";
|
||||
|
||||
/**
|
||||
* Generate RSA Key Pair operation
|
||||
*/
|
||||
class GenerateRSAKeyPair extends Operation {
|
||||
|
||||
/**
|
||||
* GenerateRSAKeyPair constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Generate RSA Key Pair";
|
||||
this.module = "Ciphers";
|
||||
this.description = `Generate an RSA key pair with a given number of bits.<br><br>${cryptNotice}`;
|
||||
this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "RSA Key Length",
|
||||
type: "option",
|
||||
value: [
|
||||
"1024",
|
||||
"2048",
|
||||
"4096"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Output Format",
|
||||
type: "option",
|
||||
value: [
|
||||
"PEM",
|
||||
"JSON",
|
||||
"DER"
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [keyLength, outputFormat] = args;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
forge.pki.rsa.generateKeyPair({
|
||||
bits: Number(keyLength),
|
||||
workers: -1,
|
||||
workerScript: "assets/forge/prime.worker.min.js"
|
||||
}, (err, keypair) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
let result;
|
||||
|
||||
switch (outputFormat) {
|
||||
case "PEM":
|
||||
result = forge.pki.publicKeyToPem(keypair.publicKey) + "\n" + forge.pki.privateKeyToPem(keypair.privateKey);
|
||||
break;
|
||||
case "JSON":
|
||||
result = JSON.stringify(keypair);
|
||||
break;
|
||||
case "DER":
|
||||
result = forge.asn1.toDer(forge.pki.privateKeyToAsn1(keypair.privateKey)).getBytes();
|
||||
break;
|
||||
}
|
||||
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default GenerateRSAKeyPair;
|
||||
@@ -21,7 +21,7 @@ class GenerateTOTP extends Operation {
|
||||
|
||||
this.name = "Generate TOTP";
|
||||
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.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 (OAUTH), 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 = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
@@ -64,7 +64,7 @@ class GenerateTOTP extends Operation {
|
||||
name: args[0],
|
||||
keySize: args[1],
|
||||
codeLength: args[2],
|
||||
secret: (new ToBase32).run(input, []),
|
||||
secret: (new ToBase32).run(input, []).split("=")[0],
|
||||
epoch: args[3],
|
||||
timeSlice: args[4]
|
||||
});
|
||||
|
||||
63
src/core/operations/GetTime.mjs
Normal file
63
src/core/operations/GetTime.mjs
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @author n1073645 [n1073645@gmail.com]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import {UNITS} from "../lib/DateTime.mjs";
|
||||
|
||||
/**
|
||||
* Get Time operation
|
||||
*/
|
||||
class GetTime extends Operation {
|
||||
|
||||
/**
|
||||
* GetTime constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Get Time";
|
||||
this.module = "Default";
|
||||
this.description = "Generates a timestamp showing the amount of time since the UNIX epoch (1970-01-01 00:00:00 UTC). Uses the W3C High Resolution Time API.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Unix_time";
|
||||
this.inputType = "string";
|
||||
this.outputType = "number";
|
||||
this.args = [
|
||||
{
|
||||
name: "Granularity",
|
||||
type: "option",
|
||||
value: UNITS
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
run(input, args) {
|
||||
const nowMs = (performance.timeOrigin + performance.now()),
|
||||
granularity = args[0];
|
||||
|
||||
switch (granularity) {
|
||||
case "Nanoseconds (ns)":
|
||||
return Math.round(nowMs * 1000 * 1000);
|
||||
case "Microseconds (μs)":
|
||||
return Math.round(nowMs * 1000);
|
||||
case "Milliseconds (ms)":
|
||||
return Math.round(nowMs);
|
||||
case "Seconds (s)":
|
||||
return Math.round(nowMs / 1000);
|
||||
default:
|
||||
throw new OperationError("Unknown granularity value: " + granularity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default GetTime;
|
||||
@@ -27,12 +27,12 @@ class Gunzip extends Operation {
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^\\x1f\\x8b\\x08",
|
||||
pattern: "^\\x1f\\x8b\\x08",
|
||||
flags: "",
|
||||
args: []
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,19 @@ class HAS160 extends Operation {
|
||||
|
||||
this.name = "HAS-160";
|
||||
this.module = "Crypto";
|
||||
this.description = "HAS-160 is a cryptographic hash function designed for use with the Korean KCDSA digital signature algorithm. It is derived from SHA-1, with assorted changes intended to increase its security. It produces a 160-bit output.<br><br>HAS-160 is used in the same way as SHA-1. First it divides input in blocks of 512 bits each and pads the final block. A digest function updates the intermediate hash value by processing the input blocks in turn.<br><br>The message digest algorithm consists of 80 rounds.";
|
||||
this.description = "HAS-160 is a cryptographic hash function designed for use with the Korean KCDSA digital signature algorithm. It is derived from SHA-1, with assorted changes intended to increase its security. It produces a 160-bit output.<br><br>HAS-160 is used in the same way as SHA-1. First it divides input in blocks of 512 bits each and pads the final block. A digest function updates the intermediate hash value by processing the input blocks in turn.<br><br>The message digest algorithm consists, by default, of 80 rounds.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/HAS-160";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
this.args = [
|
||||
{
|
||||
name: "Rounds",
|
||||
type: "number",
|
||||
value: 80,
|
||||
min: 1,
|
||||
max: 80
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,7 +41,7 @@ class HAS160 extends Operation {
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
return runHash("has160", input);
|
||||
return runHash("has160", input, {rounds: args[0]});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ class HaversineDistance extends Operation {
|
||||
|
||||
const lat1 = parseFloat(values[1]);
|
||||
const lng1 = parseFloat(values[3]);
|
||||
const lat2 = parseFloat(values[6]);
|
||||
const lng2 = parseFloat(values[8]);
|
||||
const lat2 = parseFloat(values[5]);
|
||||
const lng2 = parseFloat(values[7]);
|
||||
|
||||
const TO_RAD = Math.PI / 180;
|
||||
const dLat = (lat2-lat1) * TO_RAD;
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Image Brightness / Contrast operation
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Image Filter operation
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Image Hue/Saturation/Lightness operation
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user