Merge branch 'master' into vigenere_upgrade

This commit is contained in:
GCHQ Developer 85297 2026-02-06 00:35:08 +00:00 committed by GitHub
commit 5f3dbc143e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
92 changed files with 9225 additions and 915 deletions

View File

@ -12,3 +12,7 @@ indent_size = 4
[{package.json,.travis.yml,nightwatch.json}]
indent_style = space
indent_size = 2
[.github/**.yml]
indent_style = space
indent_size = 2

View File

@ -1,40 +0,0 @@
name: "CodeQL Analysis"
on:
workflow_dispatch:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
types: [synchronize, opened, reopened]
schedule:
- cron: '22 17 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

View File

@ -1,58 +1,64 @@
name: "Master Build, Test & Deploy"
permissions:
contents: read
on:
workflow_dispatch:
push:
branches:
- master
- master
jobs:
main:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- name: Set node version
uses: actions/setup-node@v3
with:
node-version: '18.x'
- name: Set node version
uses: actions/setup-node@v6
with:
node-version: 18
registry-url: "https://registry.npmjs.org"
- name: Install
run: |
export DETECT_CHROMEDRIVER_VERSION=true
npm install
npm run setheapsize
- name: Install
run: |
export DETECT_CHROMEDRIVER_VERSION=true
npm install
npm run setheapsize
- name: Lint
run: npx grunt lint
- name: Lint
run: npx grunt lint
- name: Unit Tests
run: |
npm test
npm run testnodeconsumer
- name: Unit Tests
run: |
npm test
npm run testnodeconsumer
- name: Production Build
if: success()
run: npx grunt prod --msg="Version 10 is here! Read about the new features <a href='https://github.com/gchq/CyberChef/wiki/Character-encoding,-EOL-separators,-and-editor-features'>here</a>"
- name: Production Build
if: success()
run: npx grunt prod --msg=""
- name: Generate sitemap
run: npx grunt exec:sitemap
- name: Generate sitemap
run: npx grunt exec:sitemap
- name: UI Tests
if: success()
run: |
sudo apt-get install xvfb
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
- name: UI Tests
if: success()
run: |
sudo apt-get install xvfb
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
- name: Prepare for GitHub Pages
if: success()
run: npx grunt copy:ghPages
- 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@v3
with:
target_branch: gh-pages
build_dir: ./build/prod
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Deploy to GitHub Pages
if: success() && github.ref == 'refs/heads/master'
uses: crazy-max/ghaction-github-pages@v3
with:
target_branch: gh-pages
build_dir: ./build/prod
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,5 +1,8 @@
name: "Pull Requests"
permissions:
contents: read
on:
workflow_dispatch:
pull_request:
@ -9,47 +12,46 @@ jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- name: Set node version
uses: actions/setup-node@v3
with:
node-version: '18.x'
- name: Set node version
uses: actions/setup-node@v6
with:
node-version: 18
registry-url: "https://registry.npmjs.org"
- name: Install
run: |
export DETECT_CHROMEDRIVER_VERSION=true
npm install
npm run setheapsize
- name: Install
run: |
export DETECT_CHROMEDRIVER_VERSION=true
npm install
npm run setheapsize
- name: Lint
run: npx grunt lint
- name: Lint
run: npx grunt lint
- name: Unit Tests
run: |
npm test
npm run testnodeconsumer
- name: Unit Tests
run: |
npm test
npm run testnodeconsumer
- name: Production Build
if: success()
run: npx grunt prod
- name: Production Build
if: success()
run: npx grunt prod
- name: Production Image Build
if: success()
id: build-image
uses: redhat-actions/buildah-build@v2
with:
# Not being uploaded to any registry, use a simple name to allow Buildah to build correctly.
image: cyberchef
containerfiles: ./Dockerfile
platforms: linux/amd64
oci: true
# Webpack seems to use a lot of open files, increase the max open file limit to accomodate.
extra-args: |
--ulimit nofile=10000
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: UI Tests
if: success()
run: |
sudo apt-get install xvfb
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Production Image Build
if: success()
id: build-image
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
- name: UI Tests
if: success()
run: |
sudo apt-get install xvfb
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui

View File

@ -4,7 +4,11 @@ on:
workflow_dispatch:
push:
tags:
- 'v*'
- "v*"
permissions:
id-token: write
contents: read
env:
REGISTRY: ghcr.io
@ -16,78 +20,78 @@ jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- name: Set node version
uses: actions/setup-node@v3
with:
node-version: '18.x'
- name: Set node version
uses: actions/setup-node@v6
with:
node-version: 18
registry-url: "https://registry.npmjs.org"
- name: Install
run: |
npm ci
npm run setheapsize
- name: Install
run: |
export DETECT_CHROMEDRIVER_VERSION=true
npm ci
npm run setheapsize
- name: Lint
run: npx grunt lint
- name: Lint
run: npx grunt lint
- name: Unit Tests
run: |
npm test
npm run testnodeconsumer
- name: Unit Tests
run: |
npm test
npm run testnodeconsumer
- name: Production Build
run: npx grunt prod
- name: Production Build
run: npx grunt prod
- name: UI Tests
run: |
sudo apt-get install xvfb
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
- name: UI Tests
run: |
sudo apt-get install xvfb
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
- name: Image Metadata
id: image-metadata
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{version}}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Production Image Build
id: build-image
uses: redhat-actions/buildah-build@v2
with:
tags: ${{ steps.image-metadata.outputs.tags }}
labels: ${{ steps.image-metadata.outputs.labels }}
containerfiles: ./Dockerfile
platforms: linux/amd64
oci: true
# Webpack seems to use a lot of open files, increase the max open file limit to accomodate.
extra-args: |
--ulimit nofile=10000
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Image Metadata
id: image-metadata
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{version}}
- name: Upload Release Assets
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: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.REGISTRY_USER }}
password: ${{ env.REGISTRY_PASSWORD }}
- name: Publish to NPM
uses: JS-DevTools/npm-publish@v1
with:
token: ${{ secrets.NPM_TOKEN }}
- name: Publish to GHCR
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.image-metadata.outputs.tags }}
labels: ${{ steps.image-metadata.outputs.labels }}
platforms: linux/amd64,linux/arm64
- name: Publish to GHCR
uses: redhat-actions/push-to-registry@v2
with:
tags: ${{ steps.build-image.outputs.tags }}
registry: ${{ env.REGISTRY }}
username: ${{ env.REGISTRY_USER }}
password: ${{ env.REGISTRY_PASSWORD }}
- name: Upload Release Assets
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
run: npm publish

View File

@ -13,6 +13,75 @@ All major and minor version changes will be documented in this file. Details of
## Details
### [10.20.0] - 2026-01-28
- Fixed Optical Character Recognition and added tests [@n1474335] | [ab37c1e]
- Fixed JA4 version fallback value [@n1474335] | [7a5225c]
- Updated chromedriver [@n1474335] | [0e82e4b]
- Fixed RSA Sign and Verify character encodings [@n1474335] | [895a929]
- Updated chromedriver [@n1474335] | [d3adfc7]
- Added message format arg to RSA Verify operation [@n1474335] | [47c85a1]
- Add operation for parsing X.509 CRLs [@robinsandhu] | [#1887]
- Fix typo in description of JWT Sign recipe [@GuilhermoReadonly] | [#1961]
- Corrected path to generateNodeIndex.mjs [@simonarnell] | [#1959]
- Add 'header' ingredient to JWT Sign operation [@RandomByte] | [#1957]
- Add Parse TLS record operation [@c65722] | [#1936]
- Automatically detect chrome driver version [@gchq] | [#1972]
- Add Strip UDP header operation [@c65722] | [#1900]
- Add Strip TCP header operation [@c65722] | [#1898]
- Webpack compress with gzip and brotli [@max0x53] | [#1955]
- add offset field to 'Add Line Numbers' operation [@Adamkadaban] | [#1866]
- Disable flakey URL test [@a3957273] | [#1973]
- Add Strip IPv4 header operation [@c65722] | [#1899]
- IPv6 Transition Operation [@jb30795] | [#1780]
- fix: Blowfish - ignore IV length in ECB mode [@FranciscoPombal] | [#1902]
- Add 'Drop nth bytes' operation [@Oshawk] | [#1914]
- Add 'Take nth bytes' operation [@Oshawk] | [#1915]
- Add Leet Speak [@bartblaze] | [#1971]
- Fix Generate TOTP & HOPT [@exactlyaron] | [#1966]
- Updated luhn checksum operation to work with different bases [@k3ach] | [#1933]
- automatically theme mode based on user preference [@vs4vijay] | [#1921]
- fix: DES/Triple DES - misleading error messages [@FranciscoPombal] | [#1904]
- fix: ROT13 - shifting numbers by negative amounts [@FranciscoPombal] | [#1903]
- Introduce Yubico's Modhex for Conversion [@linuxgemini] | [#1105]
- Feature: MIME RFC2047 Decoding [@MShwed] | [#630]
- CC-1889 add _ option [@depperm] | [#1977]
- chore(root): add cspell [@evenstensberg] | [#1976]
- Preserve uppercase for Leet Speak [@bartblaze] | [#1981]
- Load the user's preferred color scheme if the URL contains an invalid theme [@0xh3xa] | [#2007]
- Add SM2 Encrypt and Decrypt Operations [@flakjacket95] | [#1909]
- Support jq as an operation. [@zhzy0077] | [#1604]
- Add fingerprints to the 'Parse X.509 certificate' operation [@JSCU-CNI] | [#1863]
- Added a JSON to YAML and a YAML to JSON operation [@ccarpo] | [#1286]
- Add CRC Operation [@r4mos] | [#1993]
- Bug Fix: selected theme not loading when refreshing [@0xh3xa] | [#2006]
- Fix(RecipeWaiter): sanitize user input in addOperation to prevent XSS [@0xh3xa] | [#2014]
- Docker multiplatform build support [@PathToLife] | [#1974]
- Add Base32 Hex Extended Alphabet and Base32 Tests. [@peterc-s] | [#1991]
- Add ECB/NoPadding and CBC/NoPadding support to AES encryption [@plvie] | [#2013]
- Add new operation: PHP Serialize [@brun0ne] | [#1548]
- Push input through postmessage [@kenduguay1] | [#1992]
- Add jsonata query operation [@jonking-ajar] | [#1587]
- Re-enable Npm Release in github workflows [@PathToLife] | [#2031]
- Add to ECDSA Verify the message format [@r4mos] | [#2027]
- Added alternating caps functionality [@sw5678] | [#1897]
- XOR Checksum operation added [@jg42526] | [#2035]
- Add GenerateAllChecksums operation * Remove checksums from GenerateAllHashes operation [@es45411] | [66d445c]
- Update GenerateAllChecksums infoURL [@es45411] | [#2037]
- Add toggle "+" character to URLDecode operation [@es45411] | [#2040]
- Workaround for Safari load bug [@GCHQDeveloper94872] | [#2038]
- Updated Dockerfile to correctly build on ARM64 platforms [@Sma-Das] | [#2042]
- Addresses bug report #2008 Added explicit support for octal IP addresses. Changed approach to IPv4 regex to be string manipulation generated. Added some unit tests for IP address parsing - probably not full coverage. Added lookahead and lookbehind tricks to resolve warned issue that 1.2.3.256 would still be extracted as 1.2.3.25. Now only accepts valid IP addresses. Warning replaced with clause about infinite length dotted decimal forms. [@gchqdev364] | [#2041]
- Remove trim from rail fence [@Odyhibit] | [#1986]
- Fix email regex [@ericli-splunk] | [#2025]
- Add Blake3 hashing [@xumptex] | [#2023]
- Use defaultIndex instead of 0 in transformArgs [@bartvanandel] | [#2015]
- Add "Generate UUID" and "Analyse UUID" operations [@bartvanandel] | [#2011]
- Add new operation: Template [@kendallgoto] | [#2021]
- Add more clear build instructions [@remingtr] | [#1873]
- Show On Map updated to use leaflet over WikiMedia [@0xff1ce] | [#1884]
- Fixed ToDecimal signed logic [@starplanet] | [#1545]
- Use BigInt for encoding/decoding VarInt [@mikecat] | [#1978]
### [10.19.0] - 2024-06-21
- Add support for ECDSA and DSA in 'Parse CSR' [@robinsandhu] | [#1828]
- Fix typos in SIGABA.mjs [@eltociear] | [#1834]
@ -440,6 +509,7 @@ All major and minor version changes will be documented in this file. Details of
## [4.0.0] - 2016-11-28
- Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306)
[10.20.0]: https://github.com/gchq/CyberChef/releases/tag/v10.20.0
[10.19.0]: https://github.com/gchq/CyberChef/releases/tag/v10.19.0
[10.18.0]: https://github.com/gchq/CyberChef/releases/tag/v10.18.0
[10.17.0]: https://github.com/gchq/CyberChef/releases/tag/v10.17.0
@ -630,6 +700,60 @@ All major and minor version changes will be documented in this file. Details of
[@cplussharp]: https://github.com/cplussharp
[@robinsandhu]: https://github.com/robinsandhu
[@eltociear]: https://github.com/eltociear
[@GuilhermoReadonly]: https://github.com/GuilhermoReadonly
[@simonarnell]: https://github.com/simonarnell
[@RandomByte]: https://github.com/RandomByte
[@c65722]: https://github.com/c65722
[@c65722]: https://github.com/c65722
[@c65722]: https://github.com/c65722
[@max0x53]: https://github.com/max0x53
[@Adamkadaban]: https://github.com/Adamkadaban
[@c65722]: https://github.com/c65722
[@jb30795]: https://github.com/jb30795
[@FranciscoPombal]: https://github.com/FranciscoPombal
[@Oshawk]: https://github.com/Oshawk
[@Oshawk]: https://github.com/Oshawk
[@bartblaze]: https://github.com/bartblaze
[@exactlyaron]: https://github.com/exactlyaron
[@k3ach]: https://github.com/k3ach
[@vs4vijay]: https://github.com/vs4vijay
[@FranciscoPombal]: https://github.com/FranciscoPombal
[@FranciscoPombal]: https://github.com/FranciscoPombal
[@linuxgemini]: https://github.com/linuxgemini
[@depperm]: https://github.com/depperm
[@evenstensberg]: https://github.com/evenstensberg
[@bartblaze]: https://github.com/bartblaze
[@0xh3xa]: https://github.com/0xh3xa
[@flakjacket95]: https://github.com/flakjacket95
[@zhzy0077]: https://github.com/zhzy0077
[@JSCU-CNI]: https://github.com/JSCU-CNI
[@ccarpo]: https://github.com/ccarpo
[@r4mos]: https://github.com/r4mos
[@0xh3xa]: https://github.com/0xh3xa
[@0xh3xa]: https://github.com/0xh3xa
[@PathToLife]: https://github.com/PathToLife
[@peterc-s]: https://github.com/peterc-s
[@plvie]: https://github.com/plvie
[@kenduguay1]: https://github.com/kenduguay1
[@jonking-ajar]: https://github.com/jonking-ajar
[@PathToLife]: https://github.com/PathToLife
[@r4mos]: https://github.com/r4mos
[@jg42526]: https://github.com/jg42526
[@es45411]: https://github.com/es45411
[@gchq]: https://github.com/gchq
[@gchqdev364]: https://github.com/gchqdev364
[@GCHQDeveloper94872]: https://github.com/GCHQDeveloper94872
[@Sma-Das]: https://github.com/Sma-Das
[@gchq]: https://github.com/gchq
[@Odyhibit]: https://github.com/Odyhibit
[@ericli-splunk]: https://github.com/ericli-splunk
[@xumptex]: https://github.com/xumptex
[@bartvanandel]: https://github.com/bartvanandel
[@bartvanandel]: https://github.com/bartvanandel
[@kendallgoto]: https://github.com/kendallgoto
[@remingtr]: https://github.com/remingtr
[@0xff1ce]: https://github.com/0xff1ce
[@starplanet]: https://github.com/starplanet
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7
@ -642,6 +766,46 @@ All major and minor version changes will be documented in this file. Details of
[760eff4]: https://github.com/gchq/CyberChef/commit/760eff49b5307aaa3104c5e5b437ffe62299acd1
[65ffd8d]: https://github.com/gchq/CyberChef/commit/65ffd8d65d88eb369f6f61a5d1d0f807179bffb7
[0a353ee]: https://github.com/gchq/CyberChef/commit/0a353eeb378b9ca5d49e23c7dfc175ae07107b08
[66d445c]: https://github.com/gchq/CyberChef/commit/66d445c5ef4e8bd896fd15396e3ce2d660d8ace1
[ab37c1e]: https://github.com/gchq/CyberChef/commit/ab37c1e562dbee0495ed32876ecbb8225282af25
[965570d]: https://github.com/gchq/CyberChef/commit/965570d2504c17ee1f96211a1dc10ed40cd2b332
[a477f47]: https://github.com/gchq/CyberChef/commit/a477f47aecd01d78b11fe186ed4b20d9c487cfac
[7a5225c]: https://github.com/gchq/CyberChef/commit/7a5225c961a5e0d192b03152117cd10a761f73d6
[5f88ae4]: https://github.com/gchq/CyberChef/commit/5f88ae44ec77228d9bed8f11e8cc8e7dcfb36914
[0e82e4b]: https://github.com/gchq/CyberChef/commit/0e82e4b7c6c77cadb8be61cb145e081d6ecfdc88
[d635cca]: https://github.com/gchq/CyberChef/commit/d635cca2106aae2a59caf0e5d7e3633ee1ea3155
[895a929]: https://github.com/gchq/CyberChef/commit/895a9299255525cb57886deb9d9fd4ba17ae9548
[270a333]: https://github.com/gchq/CyberChef/commit/270a33317944612d27ea1cc15275ad6b0ed097e5
[d3adfc7]: https://github.com/gchq/CyberChef/commit/d3adfc7c3e5719279524356bce5261bd8350c0f8
[47c85a1]: https://github.com/gchq/CyberChef/commit/47c85a105ddbdd4cabfa44ddddbc56e3907a8c33
[3822c6c]: https://github.com/gchq/CyberChef/commit/3822c6c520a0b4200abc675c33f46082f5b9efc6
[66d445c]: https://github.com/gchq/CyberChef/commit/66d445c5ef4e8bd896fd15396e3ce2d660d8ace1
[ab37c1e]: https://github.com/gchq/CyberChef/commit/ab37c1e562dbee0495ed32876ecbb8225282af25
[965570d]: https://github.com/gchq/CyberChef/commit/965570d2504c17ee1f96211a1dc10ed40cd2b332
[a477f47]: https://github.com/gchq/CyberChef/commit/a477f47aecd01d78b11fe186ed4b20d9c487cfac
[7a5225c]: https://github.com/gchq/CyberChef/commit/7a5225c961a5e0d192b03152117cd10a761f73d6
[5f88ae4]: https://github.com/gchq/CyberChef/commit/5f88ae44ec77228d9bed8f11e8cc8e7dcfb36914
[0e82e4b]: https://github.com/gchq/CyberChef/commit/0e82e4b7c6c77cadb8be61cb145e081d6ecfdc88
[d635cca]: https://github.com/gchq/CyberChef/commit/d635cca2106aae2a59caf0e5d7e3633ee1ea3155
[895a929]: https://github.com/gchq/CyberChef/commit/895a9299255525cb57886deb9d9fd4ba17ae9548
[270a333]: https://github.com/gchq/CyberChef/commit/270a33317944612d27ea1cc15275ad6b0ed097e5
[d3adfc7]: https://github.com/gchq/CyberChef/commit/d3adfc7c3e5719279524356bce5261bd8350c0f8
[47c85a1]: https://github.com/gchq/CyberChef/commit/47c85a105ddbdd4cabfa44ddddbc56e3907a8c33
[3822c6c]: https://github.com/gchq/CyberChef/commit/3822c6c520a0b4200abc675c33f46082f5b9efc6
[66d445c]: https://github.com/gchq/CyberChef/commit/66d445c5ef4e8bd896fd15396e3ce2d660d8ace1
[ab37c1e]: https://github.com/gchq/CyberChef/commit/ab37c1e562dbee0495ed32876ecbb8225282af25
[965570d]: https://github.com/gchq/CyberChef/commit/965570d2504c17ee1f96211a1dc10ed40cd2b332
[a477f47]: https://github.com/gchq/CyberChef/commit/a477f47aecd01d78b11fe186ed4b20d9c487cfac
[7a5225c]: https://github.com/gchq/CyberChef/commit/7a5225c961a5e0d192b03152117cd10a761f73d6
[5f88ae4]: https://github.com/gchq/CyberChef/commit/5f88ae44ec77228d9bed8f11e8cc8e7dcfb36914
[0e82e4b]: https://github.com/gchq/CyberChef/commit/0e82e4b7c6c77cadb8be61cb145e081d6ecfdc88
[d635cca]: https://github.com/gchq/CyberChef/commit/d635cca2106aae2a59caf0e5d7e3633ee1ea3155
[895a929]: https://github.com/gchq/CyberChef/commit/895a9299255525cb57886deb9d9fd4ba17ae9548
[270a333]: https://github.com/gchq/CyberChef/commit/270a33317944612d27ea1cc15275ad6b0ed097e5
[d3adfc7]: https://github.com/gchq/CyberChef/commit/d3adfc7c3e5719279524356bce5261bd8350c0f8
[47c85a1]: https://github.com/gchq/CyberChef/commit/47c85a105ddbdd4cabfa44ddddbc56e3907a8c33
[3822c6c]: https://github.com/gchq/CyberChef/commit/3822c6c520a0b4200abc675c33f46082f5b9efc6
[66d445c]: https://github.com/gchq/CyberChef/commit/66d445c5ef4e8bd896fd15396e3ce2d660d8ace1
[#95]: https://github.com/gchq/CyberChef/pull/299
[#173]: https://github.com/gchq/CyberChef/pull/173
@ -778,4 +942,3 @@ All major and minor version changes will be documented in this file. Details of
[#512]: https://github.com/gchq/CyberChef/issues/512
[#1732]: https://github.com/gchq/CyberChef/issues/1732
[#1789]: https://github.com/gchq/CyberChef/issues/1789

View File

@ -1,9 +1,32 @@
FROM node:18-alpine AS build
#####################################
# Build the app to a static website #
#####################################
# Modifier --platform=$BUILDPLATFORM limits the platform to "BUILDPLATFORM" during buildx multi-platform builds
# This is because npm "chromedriver" package is not compatiable with all platforms
# For more info see: https://docs.docker.com/build/building/multi-platform/#cross-compilation
FROM --platform=$BUILDPLATFORM node:18-alpine AS builder
WORKDIR /app
COPY package.json .
COPY package-lock.json .
# Install dependencies
# --ignore-scripts prevents postinstall script (which runs grunt) as it depends on files other than package.json
RUN npm ci --ignore-scripts
# Copy files needed for postinstall and build
COPY . .
RUN npm ci
# npm postinstall runs grunt, which depends on files other than package.json
RUN npm run postinstall
# Build the app
RUN npm run build
FROM nginx:1.25-alpine3.18 AS cyberchef
#########################################
# Package static build files into nginx #
#########################################
FROM nginx:stable-alpine AS cyberchef
COPY --from=build ./build/prod /usr/share/nginx/html/
COPY --from=builder /app/build/prod /usr/share/nginx/html/

View File

@ -20,21 +20,36 @@ Cryptographic operations in CyberChef should not be relied upon to provide secur
[A live demo can be found here][1] - have fun!
## Containers
## Running Locally with Docker
If you would like to try out CyberChef locally you can either build it yourself:
**Prerequisites**
- [Docker](hhttps://www.docker.com/products/docker-desktop/)
- Docker Desktop must be open and running on your machine
#### Option 1: Build the Docker Image Yourself
1. Build the docker image
```bash
docker build --tag cyberchef --ulimit nofile=10000 .
```
2. Run the docker container
```bash
docker run -it -p 8080:80 cyberchef
```
3. Navigate to `http://localhost:8080` in your browser
Or you can use our image directly:
#### Option 2: Use the pre-built Docker Image
If you prefer to skip the build process, you can use the pre-built image
```bash
docker run -it -p 8080:80 ghcr.io/gchq/cyberchef:latest
```
Just like before, navigate to `http://localhost:8080` in your browser.
This image is built and published through our [GitHub Workflows](.github/workflows/releases.yml)
## How it works

287
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "cyberchef",
"version": "10.19.4",
"version": "10.20.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cyberchef",
"version": "10.19.4",
"version": "10.20.0",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@ -31,12 +31,13 @@
"chi-squared": "^1.1.0",
"codepage": "^1.15.0",
"crypto-api": "^0.8.5",
"crypto-browserify": "^3.12.0",
"crypto-browserify": "^3.12.1",
"crypto-js": "^4.2.0",
"ctph.js": "0.0.5",
"d3": "7.9.0",
"d3-hexbin": "^0.2.2",
"diff": "^5.2.0",
"dompurify": "^3.2.5",
"es6-promisify": "^7.0.0",
"escodegen": "^2.1.0",
"esprima": "^4.0.1",
@ -45,19 +46,22 @@
"file-saver": "^2.0.5",
"flat": "^6.0.1",
"geodesy": "1.1.3",
"handlebars": "^4.7.8",
"hash-wasm": "^4.12.0",
"highlight.js": "^11.9.0",
"ieee754": "^1.2.1",
"jimp": "^0.22.12",
"jq-web": "^0.5.1",
"jquery": "3.7.1",
"js-crc": "^0.2.0",
"js-sha3": "^0.9.3",
"jsesc": "^3.0.2",
"json5": "^2.2.3",
"jsonpath-plus": "^9.0.0",
"jsonata": "^2.0.3",
"jsonpath-plus": "^10.3.0",
"jsonwebtoken": "8.5.1",
"jsqr": "^1.4.0",
"jsrsasign": "^11.1.0",
"kbpgp": "2.1.15",
"kbpgp": "^2.1.17",
"libbzip2-wasm": "0.0.4",
"libyara-wasm": "^1.2.1",
"lodash": "^4.17.21",
@ -93,6 +97,7 @@
"ua-parser-js": "^1.0.38",
"unorm": "^1.6.0",
"utf8": "^3.0.0",
"uuid": "^11.1.0",
"vkbeautify": "^0.99.3",
"xpath": "0.0.34",
"xregexp": "^5.1.1",
@ -4364,6 +4369,13 @@
"@types/node": "*"
}
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"license": "MIT",
"optional": true
},
"node_modules/@types/ws": {
"version": "8.5.13",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
@ -5125,7 +5137,6 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"possible-typed-array-names": "^1.0.0"
@ -8410,6 +8421,15 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/dompurify": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz",
"integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
},
"node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
@ -8720,6 +8740,22 @@
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es6-object-assign": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
@ -9823,7 +9859,6 @@
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-callable": "^1.1.3"
@ -9883,14 +9918,16 @@
}
},
"node_modules/form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"dev": true,
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
@ -10825,6 +10862,27 @@
"dev": true,
"license": "MIT"
},
"node_modules/handlebars": {
"version": "4.7.8",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
"integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
"license": "MIT",
"dependencies": {
"minimist": "^1.2.5",
"neo-async": "^2.6.2",
"source-map": "^0.6.1",
"wordwrap": "^1.0.0"
},
"bin": {
"handlebars": "bin/handlebars"
},
"engines": {
"node": ">=0.4.7"
},
"optionalDependencies": {
"uglify-js": "^3.1.4"
}
},
"node_modules/has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
@ -10923,6 +10981,11 @@
"node": ">= 0.10"
}
},
"node_modules/hash-wasm": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.12.0.tgz",
"integrity": "sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ=="
},
"node_modules/hash.js": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
@ -11659,7 +11722,6 @@
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@ -12010,6 +12072,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-typed-array": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
"license": "MIT",
"dependencies": {
"which-typed-array": "^1.1.16"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-unc-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
@ -12290,18 +12367,18 @@
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
"license": "BSD-3-Clause"
},
"node_modules/jq-web": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/jq-web/-/jq-web-0.5.1.tgz",
"integrity": "sha512-3Fa3E6g3U1O1j46ljy0EM10yRr4txzILga8J7bqOG8F89gZ6Lilz82WG9z6TItWpYEO0YGa4W8yFGj+NMM1xqQ==",
"license": "ISC"
},
"node_modules/jquery": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
"license": "MIT"
},
"node_modules/js-crc": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/js-crc/-/js-crc-0.2.0.tgz",
"integrity": "sha512-8DdCSAOACpF8WDAjyDFBC2rj8OS4HUP9mNZBDfl8jCiPCnJG+2bkuycalxwZh6heFy6PrMvoWTp47lp6gzT65A==",
"license": "MIT"
},
"node_modules/js-sha3": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.9.3.tgz",
@ -12447,22 +12524,30 @@
"node": ">=6"
}
},
"node_modules/jsonata": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.0.6.tgz",
"integrity": "sha512-WhQB5tXQ32qjkx2GYHFw2XbL90u+LLzjofAYwi+86g6SyZeXHz9F1Q0amy3dWRYczshOC3Haok9J4pOCgHtwyQ==",
"engines": {
"node": ">= 8"
}
},
"node_modules/jsonpath-plus": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-9.0.0.tgz",
"integrity": "sha512-bqE77VIDStrOTV/czspZhTn+o27Xx9ZJRGVkdVShEtPoqsIx5yALv3lWVU6y+PqYvWPJNWE7ORCQheQkEe0DDA==",
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz",
"integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==",
"license": "MIT",
"dependencies": {
"@jsep-plugin/assignment": "^1.2.1",
"@jsep-plugin/regex": "^1.0.3",
"jsep": "^1.3.8"
"@jsep-plugin/assignment": "^1.3.0",
"@jsep-plugin/regex": "^1.0.4",
"jsep": "^1.4.0"
},
"bin": {
"jsonpath": "bin/jsonpath-cli.js",
"jsonpath-plus": "bin/jsonpath-cli.js"
},
"engines": {
"node": ">=14.0.0"
"node": ">=18.0.0"
}
},
"node_modules/jsonwebtoken": {
@ -12546,9 +12631,9 @@
}
},
"node_modules/kbpgp": {
"version": "2.1.15",
"resolved": "https://registry.npmjs.org/kbpgp/-/kbpgp-2.1.15.tgz",
"integrity": "sha512-iFdQT+m2Mi2DB14kEFydF2joNe9x3E2VZCGZUt7UXsiZnQx5TtSl4KofP7EPtjHvf7weCxNKlEPSYiiCNMZ2jA==",
"version": "2.1.17",
"resolved": "https://registry.npmjs.org/kbpgp/-/kbpgp-2.1.17.tgz",
"integrity": "sha512-pnjH7amyg6dZLXyF42BKbCTST0l0r1ErunqtFRrJCkHkGJb83cZZmx1pnqNFr+d/ls+5gvcHrZLPfUG5q7oRYw==",
"license": "BSD-3-Clause",
"dependencies": {
"bn": "^1.0.5",
@ -13637,7 +13722,6 @@
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true,
"license": "MIT"
},
"node_modules/netmask": {
@ -13859,6 +13943,15 @@
"node": ">=8"
}
},
"node_modules/nightwatch/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/nightwatch/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
@ -13920,9 +14013,9 @@
}
},
"node_modules/node-forge": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz",
"integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==",
"license": "(BSD-3-Clause OR GPL-2.0)",
"engines": {
"node": ">= 6.13.0"
@ -14837,19 +14930,20 @@
}
},
"node_modules/pbkdf2": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
"integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz",
"integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==",
"license": "MIT",
"dependencies": {
"create-hash": "^1.1.2",
"create-hmac": "^1.1.4",
"ripemd160": "^2.0.1",
"safe-buffer": "^5.0.1",
"sha.js": "^2.4.8"
"create-hash": "^1.2.0",
"create-hmac": "^1.1.7",
"ripemd160": "^2.0.3",
"safe-buffer": "^5.2.1",
"sha.js": "^2.4.12",
"to-buffer": "^1.2.1"
},
"engines": {
"node": ">=0.12"
"node": ">= 0.10"
}
},
"node_modules/peek-readable": {
@ -15107,7 +15201,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
"integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@ -16086,13 +16179,31 @@
}
},
"node_modules/ripemd160": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz",
"integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==",
"license": "MIT",
"dependencies": {
"hash-base": "^3.0.0",
"inherits": "^2.0.1"
"hash-base": "^3.1.2",
"inherits": "^2.0.4"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/ripemd160/node_modules/hash-base": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz",
"integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.4",
"readable-stream": "^2.3.8",
"safe-buffer": "^5.2.1",
"to-buffer": "^1.2.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/rison": {
@ -16554,16 +16665,23 @@
"license": "ISC"
},
"node_modules/sha.js": {
"version": "2.4.11",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"version": "2.4.12",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz",
"integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==",
"license": "(MIT AND BSD-3-Clause)",
"dependencies": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
"inherits": "^2.0.4",
"safe-buffer": "^5.2.1",
"to-buffer": "^1.2.0"
},
"bin": {
"sha.js": "bin.js"
},
"engines": {
"node": ">= 0.10"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/shebang-command": {
@ -16802,6 +16920,15 @@
"node": ">=0.8.0"
}
},
"node_modules/sockjs/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/socks": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz",
@ -16842,7 +16969,6 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"devOptional": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@ -17592,6 +17718,26 @@
"node": ">=14.14"
}
},
"node_modules/to-buffer": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz",
"integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==",
"license": "MIT",
"dependencies": {
"isarray": "^2.0.5",
"safe-buffer": "^5.2.1",
"typed-array-buffer": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/to-buffer/node_modules/isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"license": "MIT"
},
"node_modules/to-fast-properties": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
@ -17774,6 +17920,20 @@
"node": ">= 0.6"
}
},
"node_modules/typed-array-buffer": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
"integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.3",
"es-errors": "^1.3.0",
"is-typed-array": "^1.1.14"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/ua-parser-js": {
"version": "1.0.40",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz",
@ -18089,13 +18249,15 @@
}
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"license": "MIT",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
"uuid": "dist/esm/bin/uuid"
}
},
"node_modules/v8flags": {
@ -18782,7 +18944,6 @@
"version": "1.1.18",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz",
"integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==",
"dev": true,
"license": "MIT",
"dependencies": {
"available-typed-arrays": "^1.0.7",
@ -18860,6 +19021,12 @@
"node": ">=0.10.0"
}
},
"node_modules/wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
"license": "MIT"
},
"node_modules/worker-loader": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-3.0.8.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "10.19.4",
"version": "10.20.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",
@ -117,12 +117,13 @@
"chi-squared": "^1.1.0",
"codepage": "^1.15.0",
"crypto-api": "^0.8.5",
"crypto-browserify": "^3.12.0",
"crypto-browserify": "^3.12.1",
"crypto-js": "^4.2.0",
"ctph.js": "0.0.5",
"d3": "7.9.0",
"d3-hexbin": "^0.2.2",
"diff": "^5.2.0",
"dompurify": "^3.2.5",
"es6-promisify": "^7.0.0",
"escodegen": "^2.1.0",
"esprima": "^4.0.1",
@ -131,19 +132,22 @@
"file-saver": "^2.0.5",
"flat": "^6.0.1",
"geodesy": "1.1.3",
"handlebars": "^4.7.8",
"hash-wasm": "^4.12.0",
"highlight.js": "^11.9.0",
"ieee754": "^1.2.1",
"jimp": "^0.22.12",
"jq-web": "^0.5.1",
"jquery": "3.7.1",
"js-crc": "^0.2.0",
"js-sha3": "^0.9.3",
"jsesc": "^3.0.2",
"json5": "^2.2.3",
"jsonpath-plus": "^9.0.0",
"jsonata": "^2.0.3",
"jsonpath-plus": "^10.3.0",
"jsonwebtoken": "8.5.1",
"jsqr": "^1.4.0",
"jsrsasign": "^11.1.0",
"kbpgp": "2.1.15",
"kbpgp": "^2.1.17",
"libbzip2-wasm": "0.0.4",
"libyara-wasm": "^1.2.1",
"lodash": "^4.17.21",
@ -179,6 +183,7 @@
"ua-parser-js": "^1.0.38",
"unorm": "^1.6.0",
"utf8": "^3.0.0",
"uuid": "^11.1.0",
"vkbeautify": "^0.99.3",
"xpath": "0.0.34",
"xregexp": "^5.1.1",

View File

@ -177,7 +177,7 @@ class Utils {
*/
static printable(str, preserveWs=false, onlyAscii=false) {
if (onlyAscii) {
return str.replace(/[^\x20-\x7f]/g, ".");
return str.replace(/[^\x20-\x7e]/g, ".");
}
// eslint-disable-next-line no-misleading-character-class

View File

@ -26,6 +26,8 @@
"From Base45",
"To Base58",
"From Base58",
"To Bech32",
"From Bech32",
"To Base62",
"From Base62",
"To Base64",
@ -72,6 +74,8 @@
"Avro to JSON",
"CBOR Encode",
"CBOR Decode",
"YAML to JSON",
"JSON to YAML",
"Caret/M-decode",
"Rison Encode",
"Rison Decode",
@ -193,7 +197,9 @@
"Parse SSH Host Key",
"Parse CSR",
"Public Key from Certificate",
"Public Key from Private Key"
"Public Key from Private Key",
"SM2 Encrypt",
"SM2 Decrypt"
]
},
{
@ -290,6 +296,7 @@
"To Upper case",
"To Lower case",
"Swap case",
"Alternating Caps",
"To Case Insensitive Regex",
"From Case Insensitive Regex",
"Add line numbers",
@ -365,11 +372,13 @@
"Regular expression",
"XPath expression",
"JPath expression",
"Jsonata Query",
"CSS selector",
"Extract EXIF",
"Extract ID3",
"Extract Files",
"RAKE"
"RAKE",
"Template"
]
},
{
@ -400,6 +409,7 @@
"name": "Hashing",
"ops": [
"Analyse hash",
"Generate all checksums",
"Generate all hashes",
"MD2",
"MD4",
@ -418,6 +428,7 @@
"Snefru",
"BLAKE2b",
"BLAKE2s",
"BLAKE3",
"GOST Hash",
"Streebog",
"SSDEEP",
@ -441,10 +452,9 @@
"Fletcher-64 Checksum",
"Adler-32 Checksum",
"Luhn Checksum",
"CRC-8 Checksum",
"CRC-16 Checksum",
"CRC-32 Checksum",
"TCP/IP Checksum"
"CRC Checksum",
"TCP/IP Checksum",
"XOR Checksum"
]
},
{
@ -465,8 +475,10 @@
"CSS Minify",
"XPath expression",
"JPath expression",
"Jq",
"CSS selector",
"PHP Deserialize",
"PHP Serialize",
"Microsoft Script Decoder",
"Strip HTML tags",
"Diff",
@ -541,6 +553,7 @@
"Pseudo-Random Number Generator",
"Generate De Bruijn Sequence",
"Generate UUID",
"Analyse UUID",
"Generate TOTP",
"Generate HOTP",
"Generate QR Code",

23
src/core/lib/Base32.mjs Normal file
View File

@ -0,0 +1,23 @@
// import Utils from "../Utils.mjs";
/**
* Base32 resources.
*
* @author Peter C-S [petercs@purelymail.com]
* @license Apache-2.0
*/
/**
* Base32 alphabets.
*/
export const ALPHABET_OPTIONS = [
{
name: "Standard", // https://www.rfc-editor.org/rfc/rfc4648#section-6
value: "A-Z2-7=",
},
{
name: "Hex Extended", // https://www.rfc-editor.org/rfc/rfc4648#section-7
value: "0-9A-V=",
},
];

371
src/core/lib/Bech32.mjs Normal file
View File

@ -0,0 +1,371 @@
/**
* Pure JavaScript implementation of Bech32 and Bech32m encoding.
*
* Bech32 is defined in BIP-0173: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
* Bech32m is defined in BIP-0350: https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki
*
* @author Medjedtxm
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
/** Bech32 character set (32 characters, excludes 1, b, i, o) */
const CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
/** Reverse lookup table for decoding */
const CHARSET_REV = {};
for (let i = 0; i < CHARSET.length; i++) {
CHARSET_REV[CHARSET[i]] = i;
}
/** Checksum constant for Bech32 (BIP-0173) */
const BECH32_CONST = 1;
/** Checksum constant for Bech32m (BIP-0350) */
const BECH32M_CONST = 0x2bc830a3;
/** Generator polynomial coefficients for checksum */
const GENERATOR = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
/**
* Compute the polymod checksum
* @param {number[]} values - Array of 5-bit values
* @returns {number} - Checksum value
*/
function polymod(values) {
let chk = 1;
for (const v of values) {
const top = chk >> 25;
chk = ((chk & 0x1ffffff) << 5) ^ v;
for (let i = 0; i < 5; i++) {
if ((top >> i) & 1) {
chk ^= GENERATOR[i];
}
}
}
return chk;
}
/**
* Expand HRP for checksum computation
* @param {string} hrp - Human-readable part (lowercase)
* @returns {number[]} - Expanded values
*/
function hrpExpand(hrp) {
const result = [];
for (let i = 0; i < hrp.length; i++) {
result.push(hrp.charCodeAt(i) >> 5);
}
result.push(0);
for (let i = 0; i < hrp.length; i++) {
result.push(hrp.charCodeAt(i) & 31);
}
return result;
}
/**
* Verify checksum of a Bech32/Bech32m string
* @param {string} hrp - Human-readable part (lowercase)
* @param {number[]} data - Data including checksum (5-bit values)
* @param {string} encoding - "Bech32" or "Bech32m"
* @returns {boolean} - True if checksum is valid
*/
function verifyChecksum(hrp, data, encoding) {
const constant = encoding === "Bech32m" ? BECH32M_CONST : BECH32_CONST;
return polymod(hrpExpand(hrp).concat(data)) === constant;
}
/**
* Create checksum for Bech32/Bech32m encoding
* @param {string} hrp - Human-readable part (lowercase)
* @param {number[]} data - Data values (5-bit)
* @param {string} encoding - "Bech32" or "Bech32m"
* @returns {number[]} - 6 checksum values
*/
function createChecksum(hrp, data, encoding) {
const constant = encoding === "Bech32m" ? BECH32M_CONST : BECH32_CONST;
const values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);
const mod = polymod(values) ^ constant;
const result = [];
for (let i = 0; i < 6; i++) {
result.push((mod >> (5 * (5 - i))) & 31);
}
return result;
}
/**
* Convert 8-bit bytes to 5-bit words
* @param {number[]|Uint8Array} data - Input bytes
* @returns {number[]} - 5-bit words
*/
export function toWords(data) {
let value = 0;
let bits = 0;
const result = [];
for (let i = 0; i < data.length; i++) {
value = (value << 8) | data[i];
bits += 8;
while (bits >= 5) {
bits -= 5;
result.push((value >> bits) & 31);
}
}
// Pad remaining bits
if (bits > 0) {
result.push((value << (5 - bits)) & 31);
}
return result;
}
/**
* Convert 5-bit words to 8-bit bytes
* @param {number[]} words - 5-bit words
* @returns {number[]} - Output bytes
*/
export function fromWords(words) {
let value = 0;
let bits = 0;
const result = [];
for (let i = 0; i < words.length; i++) {
value = (value << 5) | words[i];
bits += 5;
while (bits >= 8) {
bits -= 8;
result.push((value >> bits) & 255);
}
}
// Check for invalid padding per BIP-0173
// Condition 1: Cannot have 5+ bits remaining (would indicate incomplete byte)
if (bits >= 5) {
throw new OperationError("Invalid padding: too many bits remaining");
}
// Condition 2: Remaining padding bits must all be zero
if (bits > 0) {
const paddingValue = (value << (8 - bits)) & 255;
if (paddingValue !== 0) {
throw new OperationError("Invalid padding: non-zero bits in padding");
}
}
return result;
}
/**
* Encode data to Bech32/Bech32m string
*
* @param {string} hrp - Human-readable part
* @param {number[]|Uint8Array} data - Data bytes to encode
* @param {string} encoding - "Bech32" or "Bech32m"
* @param {boolean} segwit - If true, treat first byte as witness version (for Bitcoin SegWit)
* @returns {string} - Encoded Bech32/Bech32m string
*/
export function encode(hrp, data, encoding = "Bech32", segwit = false) {
// Validate HRP
if (!hrp || hrp.length === 0) {
throw new OperationError("Human-Readable Part (HRP) cannot be empty.");
}
// Check HRP characters (ASCII 33-126)
for (let i = 0; i < hrp.length; i++) {
const c = hrp.charCodeAt(i);
if (c < 33 || c > 126) {
throw new OperationError(`HRP contains invalid character at position ${i}. Only printable ASCII characters (33-126) are allowed.`);
}
}
// Convert HRP to lowercase
const hrpLower = hrp.toLowerCase();
let words;
if (segwit && data.length >= 2) {
// SegWit encoding: first byte is witness version (0-16), rest is witness program
const witnessVersion = data[0];
if (witnessVersion > 16) {
throw new OperationError(`Invalid witness version: ${witnessVersion}. Must be 0-16.`);
}
const witnessProgram = Array.prototype.slice.call(data, 1);
// Validate witness program length per BIP-0141
if (witnessProgram.length < 2 || witnessProgram.length > 40) {
throw new OperationError(`Invalid witness program length: ${witnessProgram.length}. Must be 2-40 bytes.`);
}
if (witnessVersion === 0 && witnessProgram.length !== 20 && witnessProgram.length !== 32) {
throw new OperationError(`Invalid witness program length for v0: ${witnessProgram.length}. Must be 20 or 32 bytes.`);
}
// Witness version is kept as single 5-bit value, program is converted
words = [witnessVersion].concat(toWords(witnessProgram));
} else {
// Generic encoding: convert all bytes to 5-bit words
words = toWords(data);
}
// Create checksum
const checksum = createChecksum(hrpLower, words, encoding);
// Build result string
let result = hrpLower + "1";
for (const w of words.concat(checksum)) {
result += CHARSET[w];
}
// Check maximum length (90 characters)
if (result.length > 90) {
throw new OperationError(`Encoded string exceeds maximum length of 90 characters (got ${result.length}). Consider using smaller input data.`);
}
return result;
}
/**
* Decode a Bech32/Bech32m string
*
* @param {string} str - Bech32/Bech32m encoded string
* @param {string} encoding - "Bech32", "Bech32m", or "Auto-detect"
* @returns {{hrp: string, data: number[]}} - Decoded HRP and data bytes
*/
export function decode(str, encoding = "Auto-detect") {
// Check for empty input
if (!str || str.length === 0) {
throw new OperationError("Input cannot be empty.");
}
// Check maximum length
if (str.length > 90) {
throw new OperationError(`Invalid Bech32 string: exceeds maximum length of 90 characters (got ${str.length}).`);
}
// Check for mixed case
const hasUpper = /[A-Z]/.test(str);
const hasLower = /[a-z]/.test(str);
if (hasUpper && hasLower) {
throw new OperationError("Invalid Bech32 string: mixed case is not allowed. Use all uppercase or all lowercase.");
}
// Convert to lowercase for processing
str = str.toLowerCase();
// Find separator (last occurrence of '1')
const sepIndex = str.lastIndexOf("1");
if (sepIndex === -1) {
throw new OperationError("Invalid Bech32 string: no separator '1' found.");
}
if (sepIndex === 0) {
throw new OperationError("Invalid Bech32 string: Human-Readable Part (HRP) cannot be empty.");
}
if (sepIndex + 7 > str.length) {
throw new OperationError("Invalid Bech32 string: data part is too short (minimum 6 characters for checksum).");
}
// Extract HRP and data part
const hrp = str.substring(0, sepIndex);
const dataPart = str.substring(sepIndex + 1);
// Validate HRP characters
for (let i = 0; i < hrp.length; i++) {
const c = hrp.charCodeAt(i);
if (c < 33 || c > 126) {
throw new OperationError(`HRP contains invalid character at position ${i}.`);
}
}
// Decode data characters to 5-bit values
const data = [];
for (let i = 0; i < dataPart.length; i++) {
const c = dataPart[i];
if (CHARSET_REV[c] === undefined) {
throw new OperationError(`Invalid character '${c}' at position ${sepIndex + 1 + i}.`);
}
data.push(CHARSET_REV[c]);
}
// Verify checksum
let usedEncoding;
if (encoding === "Bech32") {
if (!verifyChecksum(hrp, data, "Bech32")) {
throw new OperationError("Invalid Bech32 checksum.");
}
usedEncoding = "Bech32";
} else if (encoding === "Bech32m") {
if (!verifyChecksum(hrp, data, "Bech32m")) {
throw new OperationError("Invalid Bech32m checksum.");
}
usedEncoding = "Bech32m";
} else {
// Auto-detect: try Bech32 first, then Bech32m
if (verifyChecksum(hrp, data, "Bech32")) {
usedEncoding = "Bech32";
} else if (verifyChecksum(hrp, data, "Bech32m")) {
usedEncoding = "Bech32m";
} else {
throw new OperationError("Invalid Bech32/Bech32m string: checksum verification failed.");
}
}
// Remove checksum (last 6 values)
const words = data.slice(0, data.length - 6);
// Check if this is likely a SegWit address (Bitcoin, Litecoin, etc.)
// For SegWit, the first 5-bit word is the witness version (0-16)
// and should be extracted separately, not bit-converted with the rest
const segwitHrps = ["bc", "tb", "ltc", "tltc", "bcrt"];
const couldBeSegWit = segwitHrps.includes(hrp) && words.length > 0 && words[0] <= 16;
let bytes;
let witnessVersion = null;
if (couldBeSegWit) {
// Try SegWit decode first
try {
witnessVersion = words[0];
const programWords = words.slice(1);
const programBytes = fromWords(programWords);
// Validate SegWit witness program length (20 or 32 bytes for v0, 2-40 for others)
const validV0 = witnessVersion === 0 && (programBytes.length === 20 || programBytes.length === 32);
const validOther = witnessVersion !== 0 && programBytes.length >= 2 && programBytes.length <= 40;
if (validV0 || validOther) {
// Valid SegWit address
bytes = [witnessVersion, ...programBytes];
} else {
// Not valid SegWit, fall back to generic decode
witnessVersion = null;
bytes = fromWords(words);
}
} catch (e) {
// SegWit decode failed, try generic decode
witnessVersion = null;
try {
bytes = fromWords(words);
} catch (e2) {
throw new OperationError(`Failed to decode data: ${e2.message}`);
}
}
} else {
// Generic Bech32: convert all words
try {
bytes = fromWords(words);
} catch (e) {
throw new OperationError(`Failed to decode data: ${e.message}`);
}
}
return {
hrp: hrp,
data: bytes,
encoding: usedEncoding,
witnessVersion: witnessVersion
};
}

View File

@ -91,9 +91,7 @@ export function toJA4(bytes) {
let alpn = "00";
for (const ext of tlsr.handshake.value.extensions.value) {
if (ext.type.value === "application_layer_protocol_negotiation") {
alpn = parseFirstALPNValue(ext.value.data);
alpn = alpn.charAt(0) + alpn.charAt(alpn.length - 1);
if (alpn.charCodeAt(0) > 127) alpn = "99";
alpn = alpnFingerprint(parseFirstALPNValue(ext.value.data));
break;
}
}
@ -212,9 +210,7 @@ export function toJA4S(bytes) {
let alpn = "00";
for (const ext of tlsr.handshake.value.extensions.value) {
if (ext.type.value === "application_layer_protocol_negotiation") {
alpn = parseFirstALPNValue(ext.value.data);
alpn = alpn.charAt(0) + alpn.charAt(alpn.length - 1);
if (alpn.charCodeAt(0) > 127) alpn = "99";
alpn = alpnFingerprint(parseFirstALPNValue(ext.value.data));
break;
}
}
@ -262,3 +258,33 @@ function tlsVersionMapper(version) {
default: return "00"; // Unknown
}
}
/**
* Checks if a byte is ASCII alphanumeric (0-9, A-Z, a-z).
* @param {number} byte
* @returns {boolean}
*/
function isAlphanumeric(byte) {
return (byte >= 0x30 && byte <= 0x39) ||
(byte >= 0x41 && byte <= 0x5A) ||
(byte >= 0x61 && byte <= 0x7A);
}
/**
* Computes the 2-character ALPN fingerprint from raw ALPN bytes.
* If both first and last bytes are ASCII alphanumeric, returns their characters.
* Otherwise, returns first hex digit of first byte + last hex digit of last byte.
* @param {Uint8Array|null} rawBytes
* @returns {string}
*/
function alpnFingerprint(rawBytes) {
if (!rawBytes || rawBytes.length === 0) return "00";
const firstByte = rawBytes[0];
const lastByte = rawBytes[rawBytes.length - 1];
if (isAlphanumeric(firstByte) && isAlphanumeric(lastByte)) {
return String.fromCharCode(firstByte) + String.fromCharCode(lastByte);
}
const firstHex = firstByte.toString(16).padStart(2, "0");
const lastHex = lastByte.toString(16).padStart(2, "0");
return firstHex[0] + lastHex[1];
}

258
src/core/lib/SM2.mjs Normal file
View File

@ -0,0 +1,258 @@
/**
* Utilities and operations utilized for SM2 encryption and decryption
* @author flakjacket95 [dflack95@gmail.com]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
import { fromHex } from "../lib/Hex.mjs";
import Utils from "../Utils.mjs";
import Sm3 from "crypto-api/src/hasher/sm3.mjs";
import {toHex} from "crypto-api/src/encoder/hex.mjs";
import r from "jsrsasign";
/**
* SM2 Class for encryption and decryption operations
*/
export class SM2 {
/**
* Constructor for SM2 class; sets up with the curve and the output format as specified in user args
*
* @param {*} curve
* @param {*} format
*/
constructor(curve, format) {
this.ecParams = null;
this.rng = new r.SecureRandom();
/*
For any additional curve definitions utilized by SM2, add another block like the below for that curve, then add the curve name to the Curve selection dropdown
*/
r.crypto.ECParameterDB.regist(
"sm2p256v1", // name / p = 2**256 - 2**224 - 2**96 + 2**64 - 1
256,
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", // p
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", // a
"28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", // b
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", // n
"1", // h
"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", // gx
"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", // gy
[]
); // alias
this.ecParams = r.crypto.ECParameterDB.getByName(curve);
this.format = format;
}
/**
* Set the public key coordinates for the SM2 class
*
* @param {string} publicKeyX
* @param {string} publicKeyY
*/
setPublicKey(publicKeyX, publicKeyY) {
/*
* TODO: This needs some additional length validation; and checking for errors in the decoding process
* TODO: Can probably support other public key encoding methods here as well in the future
*/
this.publicKey = this.ecParams.curve.decodePointHex("04" + publicKeyX + publicKeyY);
if (this.publicKey.isInfinity()) {
throw new OperationError("Invalid Public Key");
}
}
/**
* Set the private key value for the SM2 class
*
* @param {string} privateKey
*/
setPrivateKey(privateKeyHex) {
this.privateKey = new r.BigInteger(privateKeyHex, 16);
}
/**
* Main encryption function; takes user input, processes encryption and returns the result in hex (with the components arranged as configured by the user args)
*
* @param {*} input
* @returns {string}
*/
encrypt(input) {
const G = this.ecParams.G;
/*
* Compute a new, random public key along the same elliptic curve to form the starting point for our encryption process (record the resulting X and Y as hex to provide as part of the operation output)
* k: Randomly generated BigInteger
* c1: Result of dotting our curve generator point `G` with the value of `k`
*/
const k = this.generatePublicKey();
const c1 = G.multiply(k);
const [hexC1X, hexC1Y] = this.getPointAsHex(c1);
/*
* Compute p2 (secret) using the public key, and the chosen k value above
*/
const p2 = this.publicKey.multiply(k);
/*
* Compute the C3 SM3 hash before we transform the array
*/
const c3 = this.c3(p2, input);
/*
* Genreate a proper length encryption key, XOR iteratively, and convert newly encrypted data to hex
*/
const key = this.kdf(p2, input.byteLength);
for (let i = 0; i < input.byteLength; i++) {
input[i] ^= Utils.ord(key[i]);
}
const c2 = Buffer.from(input).toString("hex");
/*
* Check user input specs; order the output components as selected
*/
if (this.format === "C1C3C2") {
return hexC1X + hexC1Y + c3 + c2;
} else {
return hexC1X + hexC1Y + c2 + c3;
}
}
/**
* Function to decrypt an SM2 encrypted message
*
* @param {*} input
*/
decrypt(input) {
const c1X = input.slice(0, 64);
const c1Y = input.slice(64, 128);
let c3 = "";
let c2 = "";
if (this.format === "C1C3C2") {
c3 = input.slice(128, 192);
c2 = input.slice(192);
} else {
c2 = input.slice(128, -64);
c3 = input.slice(-64);
}
c2 = Uint8Array.from(fromHex(c2));
const c1 = this.ecParams.curve.decodePointHex("04" + c1X + c1Y);
/*
* Compute the p2 (secret) value by taking the C1 point provided in the encrypted package, and multiplying by the private k value
*/
const p2 = c1.multiply(this.privateKey);
/*
* Similar to encryption; compute sufficient length key material and XOR the input data to recover the original message
*/
const key = this.kdf(p2, c2.byteLength);
for (let i = 0; i < c2.byteLength; i++) {
c2[i] ^= Utils.ord(key[i]);
}
const check = this.c3(p2, c2);
if (check === c3) {
return c2.buffer;
} else {
throw new OperationError("Decryption Error -- Computed Hashes Do Not Match");
}
}
/**
* Generates a large random number
*
* @param {*} limit
* @returns
*/
getBigRandom(limit) {
return new r.BigInteger(limit.bitLength(), this.rng)
.mod(limit.subtract(r.BigInteger.ONE))
.add(r.BigInteger.ONE);
}
/**
* Helper function for generating a large random K number; utilized for generating our initial C1 point
* TODO: Do we need to do any sort of validation on the resulting k values?
*
* @returns {BigInteger}
*/
generatePublicKey() {
const n = this.ecParams.n;
const k = this.getBigRandom(n);
return k;
}
/**
* SM2 Key Derivation Function (KDF); Takes P2 point, and generates a key material stream large enough to encrypt all of the input data
*
* @param {*} p2
* @param {*} len
* @returns {string}
*/
kdf(p2, len) {
const [hX, hY] = this.getPointAsHex(p2);
const total = Math.ceil(len / 32) + 1;
let cnt = 1;
let keyMaterial = "";
while (cnt < total) {
const num = Utils.intToByteArray(cnt, 4, "big");
const overall = fromHex(hX).concat(fromHex(hY)).concat(num);
keyMaterial += this.sm3(overall);
cnt++;
}
return keyMaterial;
}
/**
* Calculates the C3 component of our final encrypted payload; which is the SM3 hash of the P2 point and the original, unencrypted input data
*
* @param {*} p2
* @param {*} input
* @returns {string}
*/
c3(p2, input) {
const [hX, hY] = this.getPointAsHex(p2);
const overall = fromHex(hX).concat(Array.from(input)).concat(fromHex(hY));
return toHex(this.sm3(overall));
}
/**
* SM3 setup helper function; takes input data as an array, processes the hash and returns the result
*
* @param {*} data
* @returns {string}
*/
sm3(data) {
const hashData = Utils.arrayBufferToStr(Uint8Array.from(data).buffer, false);
const hasher = new Sm3();
hasher.update(hashData);
return hasher.finalize();
}
/**
* Utility function, returns an elliptic curve points X and Y values as hex;
*
* @param {EcPointFp} point
* @returns {[]}
*/
getPointAsHex(point) {
const biX = point.getX().toBigInteger();
const biY = point.getY().toBigInteger();
const charlen = this.ecParams.keycharlen;
const hX = ("0000000000" + biX.toString(16)).slice(- charlen);
const hY = ("0000000000" + biY.toString(16)).slice(- charlen);
return [hX, hY];
}
}

View File

@ -863,15 +863,15 @@ export function parseHighestSupportedVersion(bytes) {
}
/**
* Parses the application_layer_protocol_negotiation extension and returns the first value.
* Parses the application_layer_protocol_negotiation extension and returns the first value as raw bytes.
* @param {Uint8Array} bytes
* @returns {number}
* @returns {Uint8Array|null}
*/
export function parseFirstALPNValue(bytes) {
const s = new Stream(bytes);
const alpnExtLen = s.readInt(2);
if (alpnExtLen < 3) return "00";
if (alpnExtLen < 2) return null;
const strLen = s.readInt(1);
if (strLen < 2) return "00";
return s.readString(strLen);
if (strLen < 1) return null;
return s.getBytes(strLen);
}

View File

@ -112,7 +112,7 @@ class AESDecrypt extends Operation {
run(input, args) {
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteString(args[1].string, args[1].option),
mode = args[2].substring(0, 3),
mode = args[2].split("/")[0],
noPadding = args[2].endsWith("NoPadding"),
inputType = args[3],
outputType = args[4],

View File

@ -66,6 +66,14 @@ class AESEncrypt extends Operation {
{
name: "ECB",
off: [5]
},
{
name: "CBC/NoPadding",
off: [5]
},
{
name: "ECB/NoPadding",
off: [5]
}
]
},
@ -98,7 +106,8 @@ class AESEncrypt extends Operation {
run(input, args) {
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteString(args[1].string, args[1].option),
mode = args[2],
mode = args[2].split("/")[0],
noPadding = args[2].endsWith("NoPadding"),
inputType = args[3],
outputType = args[4],
aad = Utils.convertToByteString(args[5].string, args[5].option);
@ -114,11 +123,20 @@ The following algorithms will be used based on the size of the key:
input = Utils.convertToByteString(input, inputType);
// Handle NoPadding modes
if (noPadding && input.length % 16 !== 0) {
throw new OperationError("Input length must be a multiple of 16 bytes for NoPadding modes.");
}
const cipher = forge.cipher.createCipher("AES-" + mode, key);
cipher.start({
iv: iv,
additionalData: mode === "GCM" ? aad : undefined
});
if (noPadding) {
cipher.mode.pad = function(output, options) {
return true;
};
}
cipher.update(forge.util.createBuffer(input));
cipher.finish();

View File

@ -0,0 +1,53 @@
/**
* @author sw5678
* @copyright Crown Copyright 2023
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
/**
* Alternating caps operation
*/
class AlternatingCaps extends Operation {
/**
* AlternatingCaps constructor
*/
constructor() {
super();
this.name = "Alternating Caps";
this.module = "Default";
this.description = "Alternating caps, also known as studly caps, sticky caps, or spongecase is a form of text notation in which the capitalization of letters varies by some pattern, or arbitrarily. An example of this would be spelling 'alternative caps' as 'aLtErNaTiNg CaPs'.";
this.infoURL = "https://en.wikipedia.org/wiki/Alternating_caps";
this.inputType = "string";
this.outputType = "string";
this.args= [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let output = "";
let previousCaps = true;
for (let i = 0; i < input.length; i++) {
// Check if the element is a letter
if (!RegExp(/^\p{L}/, "u").test(input[i])) {
output += input[i];
} else if (previousCaps) {
output += input[i].toLowerCase();
previousCaps = false;
} else {
output += input[i].toUpperCase();
previousCaps = true;
}
}
return output;
}
}
export default AlternatingCaps;

View File

@ -0,0 +1,48 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2023
* @license Apache-2.0
*/
import * as uuid from "uuid";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Analyse UUID operation
*/
class AnalyseUUID extends Operation {
/**
* AnalyseUUID constructor
*/
constructor() {
super();
this.name = "Analyse UUID";
this.module = "Crypto";
this.description = "Tries to determine information about a given UUID and suggests which version may have been used to generate it";
this.infoURL = "https://wikipedia.org/wiki/Universally_unique_identifier";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
try {
const uuidVersion = uuid.version(input);
return "UUID version: " + uuidVersion;
} catch (error) {
throw new OperationError("Invalid UUID");
}
}
}
export default AnalyseUUID;

View File

@ -0,0 +1,58 @@
/**
* @author xumptex [xumptex@outlook.fr]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { blake3 } from "hash-wasm";
/**
* BLAKE3 operation
*/
class BLAKE3 extends Operation {
/**
* BLAKE3 constructor
*/
constructor() {
super();
this.name = "BLAKE3";
this.module = "Hashing";
this.description = "Hashes the input using BLAKE3 (UTF-8 encoded), with an optional key (also UTF-8), and outputs the result in hexadecimal format.";
this.infoURL = "https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE3";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Size (bytes)",
"type": "number"
}, {
"name": "Key",
"type": "string",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const key = args[1];
const size = args[0];
// Check if the user want a key hash or not
if (key === "") {
return blake3(input, size*8);
} if (key.length !== 32) {
throw new OperationError("The key must be exactly 32 bytes long");
}
return blake3(input, size*8, key);
}
}
export default BLAKE3;

View File

@ -1,41 +0,0 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import JSCRC from "js-crc";
/**
* CRC-16 Checksum operation
*/
class CRC16Checksum extends Operation {
/**
* CRC16Checksum constructor
*/
constructor() {
super();
this.name = "CRC-16 Checksum";
this.module = "Crypto";
this.description = "A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data.<br><br>The CRC was invented by W. Wesley Peterson in 1961.";
this.infoURL = "https://wikipedia.org/wiki/Cyclic_redundancy_check";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return JSCRC.crc16(input);
}
}
export default CRC16Checksum;

View File

@ -1,41 +0,0 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import JSCRC from "js-crc";
/**
* CRC-32 Checksum operation
*/
class CRC32Checksum extends Operation {
/**
* CRC32Checksum constructor
*/
constructor() {
super();
this.name = "CRC-32 Checksum";
this.module = "Crypto";
this.description = "A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data.<br><br>The CRC was invented by W. Wesley Peterson in 1961; the 32-bit CRC function of Ethernet and many other standards is the work of several researchers and was published in 1975.";
this.infoURL = "https://wikipedia.org/wiki/Cyclic_redundancy_check";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return JSCRC.crc32(input);
}
}
export default CRC32Checksum;

View File

@ -1,157 +0,0 @@
/**
* @author mshwed [m@ttshwed.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { toHexFast } from "../lib/Hex.mjs";
/**
* CRC-8 Checksum operation
*/
class CRC8Checksum extends Operation {
/**
* CRC8Checksum constructor
*/
constructor() {
super();
this.name = "CRC-8 Checksum";
this.module = "Crypto";
this.description = "A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data.<br><br>The CRC was invented by W. Wesley Peterson in 1961.";
this.infoURL = "https://wikipedia.org/wiki/Cyclic_redundancy_check";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
"name": "Algorithm",
"type": "option",
"value": [
"CRC-8",
"CRC-8/CDMA2000",
"CRC-8/DARC",
"CRC-8/DVB-S2",
"CRC-8/EBU",
"CRC-8/I-CODE",
"CRC-8/ITU",
"CRC-8/MAXIM",
"CRC-8/ROHC",
"CRC-8/WCDMA"
]
}
];
}
/**
* Generates the pre-computed lookup table for byte division
*
* @param polynomial
*/
calculateCRC8LookupTable(polynomial) {
const crc8Table = new Uint8Array(256);
let currentByte;
for (let i = 0; i < 256; i++) {
currentByte = i;
for (let bit = 0; bit < 8; bit++) {
if ((currentByte & 0x80) !== 0) {
currentByte <<= 1;
currentByte ^= polynomial;
} else {
currentByte <<= 1;
}
}
crc8Table[i] = currentByte;
}
return crc8Table;
}
/**
* Calculates the CRC-8 Checksum from an input
*
* @param {ArrayBuffer} input
* @param {number} polynomial
* @param {number} initializationValue
* @param {boolean} inputReflection
* @param {boolean} outputReflection
* @param {number} xorOut
*/
calculateCRC8(input, polynomial, initializationValue, inputReflection, outputReflection, xorOut) {
const crcSize = 8;
const crcTable = this.calculateCRC8LookupTable(polynomial);
let crc = initializationValue !== 0 ? initializationValue : 0;
let currentByte, position;
input = new Uint8Array(input);
for (const inputByte of input) {
currentByte = inputReflection ? this.reverseBits(inputByte, crcSize) : inputByte;
position = (currentByte ^ crc) & 255;
crc = crcTable[position];
}
crc = outputReflection ? this.reverseBits(crc, crcSize) : crc;
if (xorOut !== 0) crc = crc ^ xorOut;
return toHexFast(new Uint8Array([crc]));
}
/**
* Reverse the bits for a given input byte.
*
* @param {number} input
*/
reverseBits(input, hashSize) {
let reversedByte = 0;
for (let i = hashSize - 1; i >= 0; i--) {
reversedByte |= ((input & 1) << i);
input >>= 1;
}
return reversedByte;
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const algorithm = args[0];
switch (algorithm) {
case "CRC-8":
return this.calculateCRC8(input, 0x7, 0x0, false, false, 0x0);
case "CRC-8/CDMA2000":
return this.calculateCRC8(input, 0x9B, 0xFF, false, false, 0x0);
case "CRC-8/DARC":
return this.calculateCRC8(input, 0x39, 0x0, true, true, 0x0);
case "CRC-8/DVB-S2":
return this.calculateCRC8(input, 0xD5, 0x0, false, false, 0x0);
case "CRC-8/EBU":
return this.calculateCRC8(input, 0x1D, 0xFF, true, true, 0x0);
case "CRC-8/I-CODE":
return this.calculateCRC8(input, 0x1D, 0xFD, false, false, 0x0);
case "CRC-8/ITU":
return this.calculateCRC8(input, 0x7, 0x0, false, false, 0x55);
case "CRC-8/MAXIM":
return this.calculateCRC8(input, 0x31, 0x0, true, true, 0x0);
case "CRC-8/ROHC":
return this.calculateCRC8(input, 0x7, 0xFF, true, true, 0x0);
case "CRC-8/WCDMA":
return this.calculateCRC8(input, 0x9B, 0x0, true, true, 0x0);
default:
throw new OperationError("Unknown checksum algorithm");
}
}
}
export default CRC8Checksum;

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,7 @@ class ConvertLeetSpeak extends Operation {
this.name = "Convert Leet Speak";
this.module = "Default";
this.description = "Converts to and from Leet Speak";
this.description = "Converts to and from Leet Speak.";
this.infoURL = "https://wikipedia.org/wiki/Leet";
this.inputType = "string";
this.outputType = "string";
@ -39,13 +39,16 @@ class ConvertLeetSpeak extends Operation {
*/
run(input, args) {
const direction = args[0];
if (direction === "To Leet Speak") {
return input.replace(/[abcdefghijklmnopqrstuvwxyz]/gi, char => {
return toLeetMap[char.toLowerCase()] || char;
return input.replace(/[a-z]/gi, char => {
const leetChar = toLeetMap[char.toLowerCase()] || char;
return char === char.toUpperCase() ? leetChar.toUpperCase() : leetChar;
});
} else if (direction === "From Leet Speak") {
return input.replace(/[48cd3f6h1jklmn0pqr57uvwxyz]/g, char => {
return fromLeetMap[char] || char;
return input.replace(/[48cd3f6h1jklmn0pqr57uvwxyz]/gi, char => {
const normalChar = fromLeetMap[char] || char;
return normalChar;
});
}
}

View File

@ -9,6 +9,7 @@ import OperationError from "../errors/OperationError.mjs";
import { fromBase64 } from "../lib/Base64.mjs";
import { toHexFast } from "../lib/Hex.mjs";
import r from "jsrsasign";
import Utils from "../Utils.mjs";
/**
* ECDSA Verify operation
@ -59,6 +60,11 @@ class ECDSAVerify extends Operation {
name: "Message",
type: "text",
value: ""
},
{
name: "Message format",
type: "option",
value: ["Raw", "Hex", "Base64"]
}
];
}
@ -70,7 +76,7 @@ class ECDSAVerify extends Operation {
*/
run(input, args) {
let inputFormat = args[0];
const [, mdAlgo, keyPem, msg] = args;
const [, mdAlgo, keyPem, msg, msgFormat] = args;
if (keyPem.replace("-----BEGIN PUBLIC KEY-----", "").length === 0) {
throw new OperationError("Please enter a public key.");
@ -145,7 +151,8 @@ class ECDSAVerify extends Operation {
throw new OperationError("Provided key is not a public key.");
}
sig.init(key);
sig.updateString(msg);
const messageStr = Utils.convertToByteString(msg, msgFormat);
sig.updateString(messageStr);
const result = sig.verify(signatureASN1Hex);
return result ? "Verified OK" : "Verification Failure";
}

View File

@ -51,7 +51,7 @@ class ExtractEmailAddresses extends Operation {
run(input, args) {
const [displayTotal, sort, unique] = args,
// 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;
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}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\])/ig;
const results = search(
input,

View File

@ -21,7 +21,7 @@ class ExtractIPAddresses extends Operation {
this.name = "Extract IP addresses";
this.module = "Regex";
this.description = "Extracts all IPv4 and IPv6 addresses.<br><br>Warning: Given a string <code>710.65.0.456</code>, this will match <code>10.65.0.45</code> so always check the original input!";
this.description = "Extracts all IPv4 and IPv6 addresses.<br><br>Warning: Given a string <code>1.2.3.4.5.6.7.8</code>, this will match <code>1.2.3.4 and 5.6.7.8</code> so always check the original input!";
this.inputType = "string";
this.outputType = "string";
this.args = [
@ -65,7 +65,21 @@ class ExtractIPAddresses extends Operation {
*/
run(input, args) {
const [includeIpv4, includeIpv6, removeLocal, displayTotal, sort, unique] = args,
ipv4 = "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?",
// IPv4 decimal groups can have values 0 to 255. To construct a regex the following sub-regex is reused:
ipv4DecimalByte = "(?:25[0-5]|2[0-4]\\d|1?[0-9]\\d|\\d)",
ipv4OctalByte = "(?:0[1-3]?[0-7]{1,2})",
// Look behind and ahead will be used to exclude matches with additional decimal digits left and right of IP address
lookBehind = "(?<!\\d)",
lookAhead = "(?!\\d)",
// Each variant requires exactly 4 groups with literal . between.
ipv4Decimal = "(?:" + lookBehind + ipv4DecimalByte + "\\.){3}" + "(?:" + ipv4DecimalByte + lookAhead + ")",
ipv4Octal = "(?:" + lookBehind + ipv4OctalByte + "\\.){3}" + "(?:" + ipv4OctalByte + lookAhead + ")",
// Then we allow IPv4 addresses to be expressed either entirely in decimal or entirely in Octal
ipv4 = "(?:" + ipv4Decimal + "|" + ipv4Octal + ")",
ipv6 = "((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})(([\\dA-F]{1,4}((?!\\3)::|:\\b|(?![\\dA-F])))|(?!\\2\\3)){2}";
let ips = "";

View File

@ -6,6 +6,8 @@
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import {ALPHABET_OPTIONS} from "../lib/Base32.mjs";
/**
* From Base32 operation
@ -27,8 +29,8 @@ class FromBase32 extends Operation {
this.args = [
{
name: "Alphabet",
type: "binaryString",
value: "A-Z2-7="
type: "editableOption",
value: ALPHABET_OPTIONS
},
{
name: "Remove non-alphabet chars",
@ -41,6 +43,11 @@ class FromBase32 extends Operation {
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]
},
{
pattern: "^(?:[0-9A-V]{8})+(?:[0-9A-V]{2}={6}|[0-9A-V]{4}={4}|[0-9A-V]{5}={3}|[0-9A-V]{7}={1})?$",
flags: "",
args: ["0-9A-V=", false]
}
];
}
@ -96,3 +103,4 @@ class FromBase32 extends Operation {
}
export default FromBase32;

View File

@ -0,0 +1,149 @@
/**
* @author Medjedtxm
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import { decode } from "../lib/Bech32.mjs";
import { toHex } from "../lib/Hex.mjs";
/**
* From Bech32 operation
*/
class FromBech32 extends Operation {
/**
* FromBech32 constructor
*/
constructor() {
super();
this.name = "From Bech32";
this.module = "Default";
this.description = "Bech32 is an encoding scheme primarily used for Bitcoin SegWit addresses (BIP-0173). It uses a 32-character alphabet that excludes easily confused characters (1, b, i, o) and includes a checksum for error detection.<br><br>Bech32m (BIP-0350) is an updated version used for Bitcoin Taproot addresses.<br><br>Auto-detect will attempt Bech32 first, then Bech32m if the checksum fails.<br><br>Output format options allow you to see the Human-Readable Part (HRP) along with the decoded data.";
this.infoURL = "https://wikipedia.org/wiki/Bech32";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Encoding",
"type": "option",
"value": ["Auto-detect", "Bech32", "Bech32m"]
},
{
"name": "Output Format",
"type": "option",
"value": ["Raw", "Hex", "Bitcoin scriptPubKey", "HRP: Hex", "JSON"]
}
];
this.checks = [
{
// Bitcoin mainnet SegWit/Taproot addresses
pattern: "^bc1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{6,87}$",
flags: "i",
args: ["Auto-detect", "Hex"]
},
{
// Bitcoin testnet addresses
pattern: "^tb1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{6,87}$",
flags: "i",
args: ["Auto-detect", "Hex"]
},
{
// AGE public keys
pattern: "^age1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{6,87}$",
flags: "i",
args: ["Auto-detect", "HRP: Hex"]
},
{
// AGE secret keys
pattern: "^AGE-SECRET-KEY-1[QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L]{6,87}$",
flags: "",
args: ["Auto-detect", "HRP: Hex"]
},
{
// Litecoin mainnet addresses
pattern: "^ltc1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{6,87}$",
flags: "i",
args: ["Auto-detect", "Hex"]
},
{
// Generic bech32 pattern
pattern: "^[a-z]{1,83}1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{6,}$",
flags: "i",
args: ["Auto-detect", "Hex"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const encoding = args[0];
const outputFormat = args[1];
input = input.trim();
if (input.length === 0) {
return "";
}
const decoded = decode(input, encoding);
// Format output based on selected option
switch (outputFormat) {
case "Raw":
return decoded.data.map(b => String.fromCharCode(b)).join("");
case "Hex":
return toHex(decoded.data, "");
case "Bitcoin scriptPubKey": {
// Convert to Bitcoin scriptPubKey format as shown in BIP-0173/BIP-0350
// Format: [OP_version][length][witness_program]
// OP_0 = 0x00, OP_1-OP_16 = 0x51-0x60
if (decoded.witnessVersion === null || decoded.data.length < 2) {
// Not a SegWit address, fall back to hex
return toHex(decoded.data, "");
}
const witnessVersion = decoded.data[0];
const witnessProgram = decoded.data.slice(1);
// Convert witness version to OP code
let opCode;
if (witnessVersion === 0) {
opCode = 0x00; // OP_0
} else if (witnessVersion >= 1 && witnessVersion <= 16) {
opCode = 0x50 + witnessVersion; // OP_1 = 0x51, ..., OP_16 = 0x60
} else {
// Invalid witness version, fall back to hex
return toHex(decoded.data, "");
}
// Build scriptPubKey: [OP_version][length][program]
const scriptPubKey = [opCode, witnessProgram.length, ...witnessProgram];
return toHex(scriptPubKey, "");
}
case "HRP: Hex":
return `${decoded.hrp}: ${toHex(decoded.data, "")}`;
case "JSON":
return JSON.stringify({
hrp: decoded.hrp,
encoding: decoded.encoding,
data: toHex(decoded.data, "")
}, null, 2);
default:
return toHex(decoded.data, "");
}
}
}
export default FromBech32;

View File

@ -43,7 +43,7 @@ class FromHexdump extends Operation {
*/
run(input, args) {
const output = [],
regex = /^\s*(?:[\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})/igm;
regex = /^\s*(?:[\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]{2})?|(?:[\dA-F]{2} )*[\dA-F]{2})/igm;
let block, line;
while ((block = regex.exec(input))) {

View File

@ -0,0 +1,254 @@
/**
* @author r4mos [2k95ljkhg@mozmail.com]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Adler32Checksum from "./Adler32Checksum.mjs";
import CRCChecksum from "./CRCChecksum.mjs";
import Fletcher8Checksum from "./Fletcher8Checksum.mjs";
import Fletcher16Checksum from "./Fletcher16Checksum.mjs";
import Fletcher32Checksum from "./Fletcher32Checksum.mjs";
import Fletcher64Checksum from "./Fletcher64Checksum.mjs";
/**
* Generate all checksums operation
*/
class GenerateAllChecksums extends Operation {
/**
* GenerateAllChecksums constructor
*/
constructor() {
super();
this.name = "Generate all checksums";
this.module = "Crypto";
this.description = "Generates all available checksums for the input.";
this.infoURL = "https://wikipedia.org/wiki/Checksum";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
name: "Length (bits)",
type: "option",
value: [
"All", "3", "4", "5", "6", "7", "8", "10", "11", "12", "13", "14", "15", "16", "17", "21", "24", "30", "31", "32", "40", "64", "82"
]
},
{
name: "Include names",
type: "boolean",
value: true
},
];
const adler32 = new Adler32Checksum;
const crc = new CRCChecksum;
const fletcher8 = new Fletcher8Checksum;
const fletcher16 = new Fletcher16Checksum;
const fletcher32 = new Fletcher32Checksum;
const fletcher64 = new Fletcher64Checksum;
this.checksums = [
{name: "CRC-3/GSM", algo: crc, params: ["CRC-3/GSM"]},
{name: "CRC-3/ROHC", algo: crc, params: ["CRC-3/ROHC"]},
{name: "CRC-4/G-704", algo: crc, params: ["CRC-4/G-704"]},
{name: "CRC-4/INTERLAKEN", algo: crc, params: ["CRC-4/INTERLAKEN"]},
{name: "CRC-4/ITU", algo: crc, params: ["CRC-4/ITU"]},
{name: "CRC-5/EPC", algo: crc, params: ["CRC-5/EPC"]},
{name: "CRC-5/EPC-C1G2", algo: crc, params: ["CRC-5/EPC-C1G2"]},
{name: "CRC-5/G-704", algo: crc, params: ["CRC-5/G-704"]},
{name: "CRC-5/ITU", algo: crc, params: ["CRC-5/ITU"]},
{name: "CRC-5/USB", algo: crc, params: ["CRC-5/USB"]},
{name: "CRC-6/CDMA2000-A", algo: crc, params: ["CRC-6/CDMA2000-A"]},
{name: "CRC-6/CDMA2000-B", algo: crc, params: ["CRC-6/CDMA2000-B"]},
{name: "CRC-6/DARC", algo: crc, params: ["CRC-6/DARC"]},
{name: "CRC-6/G-704", algo: crc, params: ["CRC-6/G-704"]},
{name: "CRC-6/GSM", algo: crc, params: ["CRC-6/GSM"]},
{name: "CRC-6/ITU", algo: crc, params: ["CRC-6/ITU"]},
{name: "CRC-7/MMC", algo: crc, params: ["CRC-7/MMC"]},
{name: "CRC-7/ROHC", algo: crc, params: ["CRC-7/ROHC"]},
{name: "CRC-7/UMTS", algo: crc, params: ["CRC-7/UMTS"]},
{name: "CRC-8", algo: crc, params: ["CRC-8"]},
{name: "CRC-8/8H2F", algo: crc, params: ["CRC-8/8H2F"]},
{name: "CRC-8/AES", algo: crc, params: ["CRC-8/AES"]},
{name: "CRC-8/AUTOSAR", algo: crc, params: ["CRC-8/AUTOSAR"]},
{name: "CRC-8/BLUETOOTH", algo: crc, params: ["CRC-8/BLUETOOTH"]},
{name: "CRC-8/CDMA2000", algo: crc, params: ["CRC-8/CDMA2000"]},
{name: "CRC-8/DARC", algo: crc, params: ["CRC-8/DARC"]},
{name: "CRC-8/DVB-S2", algo: crc, params: ["CRC-8/DVB-S2"]},
{name: "CRC-8/EBU", algo: crc, params: ["CRC-8/EBU"]},
{name: "CRC-8/GSM-A", algo: crc, params: ["CRC-8/GSM-A"]},
{name: "CRC-8/GSM-B", algo: crc, params: ["CRC-8/GSM-B"]},
{name: "CRC-8/HITAG", algo: crc, params: ["CRC-8/HITAG"]},
{name: "CRC-8/I-432-1", algo: crc, params: ["CRC-8/I-432-1"]},
{name: "CRC-8/I-CODE", algo: crc, params: ["CRC-8/I-CODE"]},
{name: "CRC-8/ITU", algo: crc, params: ["CRC-8/ITU"]},
{name: "CRC-8/LTE", algo: crc, params: ["CRC-8/LTE"]},
{name: "CRC-8/MAXIM", algo: crc, params: ["CRC-8/MAXIM"]},
{name: "CRC-8/MAXIM-DOW", algo: crc, params: ["CRC-8/MAXIM-DOW"]},
{name: "CRC-8/MIFARE-MAD", algo: crc, params: ["CRC-8/MIFARE-MAD"]},
{name: "CRC-8/NRSC-5", algo: crc, params: ["CRC-8/NRSC-5"]},
{name: "CRC-8/OPENSAFETY", algo: crc, params: ["CRC-8/OPENSAFETY"]},
{name: "CRC-8/ROHC", algo: crc, params: ["CRC-8/ROHC"]},
{name: "CRC-8/SAE-J1850", algo: crc, params: ["CRC-8/SAE-J1850"]},
{name: "CRC-8/SAE-J1850-ZERO", algo: crc, params: ["CRC-8/SAE-J1850-ZERO"]},
{name: "CRC-8/SMBUS", algo: crc, params: ["CRC-8/SMBUS"]},
{name: "CRC-8/TECH-3250", algo: crc, params: ["CRC-8/TECH-3250"]},
{name: "CRC-8/WCDMA", algo: crc, params: ["CRC-8/WCDMA"]},
{name: "Fletcher-8", algo: fletcher8, params: []},
{name: "CRC-10/ATM", algo: crc, params: ["CRC-10/ATM"]},
{name: "CRC-10/CDMA2000", algo: crc, params: ["CRC-10/CDMA2000"]},
{name: "CRC-10/GSM", algo: crc, params: ["CRC-10/GSM"]},
{name: "CRC-10/I-610", algo: crc, params: ["CRC-10/I-610"]},
{name: "CRC-11/FLEXRAY", algo: crc, params: ["CRC-11/FLEXRAY"]},
{name: "CRC-11/UMTS", algo: crc, params: ["CRC-11/UMTS"]},
{name: "CRC-12/3GPP", algo: crc, params: ["CRC-12/3GPP"]},
{name: "CRC-12/CDMA2000", algo: crc, params: ["CRC-12/CDMA2000"]},
{name: "CRC-12/DECT", algo: crc, params: ["CRC-12/DECT"]},
{name: "CRC-12/GSM", algo: crc, params: ["CRC-12/GSM"]},
{name: "CRC-12/UMTS", algo: crc, params: ["CRC-12/UMTS"]},
{name: "CRC-13/BBC", algo: crc, params: ["CRC-13/BBC"]},
{name: "CRC-14/DARC", algo: crc, params: ["CRC-14/DARC"]},
{name: "CRC-14/GSM", algo: crc, params: ["CRC-14/GSM"]},
{name: "CRC-15/CAN", algo: crc, params: ["CRC-15/CAN"]},
{name: "CRC-15/MPT1327", algo: crc, params: ["CRC-15/MPT1327"]},
{name: "CRC-16", algo: crc, params: ["CRC-16"]},
{name: "CRC-16/A", algo: crc, params: ["CRC-16/A"]},
{name: "CRC-16/ACORN", algo: crc, params: ["CRC-16/ACORN"]},
{name: "CRC-16/ARC", algo: crc, params: ["CRC-16/ARC"]},
{name: "CRC-16/AUG-CCITT", algo: crc, params: ["CRC-16/AUG-CCITT"]},
{name: "CRC-16/AUTOSAR", algo: crc, params: ["CRC-16/AUTOSAR"]},
{name: "CRC-16/B", algo: crc, params: ["CRC-16/B"]},
{name: "CRC-16/BLUETOOTH", algo: crc, params: ["CRC-16/BLUETOOTH"]},
{name: "CRC-16/BUYPASS", algo: crc, params: ["CRC-16/BUYPASS"]},
{name: "CRC-16/CCITT", algo: crc, params: ["CRC-16/CCITT"]},
{name: "CRC-16/CCITT-FALSE", algo: crc, params: ["CRC-16/CCITT-FALSE"]},
{name: "CRC-16/CCITT-TRUE", algo: crc, params: ["CRC-16/CCITT-TRUE"]},
{name: "CRC-16/CCITT-ZERO", algo: crc, params: ["CRC-16/CCITT-ZERO"]},
{name: "CRC-16/CDMA2000", algo: crc, params: ["CRC-16/CDMA2000"]},
{name: "CRC-16/CMS", algo: crc, params: ["CRC-16/CMS"]},
{name: "CRC-16/DARC", algo: crc, params: ["CRC-16/DARC"]},
{name: "CRC-16/DDS-110", algo: crc, params: ["CRC-16/DDS-110"]},
{name: "CRC-16/DECT-R", algo: crc, params: ["CRC-16/DECT-R"]},
{name: "CRC-16/DECT-X", algo: crc, params: ["CRC-16/DECT-X"]},
{name: "CRC-16/DNP", algo: crc, params: ["CRC-16/DNP"]},
{name: "CRC-16/EN-13757", algo: crc, params: ["CRC-16/EN-13757"]},
{name: "CRC-16/EPC", algo: crc, params: ["CRC-16/EPC"]},
{name: "CRC-16/EPC-C1G2", algo: crc, params: ["CRC-16/EPC-C1G2"]},
{name: "CRC-16/GENIBUS", algo: crc, params: ["CRC-16/GENIBUS"]},
{name: "CRC-16/GSM", algo: crc, params: ["CRC-16/GSM"]},
{name: "CRC-16/I-CODE", algo: crc, params: ["CRC-16/I-CODE"]},
{name: "CRC-16/IBM", algo: crc, params: ["CRC-16/IBM"]},
{name: "CRC-16/IBM-3740", algo: crc, params: ["CRC-16/IBM-3740"]},
{name: "CRC-16/IBM-SDLC", algo: crc, params: ["CRC-16/IBM-SDLC"]},
{name: "CRC-16/IEC-61158-2", algo: crc, params: ["CRC-16/IEC-61158-2"]},
{name: "CRC-16/ISO-HDLC", algo: crc, params: ["CRC-16/ISO-HDLC"]},
{name: "CRC-16/ISO-IEC-14443-3-A", algo: crc, params: ["CRC-16/ISO-IEC-14443-3-A"]},
{name: "CRC-16/ISO-IEC-14443-3-B", algo: crc, params: ["CRC-16/ISO-IEC-14443-3-B"]},
{name: "CRC-16/KERMIT", algo: crc, params: ["CRC-16/KERMIT"]},
{name: "CRC-16/LHA", algo: crc, params: ["CRC-16/LHA"]},
{name: "CRC-16/LJ1200", algo: crc, params: ["CRC-16/LJ1200"]},
{name: "CRC-16/LTE", algo: crc, params: ["CRC-16/LTE"]},
{name: "CRC-16/M17", algo: crc, params: ["CRC-16/M17"]},
{name: "CRC-16/MAXIM", algo: crc, params: ["CRC-16/MAXIM"]},
{name: "CRC-16/MAXIM-DOW", algo: crc, params: ["CRC-16/MAXIM-DOW"]},
{name: "CRC-16/MCRF4XX", algo: crc, params: ["CRC-16/MCRF4XX"]},
{name: "CRC-16/MODBUS", algo: crc, params: ["CRC-16/MODBUS"]},
{name: "CRC-16/NRSC-5", algo: crc, params: ["CRC-16/NRSC-5"]},
{name: "CRC-16/OPENSAFETY-A", algo: crc, params: ["CRC-16/OPENSAFETY-A"]},
{name: "CRC-16/OPENSAFETY-B", algo: crc, params: ["CRC-16/OPENSAFETY-B"]},
{name: "CRC-16/PROFIBUS", algo: crc, params: ["CRC-16/PROFIBUS"]},
{name: "CRC-16/RIELLO", algo: crc, params: ["CRC-16/RIELLO"]},
{name: "CRC-16/SPI-FUJITSU", algo: crc, params: ["CRC-16/SPI-FUJITSU"]},
{name: "CRC-16/T10-DIF", algo: crc, params: ["CRC-16/T10-DIF"]},
{name: "CRC-16/TELEDISK", algo: crc, params: ["CRC-16/TELEDISK"]},
{name: "CRC-16/TMS37157", algo: crc, params: ["CRC-16/TMS37157"]},
{name: "CRC-16/UMTS", algo: crc, params: ["CRC-16/UMTS"]},
{name: "CRC-16/USB", algo: crc, params: ["CRC-16/USB"]},
{name: "CRC-16/V-41-LSB", algo: crc, params: ["CRC-16/V-41-LSB"]},
{name: "CRC-16/V-41-MSB", algo: crc, params: ["CRC-16/V-41-MSB"]},
{name: "CRC-16/VERIFONE", algo: crc, params: ["CRC-16/VERIFONE"]},
{name: "CRC-16/X-25", algo: crc, params: ["CRC-16/X-25"]},
{name: "CRC-16/XMODEM", algo: crc, params: ["CRC-16/XMODEM"]},
{name: "CRC-16/ZMODEM", algo: crc, params: ["CRC-16/ZMODEM"]},
{name: "Fletcher-16", algo: fletcher16, params: []},
{name: "CRC-17/CAN-FD", algo: crc, params: ["CRC-17/CAN-FD"]},
{name: "CRC-21/CAN-FD", algo: crc, params: ["CRC-21/CAN-FD"]},
{name: "CRC-24/BLE", algo: crc, params: ["CRC-24/BLE"]},
{name: "CRC-24/FLEXRAY-A", algo: crc, params: ["CRC-24/FLEXRAY-A"]},
{name: "CRC-24/FLEXRAY-B", algo: crc, params: ["CRC-24/FLEXRAY-B"]},
{name: "CRC-24/INTERLAKEN", algo: crc, params: ["CRC-24/INTERLAKEN"]},
{name: "CRC-24/LTE-A", algo: crc, params: ["CRC-24/LTE-A"]},
{name: "CRC-24/LTE-B", algo: crc, params: ["CRC-24/LTE-B"]},
{name: "CRC-24/OPENPGP", algo: crc, params: ["CRC-24/OPENPGP"]},
{name: "CRC-24/OS-9", algo: crc, params: ["CRC-24/OS-9"]},
{name: "CRC-30/CDMA", algo: crc, params: ["CRC-30/CDMA"]},
{name: "CRC-31/PHILIPS", algo: crc, params: ["CRC-31/PHILIPS"]},
{name: "Adler-32", algo: adler32, params: []},
{name: "CRC-32", algo: crc, params: ["CRC-32"]},
{name: "CRC-32/AAL5", algo: crc, params: ["CRC-32/AAL5"]},
{name: "CRC-32/ADCCP", algo: crc, params: ["CRC-32/ADCCP"]},
{name: "CRC-32/AIXM", algo: crc, params: ["CRC-32/AIXM"]},
{name: "CRC-32/AUTOSAR", algo: crc, params: ["CRC-32/AUTOSAR"]},
{name: "CRC-32/BASE91-C", algo: crc, params: ["CRC-32/BASE91-C"]},
{name: "CRC-32/BASE91-D", algo: crc, params: ["CRC-32/BASE91-D"]},
{name: "CRC-32/BZIP2", algo: crc, params: ["CRC-32/BZIP2"]},
{name: "CRC-32/C", algo: crc, params: ["CRC-32/C"]},
{name: "CRC-32/CASTAGNOLI", algo: crc, params: ["CRC-32/CASTAGNOLI"]},
{name: "CRC-32/CD-ROM-EDC", algo: crc, params: ["CRC-32/CD-ROM-EDC"]},
{name: "CRC-32/CKSUM", algo: crc, params: ["CRC-32/CKSUM"]},
{name: "CRC-32/D", algo: crc, params: ["CRC-32/D"]},
{name: "CRC-32/DECT-B", algo: crc, params: ["CRC-32/DECT-B"]},
{name: "CRC-32/INTERLAKEN", algo: crc, params: ["CRC-32/INTERLAKEN"]},
{name: "CRC-32/ISCSI", algo: crc, params: ["CRC-32/ISCSI"]},
{name: "CRC-32/ISO-HDLC", algo: crc, params: ["CRC-32/ISO-HDLC"]},
{name: "CRC-32/JAMCRC", algo: crc, params: ["CRC-32/JAMCRC"]},
{name: "CRC-32/MEF", algo: crc, params: ["CRC-32/MEF"]},
{name: "CRC-32/MPEG-2", algo: crc, params: ["CRC-32/MPEG-2"]},
{name: "CRC-32/NVME", algo: crc, params: ["CRC-32/NVME"]},
{name: "CRC-32/PKZIP", algo: crc, params: ["CRC-32/PKZIP"]},
{name: "CRC-32/POSIX", algo: crc, params: ["CRC-32/POSIX"]},
{name: "CRC-32/Q", algo: crc, params: ["CRC-32/Q"]},
{name: "CRC-32/SATA", algo: crc, params: ["CRC-32/SATA"]},
{name: "CRC-32/V-42", algo: crc, params: ["CRC-32/V-42"]},
{name: "CRC-32/XFER", algo: crc, params: ["CRC-32/XFER"]},
{name: "CRC-32/XZ", algo: crc, params: ["CRC-32/XZ"]},
{name: "Fletcher-32", algo: fletcher32, params: []},
{name: "CRC-40/GSM", algo: crc, params: ["CRC-40/GSM"]},
{name: "CRC-64/ECMA-182", algo: crc, params: ["CRC-64/ECMA-182"]},
{name: "CRC-64/GO-ECMA", algo: crc, params: ["CRC-64/GO-ECMA"]},
{name: "CRC-64/GO-ISO", algo: crc, params: ["CRC-64/GO-ISO"]},
{name: "CRC-64/MS", algo: crc, params: ["CRC-64/MS"]},
{name: "CRC-64/NVME", algo: crc, params: ["CRC-64/NVME"]},
{name: "CRC-64/REDIS", algo: crc, params: ["CRC-64/REDIS"]},
{name: "CRC-64/WE", algo: crc, params: ["CRC-64/WE"]},
{name: "CRC-64/XZ", algo: crc, params: ["CRC-64/XZ"]},
{name: "Fletcher-64", algo: fletcher64, params: []},
{name: "CRC-82/DARC", algo: crc, params: ["CRC-82/DARC"]}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [length, includeNames] = args;
let output = "";
this.checksums.forEach(checksum => {
const checksumLength = checksum.name.match(new RegExp("-(\\d{1,2})(\\/|$)"))[1];
if (length === "All" || length === checksumLength) {
const value = checksum.algo.run(new Uint8Array(input), checksum.params || []);
output += includeNames ?
`${checksum.name}:${" ".repeat(25-checksum.name.length)}${value}\n`:
`${value}\n`;
}
});
return output;
}
}
export default GenerateAllChecksums;

View File

@ -22,14 +22,6 @@ import HAS160 from "./HAS160.mjs";
import Whirlpool from "./Whirlpool.mjs";
import SSDEEP from "./SSDEEP.mjs";
import CTPH from "./CTPH.mjs";
import Fletcher8Checksum from "./Fletcher8Checksum.mjs";
import Fletcher16Checksum from "./Fletcher16Checksum.mjs";
import Fletcher32Checksum from "./Fletcher32Checksum.mjs";
import Fletcher64Checksum from "./Fletcher64Checksum.mjs";
import Adler32Checksum from "./Adler32Checksum.mjs";
import CRC8Checksum from "./CRC8Checksum.mjs";
import CRC16Checksum from "./CRC16Checksum.mjs";
import CRC32Checksum from "./CRC32Checksum.mjs";
import BLAKE2b from "./BLAKE2b.mjs";
import BLAKE2s from "./BLAKE2s.mjs";
import Streebog from "./Streebog.mjs";
@ -114,16 +106,6 @@ class GenerateAllHashes extends Operation {
{name: "SSDEEP", algo: (new SSDEEP()), inputType: "str"},
{name: "CTPH", algo: (new CTPH()), inputType: "str"}
];
this.checksums = [
{name: "Fletcher-8", algo: (new Fletcher8Checksum), inputType: "byteArray", params: []},
{name: "Fletcher-16", algo: (new Fletcher16Checksum), inputType: "byteArray", params: []},
{name: "Fletcher-32", algo: (new Fletcher32Checksum), inputType: "byteArray", params: []},
{name: "Fletcher-64", algo: (new Fletcher64Checksum), inputType: "byteArray", params: []},
{name: "Adler-32", algo: (new Adler32Checksum), inputType: "byteArray", params: []},
{name: "CRC-8", algo: (new CRC8Checksum), inputType: "arrayBuffer", params: ["CRC-8"]},
{name: "CRC-16", algo: (new CRC16Checksum), inputType: "arrayBuffer", params: []},
{name: "CRC-32", algo: (new CRC32Checksum), inputType: "arrayBuffer", params: []}
];
}
/**
@ -144,14 +126,6 @@ class GenerateAllHashes extends Operation {
output += this.formatDigest(digest, length, includeNames, hash.name);
});
if (length === "All") {
output += "\nChecksums:\n";
this.checksums.forEach(checksum => {
digest = this.executeAlgo(checksum.algo, checksum.inputType, checksum.params || []);
output += this.formatDigest(digest, length, includeNames, checksum.name);
});
}
return output;
}

View File

@ -5,8 +5,8 @@
*/
import Operation from "../Operation.mjs";
import crypto from "crypto";
import * as uuid from "uuid";
import OperationError from "../errors/OperationError.mjs";
/**
* Generate UUID operation
*/
@ -20,11 +20,38 @@ class GenerateUUID extends Operation {
this.name = "Generate UUID";
this.module = "Crypto";
this.description = "Generates an RFC 4122 version 4 compliant Universally Unique Identifier (UUID), also known as a Globally Unique Identifier (GUID).<br><br>A version 4 UUID relies on random numbers, in this case generated using <code>window.crypto</code> if available and falling back to <code>Math.random</code> if not.";
this.description =
"Generates an RFC 9562 (formerly RFC 4122) compliant Universally Unique Identifier (UUID), " +
"also known as a Globally Unique Identifier (GUID).<br>" +
"<br>" +
"We currently support generating the following UUID versions:<br>" +
"<ul>" +
"<li><strong>v1</strong>: Timestamp-based</li>" +
"<li><strong>v3</strong>: Namespace w/ MD5</li>" +
"<li><strong>v4</strong>: Random (default)</li>" +
"<li><strong>v5</strong>: Namespace w/ SHA-1</li>" +
"<li><strong>v6</strong>: Timestamp, reordered</li>" +
"<li><strong>v7</strong>: Unix Epoch time-based</li>" +
"</ul>" +
"UUIDs are generated using the <a href='https://npmjs.org/uuid/'><code>uuid</code><a> package.<br>";
this.infoURL = "https://wikipedia.org/wiki/Universally_unique_identifier";
this.inputType = "string";
this.outputType = "string";
this.args = [];
this.args = [
{
name: "Version",
hint: "UUID version",
type: "option",
value: ["v1", "v3", "v4", "v5", "v6", "v7"],
defaultIndex: 2,
},
{
name: "Namespace",
hint: "UUID namespace (UUID; valid for v3 and v5)",
type: "string",
value: "1b671a64-40d5-491e-99b0-da01ff1f3341"
}
];
}
/**
@ -33,16 +60,17 @@ class GenerateUUID extends Operation {
* @returns {string}
*/
run(input, args) {
const buf = new Uint32Array(4).map(() => {
return crypto.randomBytes(4).readUInt32BE(0, true);
});
let i = 0;
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
const r = (buf[i >> 3] >> ((i % 8) * 4)) & 0xf,
v = c === "x" ? r : (r & 0x3 | 0x8);
i++;
return v.toString(16);
});
const [version, namespace] = args;
const hasDesiredVersion = typeof uuid[version] === "function";
if (!hasDesiredVersion) throw new OperationError("Invalid UUID version");
const requiresNamespace = ["v3", "v5"].includes(version);
if (!requiresNamespace) return uuid[version]();
const hasValidNamespace = typeof namespace === "string" && uuid.validate(namespace);
if (!hasValidNamespace) throw new OperationError("Invalid UUID namespace");
return uuid[version](input, namespace);
}
}

View File

@ -0,0 +1,46 @@
/**
* @author ccarpo [ccarpo@gmx.net]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import YAML from "yaml";
/**
* JSON to YAML operation
*/
class JSONtoYAML extends Operation {
/**
* JSONtoYAML constructor
*/
constructor() {
super();
this.name = "JSON to YAML";
this.module = "Default";
this.description = "Format a JSON object into YAML";
this.infoURL = "https://en.wikipedia.org/wiki/YAML";
this.inputType = "JSON";
this.outputType = "string";
this.args = [];
}
/**
* @param {JSON} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
try {
return YAML.stringify(input);
} catch (err) {
throw new OperationError("Test");
}
}
}
export default JSONtoYAML;

View File

@ -0,0 +1,57 @@
/**
* @author zhzy0077 [zhzy0077@hotmail.com]
* @copyright Crown Copyright 2023
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import jq from "jq-web";
/**
* jq operation
*/
class Jq extends Operation {
/**
* Jq constructor
*/
constructor() {
super();
this.name = "Jq";
this.module = "Jq";
this.description = "jq is a lightweight and flexible command-line JSON processor.";
this.infoURL = "https://github.com/jqlang/jq";
this.inputType = "JSON";
this.outputType = "string";
this.args = [
{
name: "Query",
type: "string",
value: ""
}
];
}
/**
* @param {JSON} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [query] = args;
let result;
try {
result = jq.json(input, query);
} catch (err) {
throw new OperationError(`Invalid jq expression: ${err.message}`);
}
return JSON.stringify(result);
}
}
export default Jq;

View File

@ -0,0 +1,65 @@
/**
* @author Jon K (jon@ajarsoftware.com)
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import jsonata from "jsonata";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Jsonata Query operation
*/
class JsonataQuery extends Operation {
/**
* JsonataQuery constructor
*/
constructor() {
super();
this.name = "Jsonata Query";
this.module = "Code";
this.description =
"Query and transform JSON data with a jsonata query.";
this.infoURL = "https://docs.jsonata.org/overview.html";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Query",
type: "text",
value: "string",
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async run(input, args) {
const [query] = args;
let result, jsonObj;
try {
jsonObj = JSON.parse(input);
} catch (err) {
throw new OperationError(`Invalid input JSON: ${err.message}`);
}
try {
const expression = jsonata(query);
result = await expression.evaluate(jsonObj);
} catch (err) {
throw new OperationError(
`Invalid Jsonata Expression: ${err.message}`
);
}
return JSON.stringify(result === undefined ? "" : result);
}
}
export default JsonataQuery;

View File

@ -0,0 +1,126 @@
/**
* @author brun0ne [brunonblok@gmail.com]
* @copyright Crown Copyright 2023
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* PHP Serialize operation
*/
class PHPSerialize extends Operation {
/**
* PHPSerialize constructor
*/
constructor() {
super();
this.name = "PHP Serialize";
this.module = "Default";
this.description = "Performs PHP serialization on JSON data.<br><br>This function does not support <code>object</code> tags.<br><br>Since PHP doesn't distinguish dicts and arrays, this operation is not always symmetric to <code>PHP Deserialize</code>.<br><br>Example:<br><code>[5,&quot;abc&quot;,true]</code><br>becomes<br><code>a:3:{i:0;i:5;i:1;s:3:&quot;abc&quot;;i:2;b:1;}<code>";
this.infoURL = "https://www.phpinternalsbook.com/php5/classes_objects/serialization.html";
this.inputType = "JSON";
this.outputType = "string";
this.args = [];
}
/**
* @param {JSON} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
/**
* Determines if a number is an integer
* @param {number} value
* @returns {boolean}
*/
function isInteger(value) {
return typeof value === "number" && parseInt(value.toString(), 10) === value;
}
/**
* Serialize basic types
* @param {string | number | boolean} content
* @returns {string}
*/
function serializeBasicTypes(content) {
const basicTypes = {
"string": "s",
"integer": "i",
"float": "d",
"boolean": "b"
};
/**
* Booleans
* cast to 0 or 1
*/
if (typeof content === "boolean") {
return `${basicTypes.boolean}:${content ? 1 : 0}`;
}
/* Numbers */
if (typeof content === "number") {
if (isInteger(content)) {
return `${basicTypes.integer}:${content.toString()}`;
} else {
return `${basicTypes.float}:${content.toString()}`;
}
}
/* Strings */
if (typeof content === "string")
return `${basicTypes.string}:${content.length}:"${content}"`;
/** This should be unreachable */
throw new OperationError(`Encountered a non-implemented type: ${typeof content}`);
}
/**
* Recursively serialize
* @param {*} object
* @returns {string}
*/
function serialize(object) {
/* Null */
if (object == null) {
return `N;`;
}
if (typeof object !== "object") {
/* Basic types */
return `${serializeBasicTypes(object)};`;
} else if (object instanceof Array) {
/* Arrays */
const serializedElements = [];
for (let i = 0; i < object.length; i++) {
serializedElements.push(`${serialize(i)}${serialize(object[i])}`);
}
return `a:${object.length}:{${serializedElements.join("")}}`;
} else if (object instanceof Object) {
/**
* Objects
* Note: the output cannot be guaranteed to be in the same order as the input
*/
const serializedElements = [];
const keys = Object.keys(object);
for (const key of keys) {
serializedElements.push(`${serialize(key)}${serialize(object[key])}`);
}
return `a:${keys.length}:{${serializedElements.join("")}}`;
}
/** This should be unreachable */
throw new OperationError(`Encountered a non-implemented type: ${typeof object}`);
}
return serialize(input);
}
}
export default PHPSerialize;

View File

@ -6,7 +6,8 @@
import r from "jsrsasign";
import { fromBase64 } from "../lib/Base64.mjs";
import { toHex } from "../lib/Hex.mjs";
import { runHash } from "../lib/Hash.mjs";
import { fromHex, toHex } from "../lib/Hex.mjs";
import { formatByteStr, formatDnObj } from "../lib/PublicKey.mjs";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
@ -81,7 +82,8 @@ class ParseX509Certificate extends Operation {
}
if (undefinedInputFormat) throw "Undefined input format";
const sn = cert.getSerialNumberHex(),
const hex = Utils.strToArrayBuffer(Utils.byteArrayToChars(fromHex(cert.hex))),
sn = cert.getSerialNumberHex(),
issuer = cert.getIssuer(),
subject = cert.getSubject(),
pk = cert.getPublicKey(),
@ -191,6 +193,10 @@ Issuer
${issuerStr}
Subject
${subjectStr}
Fingerprints
MD5: ${runHash("md5", hex)}
SHA1: ${runHash("sha1", hex)}
SHA256: ${runHash("sha256", hex)}
Public Key
${pkStr.slice(0, -1)}
Certificate Signature

View File

@ -72,7 +72,7 @@ class RailFenceCipherDecode extends Operation {
}
}
return plaintext.join("").trim();
return plaintext.join("");
}
}

View File

@ -66,7 +66,7 @@ class RailFenceCipherEncode extends Operation {
rows[rowIdx] += plaintext[pos];
}
return rows.join("").trim();
return rows.join("");
}
}

View File

@ -0,0 +1,71 @@
/**
* @author flakjacket95 [dflack95@gmail.com]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
import Operation from "../Operation.mjs";
import { SM2 } from "../lib/SM2.mjs";
/**
* SM2Decrypt operation
*/
class SM2Decrypt extends Operation {
/**
* SM2Decrypt constructor
*/
constructor() {
super();
this.name = "SM2 Decrypt";
this.module = "Crypto";
this.description = "Decrypts a message utilizing the SM2 standard";
this.infoURL = ""; // Usually a Wikipedia link. Remember to remove localisation (i.e. https://wikipedia.org/etc rather than https://en.wikipedia.org/etc)
this.inputType = "string";
this.outputType = "ArrayBuffer";
this.args = [
{
name: "Private Key",
type: "string",
value: "DEADBEEF"
},
{
"name": "Input Format",
"type": "option",
"value": ["C1C3C2", "C1C2C3"],
"defaultIndex": 0
},
{
name: "Curve",
type: "option",
"value": ["sm2p256v1"],
"defaultIndex": 0
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
run(input, args) {
const [privateKey, inputFormat, curveName] = args;
if (privateKey.length !== 64) {
throw new OperationError("Input private key must be in hex; and should be 32 bytes");
}
const sm2 = new SM2(curveName, inputFormat);
sm2.setPrivateKey(privateKey);
const result = sm2.decrypt(input);
return result;
}
}
export default SM2Decrypt;

View File

@ -0,0 +1,77 @@
/**
* @author flakjacket95 [dflack95@gmail.com]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
import Operation from "../Operation.mjs";
import { SM2 } from "../lib/SM2.mjs";
/**
* SM2 Encrypt operation
*/
class SM2Encrypt extends Operation {
/**
* SM2Encrypt constructor
*/
constructor() {
super();
this.name = "SM2 Encrypt";
this.module = "Crypto";
this.description = "Encrypts a message utilizing the SM2 standard";
this.infoURL = ""; // Usually a Wikipedia link. Remember to remove localisation (i.e. https://wikipedia.org/etc rather than https://en.wikipedia.org/etc)
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
name: "Public Key X",
type: "string",
value: "DEADBEEF"
},
{
name: "Public Key Y",
type: "string",
value: "DEADBEEF"
},
{
"name": "Output Format",
"type": "option",
"value": ["C1C3C2", "C1C2C3"],
"defaultIndex": 0
},
{
name: "Curve",
type: "option",
"value": ["sm2p256v1"],
"defaultIndex": 0
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const [publicKeyX, publicKeyY, outputFormat, curveName] = args;
this.outputFormat = outputFormat;
if (publicKeyX.length !== 64 || publicKeyY.length !== 64) {
throw new OperationError("Invalid Public Key - Ensure each component is 32 bytes in size and in hex");
}
const sm2 = new SM2(curveName, outputFormat);
sm2.setPublicKey(publicKeyX, publicKeyY);
const result = sm2.encrypt(new Uint8Array(input));
return result;
}
}
export default SM2Encrypt;

View File

@ -1,6 +1,7 @@
/**
* @author j433866 [j433866@gmail.com]
* @copyright Crown Copyright 2019
* @author 0xff1ce [github.com/0xff1ce]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
@ -22,7 +23,7 @@ class ShowOnMap extends Operation {
this.name = "Show on map";
this.module = "Hashing";
this.description = "Displays co-ordinates on a slippy map.<br><br>Co-ordinates will be converted to decimal degrees before being shown on the map.<br><br>Supported formats:<ul><li>Degrees Minutes Seconds (DMS)</li><li>Degrees Decimal Minutes (DDM)</li><li>Decimal Degrees (DD)</li><li>Geohash</li><li>Military Grid Reference System (MGRS)</li><li>Ordnance Survey National Grid (OSNG)</li><li>Universal Transverse Mercator (UTM)</li></ul><br>This operation will not work offline.";
this.infoURL = "https://foundation.wikimedia.org/wiki/Maps_Terms_of_Use";
this.infoURL = "https://osmfoundation.org/wiki/Terms_of_Use";
this.inputType = "string";
this.outputType = "string";
this.presentType = "html";
@ -85,10 +86,10 @@ class ShowOnMap extends Operation {
data = "0, 0";
}
const zoomLevel = args[0];
const tileUrl = "https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png",
tileAttribution = "<a href=\"https://wikimediafoundation.org/wiki/Maps_Terms_of_Use\">Wikimedia maps</a> | &copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors",
leafletUrl = "https://unpkg.com/leaflet@1.5.0/dist/leaflet.js",
leafletCssUrl = "https://unpkg.com/leaflet@1.5.0/dist/leaflet.css";
const tileUrl = "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
tileAttribution = "&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors",
leafletUrl = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js",
leafletCssUrl = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css";
return `<link rel="stylesheet" href="${leafletCssUrl}" crossorigin=""/>
<style>
#output-text .cm-content,

View File

@ -0,0 +1,53 @@
/**
* @author kendallgoto [k@kgo.to]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Handlebars from "handlebars";
/**
* Template operation
*/
class Template extends Operation {
/**
* Template constructor
*/
constructor() {
super();
this.name = "Template";
this.module = "Handlebars";
this.description = "Render a template with Handlebars/Mustache substituting variables using JSON input. Templates will be rendered to plain-text only, to prevent XSS.";
this.infoURL = "https://handlebarsjs.com/";
this.inputType = "JSON";
this.outputType = "string";
this.args = [
{
name: "Template definition (.handlebars)",
type: "text",
value: ""
}
];
}
/**
* @param {JSON} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [templateStr] = args;
try {
const template = Handlebars.compile(templateStr);
return template(input);
} catch (e) {
throw new OperationError(e);
}
}
}
export default Template;

View File

@ -6,6 +6,7 @@
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import {ALPHABET_OPTIONS} from "../lib/Base32.mjs";
/**
* To Base32 operation
@ -27,8 +28,8 @@ class ToBase32 extends Operation {
this.args = [
{
name: "Alphabet",
type: "binaryString",
value: "A-Z2-7="
type: "editableOption",
value: ALPHABET_OPTIONS
}
];
}
@ -83,3 +84,4 @@ class ToBase32 extends Operation {
}
export default ToBase32;

View File

@ -33,7 +33,7 @@ class ToBase85 extends Operation {
value: ALPHABET_OPTIONS
},
{
name: "Include delimeter",
name: "Include delimiter",
type: "boolean",
value: false
}

View File

@ -0,0 +1,92 @@
/**
* @author Medjedtxm
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import { encode } from "../lib/Bech32.mjs";
import { fromHex } from "../lib/Hex.mjs";
/**
* To Bech32 operation
*/
class ToBech32 extends Operation {
/**
* ToBech32 constructor
*/
constructor() {
super();
this.name = "To Bech32";
this.module = "Default";
this.description = "Bech32 is an encoding scheme primarily used for Bitcoin SegWit addresses (BIP-0173). It uses a 32-character alphabet that excludes easily confused characters (1, b, i, o) and includes a checksum for error detection.<br><br>Bech32m (BIP-0350) is an updated version that fixes a weakness in the original Bech32 checksum and is used for Bitcoin Taproot addresses.<br><br>The Human-Readable Part (HRP) identifies the network or purpose (e.g., 'bc' for Bitcoin mainnet, 'tb' for testnet, 'age' for AGE encryption keys).<br><br>Maximum output length is 90 characters as per specification.";
this.infoURL = "https://wikipedia.org/wiki/Bech32";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
"name": "Human-Readable Part (HRP)",
"type": "string",
"value": "bc"
},
{
"name": "Encoding",
"type": "option",
"value": ["Bech32", "Bech32m"]
},
{
"name": "Input Format",
"type": "option",
"value": ["Raw bytes", "Hex"]
},
{
"name": "Mode",
"type": "option",
"value": ["Generic", "Bitcoin SegWit"]
},
{
"name": "Witness Version",
"type": "number",
"value": 0,
"hint": "SegWit witness version (0-16). Only used in Bitcoin SegWit mode."
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const hrp = args[0];
const encoding = args[1];
const inputFormat = args[2];
const mode = args[3];
const witnessVersion = args[4];
let inputArray;
if (inputFormat === "Hex") {
// Convert hex string to bytes
const hexStr = new TextDecoder().decode(new Uint8Array(input)).replace(/\s/g, "");
inputArray = fromHex(hexStr);
} else {
inputArray = new Uint8Array(input);
}
if (mode === "Bitcoin SegWit") {
// Prepend witness version to the input data
const withVersion = new Uint8Array(inputArray.length + 1);
withVersion[0] = witnessVersion;
withVersion.set(inputArray, 1);
return encode(hrp, withVersion, encoding, true);
}
return encode(hrp, inputArray, encoding, false);
}
}
export default ToBech32;

View File

@ -45,11 +45,12 @@ class ToDecimal extends Operation {
* @returns {string}
*/
run(input, args) {
input = new Uint8Array(input);
const delim = Utils.charRep(args[0]),
signed = args[1];
if (signed) {
input = input.map(v => v > 0x7F ? v - 0xFF - 1 : v);
input = new Int8Array(input);
} else {
input = new Uint8Array(input);
}
return input.join(delim);
}

View File

@ -23,7 +23,13 @@ class URLDecode extends Operation {
this.infoURL = "https://wikipedia.org/wiki/Percent-encoding";
this.inputType = "string";
this.outputType = "string";
this.args = [];
this.args = [
{
"name": "Treat \"+\" as space",
"type": "boolean",
"value": true
},
];
this.checks = [
{
pattern: ".*(?:%[\\da-f]{2}.*){4}",
@ -39,7 +45,8 @@ class URLDecode extends Operation {
* @returns {string}
*/
run(input, args) {
const data = input.replace(/\+/g, "%20");
const plusIsSpace = args[0];
const data = plusIsSpace ? input.replace(/\+/g, "%20") : input;
try {
return decodeURIComponent(data);
} catch (err) {

View File

@ -24,7 +24,7 @@ class VarIntDecode extends Operation {
this.description = "Decodes a VarInt encoded integer. VarInt is an efficient way of encoding variable length integers and is commonly used with Protobuf.";
this.infoURL = "https://developers.google.com/protocol-buffers/docs/encoding#varints";
this.inputType = "byteArray";
this.outputType = "number";
this.outputType = "string";
this.args = [];
}
@ -35,7 +35,18 @@ class VarIntDecode extends Operation {
*/
run(input, args) {
try {
return Protobuf.varIntDecode(input);
if (typeof BigInt === "function") {
let result = BigInt(0);
let offset = BigInt(0);
for (let i = 0; i < input.length; i++) {
result |= BigInt(input[i] & 0x7f) << offset;
if (!(input[i] & 0x80)) break;
offset += BigInt(7);
}
return result.toString();
} else {
return Protobuf.varIntDecode(input).toString();
}
} catch (err) {
throw new OperationError(err);
}

View File

@ -23,19 +23,31 @@ class VarIntEncode extends Operation {
this.module = "Default";
this.description = "Encodes a Vn integer as a VarInt. VarInt is an efficient way of encoding variable length integers and is commonly used with Protobuf.";
this.infoURL = "https://developers.google.com/protocol-buffers/docs/encoding#varints";
this.inputType = "number";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [];
}
/**
* @param {number} input
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
try {
return Protobuf.varIntEncode(input);
if (typeof BigInt === "function") {
let value = BigInt(input);
if (value < 0) throw new OperationError("Negative values cannot be represented as VarInt");
const result = [];
while (value >= 0x80) {
result.push(Number(value & BigInt(0x7f)) | 0x80);
value >>= BigInt(7);
}
result.push(Number(value));
return result;
} else {
return Protobuf.varIntEncode(Number(input));
}
} catch (err) {
throw new OperationError(err);
}

View File

@ -0,0 +1,59 @@
/**
* @author Thomas Weißschuh [thomas@t-8ch.de]
* @copyright Crown Copyright 2023
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import { toHex } from "../lib/Hex.mjs";
/**
* XOR Checksum operation
*/
class XORChecksum extends Operation {
/**
* XORChecksum constructor
*/
constructor() {
super();
this.name = "XOR Checksum";
this.module = "Crypto";
this.description = "XOR Checksum splits the input into blocks of a configurable size and performs the XOR operation on these blocks.";
this.infoURL = "https://wikipedia.org/wiki/XOR";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
name: "Blocksize",
type: "number",
value: 4
},
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const blocksize = args[0];
input = new Uint8Array(input);
const res = Array(blocksize);
res.fill(0);
for (const chunk of Utils.chunked(input, blocksize)) {
for (let i = 0; i < blocksize; i++) {
res[i] ^= chunk[i];
}
}
return toHex(res, "");
}
}
export default XORChecksum;

View File

@ -0,0 +1,45 @@
/**
* @author ccarpo [ccarpo@gmx.net]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import jsYaml from "js-yaml";
/**
* YAML to JSON operation
*/
class YAMLToJSON extends Operation {
/**
* YAMLToJSON constructor
*/
constructor() {
super();
this.name = "YAML to JSON";
this.module = "Default";
this.description = "Convert YAML to JSON";
this.infoURL = "https://en.wikipedia.org/wiki/YAML";
this.inputType = "string";
this.outputType = "JSON";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {JSON}
*/
run(input, args) {
try {
return jsYaml.load(input);
} catch (err) {
throw new OperationError("Unable to parse YAML: " + err);
}
}
}
export default YAMLToJSON;

View File

@ -74,11 +74,11 @@ function transformArgs(opArgsList, newArgs) {
return opArgs.map((arg) => {
if (arg.type === "option") {
// pick default option if not already chosen
return typeof arg.value === "string" ? arg.value : arg.value[0];
return typeof arg.value === "string" ? arg.value : arg.value[arg.defaultIndex ?? 0];
}
if (arg.type === "editableOption") {
return typeof arg.value === "string" ? arg.value : arg.value[0].value;
return typeof arg.value === "string" ? arg.value : arg.value[arg.defaultIndex ?? 0].value;
}
if (arg.type === "toggleString") {

View File

@ -66,7 +66,7 @@ export function removeSubheadingsFromArray(array) {
* @param str
*/
export function sanitise(str) {
return str.replace(/ /g, "").toLowerCase();
return str.replace(/[/\s.-]/g, "").toLowerCase();
}

View File

@ -650,7 +650,7 @@ class App {
// const compareURL = `https://github.com/gchq/CyberChef/compare/v${prev.join(".")}...v${PKG_VERSION}`;
let compileInfo = `<a href='https://github.com/gchq/CyberChef/blob/master/CHANGELOG.md'>Last build: ${timeSinceCompile.substr(0, 1).toUpperCase() + timeSinceCompile.substr(1)} ago</a>`;
let compileInfo = `<a href='https://github.com/gchq/CyberChef/blob/master/CHANGELOG.md'>Last build: ${timeSinceCompile.substring(0, 1).toUpperCase() + timeSinceCompile.substring(1)} ago</a>`;
if (window.compileMessage !== "") {
compileInfo += " - " + window.compileMessage;

View File

@ -125,6 +125,7 @@ class Manager {
window.addEventListener("focus", this.window.windowFocus.bind(this.window));
window.addEventListener("statechange", this.app.stateChange.bind(this.app));
window.addEventListener("popstate", this.app.popState.bind(this.app));
window.addEventListener("message", this.input.handlePostMessage.bind(this.input));
// Controls
document.getElementById("bake").addEventListener("click", this.controls.bakeClick.bind(this.controls));

View File

@ -1,6 +1,5 @@
import sm from "sitemap";
import OperationConfig from "../../core/config/OperationConfig.json" assert {type: "json"};
import OperationConfig from "../../core/config/OperationConfig.json" assert { type: "json" };
/**
* Generates an XML sitemap for all CyberChef operations and a number of recipes.
@ -10,25 +9,25 @@ import OperationConfig from "../../core/config/OperationConfig.json" assert {typ
* @license Apache-2.0
*/
const smStream = new sm.SitemapStream({
hostname: "https://gchq.github.io/CyberChef",
});
const baseUrl = "https://gchq.github.io/CyberChef/";
const smStream = new sm.SitemapStream({});
smStream.write({
url: "/",
url: baseUrl,
changefreq: "weekly",
priority: 1.0
priority: 1.0,
});
for (const op in OperationConfig) {
smStream.write({
url: `/?op=${encodeURIComponent(op)}`,
url: `${baseUrl}?op=${encodeURIComponent(op)}`,
changeFreq: "yearly",
priority: 0.5
priority: 0.5,
});
}
smStream.end();
sm.streamToPromise(smStream).then(
buffer => console.log(buffer.toString()) // eslint-disable-line no-console
(buffer) => console.log(buffer.toString()), // eslint-disable-line no-console
);

View File

@ -1,23 +1,26 @@
[
{
"@context": "http://schema.org",
"@type": "Organization",
"url": "https://gchq.github.io/CyberChef/",
"logo": "https://gchq.github.io/CyberChef/images/cyberchef-128x128.png",
"sameAs": [
"https://github.com/gchq/CyberChef",
"https://www.npmjs.com/package/cyberchef"
"@graph": [
{
"@type": "Organization",
"url": "https://gchq.github.io/CyberChef/",
"logo": "https://gchq.github.io/CyberChef/images/cyberchef-128x128.png",
"sameAs": [
"https://github.com/gchq/CyberChef",
"https://www.npmjs.com/package/cyberchef"
]
},
{
"@type": "WebSite",
"url": "https://gchq.github.io/CyberChef/",
"name": "CyberChef",
"potentialAction": {
"@type": "SearchAction",
"target": "https://gchq.github.io/CyberChef/?op={operation_search_term}",
"query-input": "required name=operation_search_term"
}
}
]
},
{
"@context": "http://schema.org",
"@type": "WebSite",
"url": "https://gchq.github.io/CyberChef/",
"name": "CyberChef",
"potentialAction": {
"@type": "SearchAction",
"target": "https://gchq.github.io/CyberChef/?op={operation_search_term}",
"query-input": "required name=operation_search_term"
}
}
]
]

View File

@ -1654,6 +1654,23 @@ class InputWaiter {
this.changeTab(inputNum, this.app.options.syncTabs);
}
/**
* Handler for incoming postMessages
* If the events data has a `type` property set to `dataSubmit`
* the value property is set to the current input
* @param {event} e
* @param {object} e.data
* @param {string} e.data.type - the type of request, currently the only value is "dataSubmit"
* @param {string} e.data.value - the value of the message
*/
handlePostMessage(e) {
log.debug(e);
if ("data" in e && "id" in e.data && "value" in e.data) {
if (e.data.id === "setInput") {
this.setInput(e.data.value);
}
}
}
}
export default InputWaiter;

View File

@ -160,18 +160,37 @@ class OptionsWaiter {
// Update theme selection
const themeSelect = document.getElementById("theme");
themeSelect.selectedIndex = themeSelect.querySelector(`option[value="${theme}"`).index;
let themeOption = themeSelect.querySelector(`option[value="${theme}"]`);
if (!themeOption) {
const preferredColorScheme = this.getPreferredColorScheme();
document.querySelector(":root").className = preferredColorScheme;
themeOption = themeSelect.querySelector(`option[value="${preferredColorScheme}"]`);
}
themeSelect.selectedIndex = themeOption.index;
}
/**
* Applies the user's preferred color scheme using the `prefers-color-scheme` media query.
*/
applyPreferredColorScheme() {
const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)").matches;
const theme = prefersDarkScheme ? "dark" : "classic";
const themeFromStorage = this.app?.options?.theme;
let theme = themeFromStorage;
if (!theme) {
theme = this.getPreferredColorScheme();
}
this.changeTheme(theme);
}
/**
* Get the user's preferred color scheme using the `prefers-color-scheme` media query.
*/
getPreferredColorScheme() {
const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)").matches;
return prefersDarkScheme ? "dark" : "classic";
}
/**
* Changes the console logging level.
*

View File

@ -8,6 +8,7 @@ import HTMLOperation from "../HTMLOperation.mjs";
import Sortable from "sortablejs";
import Utils from "../../core/Utils.mjs";
import {escapeControlChars} from "../utils/editorUtils.mjs";
import DOMPurify from "dompurify";
/**
@ -435,7 +436,9 @@ class RecipeWaiter {
const item = document.createElement("li");
item.classList.add("operation");
item.innerHTML = name;
const clean = DOMPurify.sanitize(name);
item.innerHTML = clean;
this.buildRecipeOperation(item);
document.getElementById("rec-list").appendChild(item);

View File

@ -64,9 +64,9 @@ module.exports = {
testOp(browser, ["From Hex", "Bzip2 Decompress"], "425a68393141592653597b0884b7000003038000008200ce00200021a647a4218013709517c5dc914e14241ec2212dc0", "test_output", [[], [true]]);
// testOp(browser, "CBOR Decode", "test input", "test output");
// testOp(browser, "CBOR Encode", "test input", "test output");
testOp(browser, "CRC-16 Checksum", "test input", "77c7");
testOp(browser, "CRC-32 Checksum", "test input", "29822bc8");
testOp(browser, "CRC-8 Checksum", "test input", "9d");
testOp(browser, "CRC Checksum", "test input", "77c7", ["CRC-16"]);
testOp(browser, "CRC Checksum", "test input", "29822bc8", ["CRC-32"]);
testOp(browser, "CRC Checksum", "test input", "9d", ["CRC-8"]);
// testOp(browser, "CSS Beautify", "test input", "test_output");
// testOp(browser, "CSS Minify", "test input", "test_output");
// testOp(browser, "CSS selector", "test input", "test_output");

View File

@ -20,4 +20,10 @@ TestRegister.addApiTests([
assert.equal(Utils.parseEscapedChars("\\\\\\'"), "\\'");
}),
it("Utils: should replace delete character", () => {
assert.equal(
Utils.printable("\x7e\x7f\x80\xa7", false, true),
"\x7e...",
);
}),
]);

View File

@ -345,6 +345,42 @@ TestRegister.addApiTests([
assert.strictEqual(result.toString(), "begin_something_aaaaaaaaaaaaaa_end_something");
}),
it("chef.bake: should accept operation names from Chef Website which contain forward slash", () => {
const result = chef.bake("I'll have the test salmon", [
{ "op": "Find / Replace",
"args": [{ "option": "Regex", "string": "test" }, "good", true, false, true, false]}
]);
assert.strictEqual(result.toString(), "I'll have the good salmon");
}),
it("chef.bake: should accept operation names from Chef Website which contain a hyphen", () => {
const result = chef.bake("I'll have the test salmon", [
{ "op": "Adler-32 Checksum",
"args": [] }
]);
assert.strictEqual(result.toString(), "6e4208f8");
}),
it("chef.bake: should accept operation names from Chef Website which contain a period", () => {
const result = chef.bake("30 13 02 01 05 16 0e 41 6e 79 62 6f 64 79 20 74 68 65 72 65 3f", [
{ "op": "Parse ASN.1 hex string",
"args": [0, 32] }
]);
assert.strictEqual(result.toString(), `SEQUENCE
INTEGER 05
IA5String 'Anybody there?'
`);
}),
it("Excluded operations: throw a sensible error when you try and call one", () => {
try {
chef.fork();
} catch (e) {
assert.strictEqual(e.type, "ExcludedOperationError");
assert.strictEqual(e.message, "Sorry, the Fork operation is not available in the Node.js version of CyberChef.");
}
}),
it("chef.bake: cannot accept flowControl operations in recipe", () => {
assert.throws(() => chef.bake("some input", "magic"), {
name: "TypeError",

View File

@ -305,16 +305,6 @@ Full hash: $2a$10$ODeP1.6fMsb.ENk2ngPUCO7qTGVPyHA9TqDVcyupyed8FjsiF65L6`;
assert.strictEqual(result.toString(), "2");
}),
it("CRC16 Checksum", () => {
const result = chef.CRC16Checksum("Rain on Your Parade");
assert.strictEqual(result.toString(), "db1c");
}),
it("CRC32 Checksum", () => {
const result = chef.CRC32Checksum("Rain on Your Parade");
assert.strictEqual(result.toString(), "e902f76c");
}),
it("CSS Beautify", () => {
const result = chef.CSSBeautify("header {color:black;padding:3rem;}");
const expected = `header {
@ -590,10 +580,25 @@ Password: 282760`;
assert.strictEqual(result.toString().substr(0, 37), "-----BEGIN PGP PRIVATE KEY BLOCK-----");
}),
it("Generate UUID", () => {
const result = chef.generateUUID();
assert.ok(result.toString());
assert.strictEqual(result.toString().length, 36);
...[1, 3, 4, 5, 6, 7].map(version => it(`Generate UUID v${version}`, () => {
const result = chef.generateUUID("", { "version": `v${version}` }).toString();
assert.ok(result);
assert.strictEqual(result.length, 36);
})),
...[1, 3, 4, 5, 6, 7].map(version => it(`Analyze UUID v${version}`, () => {
const uuid = chef.generateUUID("", { "version": `v${version}` }).toString();
const result = chef.analyseUUID(uuid).toString();
const expected = `UUID version: ${version}`;
assert.strictEqual(result, expected);
})),
it("Generate UUID using defaults", () => {
const uuid = chef.generateUUID();
assert.ok(uuid);
const analysis = chef.analyseUUID(uuid).toString();
assert.strictEqual(analysis, "UUID version: 4");
}),
it("Gzip, Gunzip", () => {

View File

@ -11,15 +11,14 @@
* @license Apache-2.0
*/
import {
setLongTestFailure,
logTestReport,
} from "../lib/utils.mjs";
import { setLongTestFailure, logTestReport } from "../lib/utils.mjs";
import TestRegister from "../lib/TestRegister.mjs";
import "./tests/AESKeyWrap.mjs";
import "./tests/AlternatingCaps.mjs";
import "./tests/AvroToJSON.mjs";
import "./tests/BaconCipher.mjs";
import "./tests/Base32.mjs";
import "./tests/Base45.mjs";
import "./tests/Base58.mjs";
import "./tests/Base62.mjs";
@ -27,9 +26,11 @@ import "./tests/Base64.mjs";
import "./tests/Base85.mjs";
import "./tests/Base92.mjs";
import "./tests/BCD.mjs";
import "./tests/Bech32.mjs";
import "./tests/BitwiseOp.mjs";
import "./tests/BLAKE2b.mjs";
import "./tests/BLAKE2s.mjs";
import "./tests/BLAKE3.mjs";
import "./tests/Bombe.mjs";
import "./tests/BSON.mjs";
import "./tests/ByteRepr.mjs";
@ -44,7 +45,6 @@ import "./tests/ChaCha.mjs";
import "./tests/ChangeIPFormat.mjs";
import "./tests/CharEnc.mjs";
import "./tests/Charts.mjs";
import "./tests/Checksum.mjs";
import "./tests/Ciphers.mjs";
import "./tests/CipherSaber2.mjs";
import "./tests/CMAC.mjs";
@ -56,6 +56,7 @@ import "./tests/ConditionalJump.mjs";
import "./tests/ConvertCoordinateFormat.mjs";
import "./tests/ConvertLeetSpeak.mjs";
import "./tests/ConvertToNATOAlphabet.mjs";
import "./tests/CRCChecksum.mjs";
import "./tests/Crypt.mjs";
import "./tests/CSV.mjs";
import "./tests/DateTime.mjs";
@ -66,11 +67,13 @@ import "./tests/ELFInfo.mjs";
import "./tests/Enigma.mjs";
import "./tests/ExtractEmailAddresses.mjs";
import "./tests/ExtractHashes.mjs";
import "./tests/ExtractIPAddresses.mjs";
import "./tests/Float.mjs";
import "./tests/FileTree.mjs";
import "./tests/FletcherChecksum.mjs";
import "./tests/Fork.mjs";
import "./tests/FromDecimal.mjs";
import "./tests/GenerateAllChecksums.mjs";
import "./tests/GenerateAllHashes.mjs";
import "./tests/GenerateDeBruijnSequence.mjs";
import "./tests/GetAllCasings.mjs";
@ -88,6 +91,7 @@ import "./tests/IndexOfCoincidence.mjs";
import "./tests/JA3Fingerprint.mjs";
import "./tests/JA4.mjs";
import "./tests/JA3SFingerprint.mjs";
import "./tests/Jsonata.mjs";
import "./tests/JSONBeautify.mjs";
import "./tests/JSONMinify.mjs";
import "./tests/JSONtoCSV.mjs";
@ -125,6 +129,7 @@ import "./tests/ParseUDP.mjs";
import "./tests/PEMtoHex.mjs";
import "./tests/PGP.mjs";
import "./tests/PHP.mjs";
import "./tests/PHPSerialize.mjs";
import "./tests/PowerSet.mjs";
import "./tests/Protobuf.mjs";
import "./tests/PubKeyFromCert.mjs";
@ -144,6 +149,7 @@ import "./tests/SetIntersection.mjs";
import "./tests/SetUnion.mjs";
import "./tests/Shuffle.mjs";
import "./tests/SIGABA.mjs";
import "./tests/SM2.mjs";
import "./tests/SM4.mjs";
// import "./tests/SplitColourChannels.mjs"; // Cannot test operations that use the File type yet
import "./tests/StrUtils.mjs";
@ -154,12 +160,24 @@ import "./tests/Subsection.mjs";
import "./tests/SwapCase.mjs";
import "./tests/SymmetricDifference.mjs";
import "./tests/TakeNthBytes.mjs";
import "./tests/Template.mjs";
import "./tests/TextEncodingBruteForce.mjs";
import "./tests/ToFromInsensitiveRegex.mjs";
import "./tests/TranslateDateTimeFormat.mjs";
import "./tests/Typex.mjs";
import "./tests/UnescapeString.mjs";
import "./tests/Unicode.mjs";
import "./tests/URLEncodeDecode.mjs";
import "./tests/RSA.mjs";
import "./tests/CBOREncode.mjs";
import "./tests/CBORDecode.mjs";
import "./tests/JA3Fingerprint.mjs";
import "./tests/JA3SFingerprint.mjs";
import "./tests/HASSH.mjs";
import "./tests/JSONtoYAML.mjs";
// Cannot test operations that use the File type yet
// import "./tests/SplitColourChannels.mjs";
import "./tests/YARA.mjs";
import "./tests/ParseCSR.mjs";
import "./tests/XXTEA.mjs";
@ -168,14 +186,14 @@ const testStatus = {
allTestsPassing: true,
counts: {
total: 0,
}
},
};
setLongTestFailure();
const logOpsTestReport = logTestReport.bind(null, testStatus);
(async function() {
(async function () {
const results = await TestRegister.runTests();
logOpsTestReport(results);
})();

View File

@ -0,0 +1,19 @@
/* @author sw5678
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
"name": "AlternatingCaps: Basic Example",
"input": "Hello, world!",
"expectedOutput": "hElLo, WoRlD!",
"recipeConfig": [
{
"op": "Alternating Caps",
"args": []
},
],
}
]);

View File

@ -0,0 +1,55 @@
/**
* BLAKE3 tests.
* @author xumptex [xumptex@outlook.fr]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "BLAKE3: 8 - Hello world",
input: "Hello world",
expectedOutput: "e7e6fb7d2869d109",
recipeConfig: [
{ "op": "BLAKE3",
"args": [8, ""] }
]
},
{
name: "BLAKE3: 16 - Hello world 2",
input: "Hello world 2",
expectedOutput: "2a3df5fe5f0d3fcdd995fc203c7f7c52",
recipeConfig: [
{ "op": "BLAKE3",
"args": [16, ""] }
]
},
{
name: "BLAKE3: 32 - Hello world",
input: "Hello world",
expectedOutput: "e7e6fb7d2869d109b62cdb1227208d4016cdaa0af6603d95223c6a698137d945",
recipeConfig: [
{ "op": "BLAKE3",
"args": [32, ""] }
]
},
{
name: "BLAKE3: Key Test",
input: "Hello world",
expectedOutput: "59dd23ac9d025690",
recipeConfig: [
{ "op": "BLAKE3",
"args": [8, "ThiskeyisexactlythirtytwoBytesLo"] }
]
},
{
name: "BLAKE3: Key Test 2",
input: "Hello world",
expectedOutput: "c8302c9634c1da42",
recipeConfig: [
{ "op": "BLAKE3",
"args": [8, "ThiskeyisexactlythirtytwoByteslo"] }
]
}
]);

View File

@ -0,0 +1,176 @@
/**
* Base32 Tests
*
* @author Peter C-S [petercs@purelymail.com]
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
import {ALPHABET_OPTIONS} from "../../../src/core/lib/Base32.mjs";
// Example Standard Base32 Tests
const STANDARD_INP = "HELLO BASE32";
const STANDARD_OUT = "JBCUYTCPEBBECU2FGMZA====";
// Example Hex Extended Base32 Tests
const EXTENDED_INP = "HELLO BASE32 EXTENDED";
const EXTENDED_OUT = "912KOJ2F41142KQ56CP20HAOAH2KSH258G======";
// All Bytes
const ALL_BYTES = [
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
].join("");
const ALL_BYTES_EXTENDED_OUT = "000G40O40K30E209185GO38E1S8124GJ2GAHC5OO34D1M70T3OFI08924CI2A9H750KIKAPC5KN2UC1H68PJ8D9M6SS3IEHR7GUJSFQ085146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB9DLONAUBTG62OJ3CHIMCPR8D5L6MR3DDPNN0SBIEDQ7ATJNF1SNKURSFLV7V041GA1O91C6GU48J2KBHI6OT3SGI699754LIQBPH6CQJEE9R7KVK2GQ58T4KMJAFA59LALQPBDELUOB3CLJMIQRDDTON6TBNF5TNQVS1GE2OF2CBHM7P34SLIUCPN7CVK6HQB9T9LEMQVCDJMMRRJETTNV0S7HE7P75SRJUHQFATFMERRNFU3OV5SVKUNRFFU7PVBTVPVFUVS======";
const ALL_BYTES_STANDARD_OUT = "AAAQEAYEAUDAOCAJBIFQYDIOB4IBCEQTCQKRMFYYDENBWHA5DYPSAIJCEMSCKJRHFAUSUKZMFUXC6MBRGIZTINJWG44DSOR3HQ6T4P2AIFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLJNVYXK6L5QGCYTDMRSWMZ3INFVGW3DNNZXXA4LSON2HK5TXPB4XU634PV7H7AEBQKBYJBMGQ6EITCULRSGY5D4QSGJJHFEVS2LZRGM2TOOJ3HU7UCQ2FI5EUWTKPKFJVKV2ZLNOV6YLDMVTWS23NN5YXG5LXPF5X274BQOCYPCMLRWHZDE4VS6MZXHM7UGR2LJ5JVOW27MNTWW33TO55X7A4HROHZHF43T6R2PK5PWO33XP6DY7F47U6X3PP6HZ7L57Z7P674======";
TestRegister.addTests([
{
name: "To Base32 Standard: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "To Base32",
args: [ALPHABET_OPTIONS[0].value],
},
],
},
{
name: "To Base32 Hex Extended: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "To Base32",
args: [ALPHABET_OPTIONS[1].value],
},
],
},
{
name: "From Base32 Standard: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[0].value, false],
},
],
},
{
name: "From Base32 Hex Extended: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[1].value, false],
},
],
},
{
name: "To Base32 Standard: " + STANDARD_INP,
input: STANDARD_INP,
expectedOutput: STANDARD_OUT,
recipeConfig: [
{
op: "To Base32",
args: [ALPHABET_OPTIONS[0].value],
},
],
},
{
name: "To Base32 Hex Extended: " + EXTENDED_INP,
input: EXTENDED_INP,
expectedOutput: EXTENDED_OUT,
recipeConfig: [
{
op: "To Base32",
args: [ALPHABET_OPTIONS[1].value],
},
],
},
{
name: "From Base32 Standard: " + STANDARD_OUT,
input: STANDARD_OUT,
expectedOutput: STANDARD_INP,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[0].value, false],
},
],
},
{
name: "From Base32 Hex Extended: " + EXTENDED_OUT,
input: EXTENDED_OUT,
expectedOutput: EXTENDED_INP,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[1].value, false],
},
],
},
{
name: "To Base32 Hex Standard: All Bytes",
input: ALL_BYTES,
expectedOutput: ALL_BYTES_STANDARD_OUT,
recipeConfig: [
{
op: "To Base32",
args: [ALPHABET_OPTIONS[0].value],
},
],
},
{
name: "To Base32 Hex Extended: All Bytes",
input: ALL_BYTES,
expectedOutput: ALL_BYTES_EXTENDED_OUT,
recipeConfig: [
{
op: "To Base32",
args: [ALPHABET_OPTIONS[1].value],
},
],
},
{
name: "From Base32 Hex Standard: All Bytes",
input: ALL_BYTES_STANDARD_OUT,
expectedOutput: ALL_BYTES,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[0].value, false],
},
],
},
{
name: "From Base32 Hex Extended: All Bytes",
input: ALL_BYTES_EXTENDED_OUT,
expectedOutput: ALL_BYTES,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[1].value, false],
},
],
},
]);

View File

@ -0,0 +1,702 @@
/**
* Bech32 tests.
*
* Test vectors from official BIP specifications:
* BIP-0173: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
* BIP-0350: https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki
*
* AGE key test vectors from:
* https://asecuritysite.com/age/go_age5
*
* @author Medjedtxm
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
// ============= To Bech32 Tests =============
{
name: "To Bech32: empty input",
input: "",
expectedOutput: "bc1gmk9yu",
recipeConfig: [
{
"op": "To Bech32",
"args": ["bc", "Bech32", "Raw bytes", "Generic", 0]
}
],
},
{
name: "To Bech32: single byte",
input: "A",
expectedOutput: "bc1gyufle22",
recipeConfig: [
{
"op": "To Bech32",
"args": ["bc", "Bech32", "Raw bytes", "Generic", 0]
}
],
},
{
name: "To Bech32: Hello",
input: "Hello",
expectedOutput: "bc1fpjkcmr0gzsgcg",
recipeConfig: [
{
"op": "To Bech32",
"args": ["bc", "Bech32", "Raw bytes", "Generic", 0]
}
],
},
{
name: "To Bech32: custom HRP",
input: "test",
expectedOutput: "custom1w3jhxaq593qur",
recipeConfig: [
{
"op": "To Bech32",
"args": ["custom", "Bech32", "Raw bytes", "Generic", 0]
}
],
},
{
name: "To Bech32: testnet HRP",
input: "data",
expectedOutput: "tb1v3shgcg3x07jr",
recipeConfig: [
{
"op": "To Bech32",
"args": ["tb", "Bech32", "Raw bytes", "Generic", 0]
}
],
},
{
name: "To Bech32m: empty input",
input: "",
expectedOutput: "bc1a8xfp7",
recipeConfig: [
{
"op": "To Bech32",
"args": ["bc", "Bech32m", "Raw bytes", "Generic", 0]
}
],
},
{
name: "To Bech32m: single byte",
input: "A",
expectedOutput: "bc1gyf4040g",
recipeConfig: [
{
"op": "To Bech32",
"args": ["bc", "Bech32m", "Raw bytes", "Generic", 0]
}
],
},
{
name: "To Bech32m: Hello",
input: "Hello",
expectedOutput: "bc1fpjkcmr0a7qya2",
recipeConfig: [
{
"op": "To Bech32",
"args": ["bc", "Bech32m", "Raw bytes", "Generic", 0]
}
],
},
{
name: "To Bech32: empty HRP error",
input: "test",
expectedOutput: "Human-Readable Part (HRP) cannot be empty.",
recipeConfig: [
{
"op": "To Bech32",
"args": ["", "Bech32", "Raw bytes", "Generic", 0]
}
],
},
// ============= From Bech32 Tests (Raw output) =============
{
name: "From Bech32: decode single byte (Raw)",
input: "bc1gyufle22",
expectedOutput: "A",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "Raw"]
}
],
},
{
name: "From Bech32: decode Hello (Raw)",
input: "bc1fpjkcmr0gzsgcg",
expectedOutput: "Hello",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "Raw"]
}
],
},
{
name: "From Bech32: auto-detect Bech32 (Raw)",
input: "bc1fpjkcmr0gzsgcg",
expectedOutput: "Hello",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Raw"]
}
],
},
{
name: "From Bech32m: decode Hello (Raw)",
input: "bc1fpjkcmr0a7qya2",
expectedOutput: "Hello",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32m", "Raw"]
}
],
},
{
name: "From Bech32: auto-detect Bech32m (Raw)",
input: "bc1fpjkcmr0a7qya2",
expectedOutput: "Hello",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Raw"]
}
],
},
{
name: "From Bech32: uppercase input (Raw)",
input: "BC1FPJKCMR0GZSGCG",
expectedOutput: "Hello",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Raw"]
}
],
},
{
name: "From Bech32: custom HRP (Raw)",
input: "custom1w3jhxaq593qur",
expectedOutput: "test",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "Raw"]
}
],
},
{
name: "From Bech32: empty input",
input: "",
expectedOutput: "",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Hex"]
}
],
},
{
name: "From Bech32: empty data part (Hex)",
input: "bc1gmk9yu",
expectedOutput: "",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "Hex"]
}
],
},
// ============= From Bech32 HRP Output Tests =============
{
name: "From Bech32: HRP: Hex output format",
input: "bc1fpjkcmr0gzsgcg",
expectedOutput: "bc: 48656c6c6f",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "HRP: Hex"]
}
],
},
{
name: "From Bech32: JSON output format",
input: "bc1fpjkcmr0gzsgcg",
expectedOutput: "{\n \"hrp\": \"bc\",\n \"encoding\": \"Bech32\",\n \"data\": \"48656c6c6f\"\n}",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "JSON"]
}
],
},
{
name: "From Bech32: Hex output format",
input: "bc1fpjkcmr0gzsgcg",
expectedOutput: "48656c6c6f",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "Hex"]
}
],
},
// ============= AGE Key Test Vectors =============
// From: https://asecuritysite.com/age/go_age5
{
name: "From Bech32: AGE public key 1 (HRP: Hex)",
input: "age1kk86t4lr4s9uwvnqjzp2e35rflvcpnjt33q99547ct23xzk0ssss3ma49j",
expectedOutput: "age: b58fa5d7e3ac0bc732609082acc6834fd980ce4b8c4052d2bec2d5130acf8421",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "HRP: Hex"]
}
],
},
{
name: "From Bech32: AGE private key 1 (HRP: Hex)",
input: "AGE-SECRET-KEY-1Z5N23X54Y4E9NLMPNH6EZDQQX9V883TMKJ3ZJF5QXXMKNZ2RPFXQUQF74G",
expectedOutput: "age-secret-key-: 1526a89a95257259ff619df5913400315873c57bb4a229268031b76989430a4c",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "HRP: Hex"]
}
],
},
{
name: "From Bech32: AGE public key 2 (HRP: Hex)",
input: "age1nwt7gkq7udvalagqn7l8a4jgju7wtenkg925pvuqvn7cfcry6u2qkae4ad",
expectedOutput: "age: 9b97e4581ee359dff5009fbe7ed648973ce5e676415540b38064fd84e064d714",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "HRP: Hex"]
}
],
},
{
name: "From Bech32: AGE private key 2 (HRP: Hex)",
input: "AGE-SECRET-KEY-137M0YVE3CL6M8C4ET9L2KU67FPQHJZTW547QD5CK0R5A5T09ZGJSQGR9LX",
expectedOutput: "age-secret-key-: 8fb6f23331c7f5b3e2b9597eab735e484179096ea57c06d31678e9da2de51225",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "HRP: Hex"]
}
],
},
{
name: "From Bech32: AGE public key 1 (JSON)",
input: "age1kk86t4lr4s9uwvnqjzp2e35rflvcpnjt33q99547ct23xzk0ssss3ma49j",
expectedOutput: "{\n \"hrp\": \"age\",\n \"encoding\": \"Bech32\",\n \"data\": \"b58fa5d7e3ac0bc732609082acc6834fd980ce4b8c4052d2bec2d5130acf8421\"\n}",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "JSON"]
}
],
},
// ============= Error Cases =============
{
name: "From Bech32: mixed case error",
input: "bc1FpjKcmr0gzsgcg",
expectedOutput: "Invalid Bech32 string: mixed case is not allowed. Use all uppercase or all lowercase.",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Hex"]
}
],
},
{
name: "From Bech32: no separator error",
input: "noseparator",
expectedOutput: "Invalid Bech32 string: no separator '1' found.",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Hex"]
}
],
},
{
name: "From Bech32: empty HRP error",
input: "1qqqqqqqqqqqqqqqq",
expectedOutput: "Invalid Bech32 string: Human-Readable Part (HRP) cannot be empty.",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Hex"]
}
],
},
{
name: "From Bech32: invalid checksum",
input: "bc1fpjkcmr0gzsgcx",
expectedOutput: "Invalid Bech32/Bech32m string: checksum verification failed.",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Hex"]
}
],
},
{
name: "From Bech32: data too short",
input: "bc1abc",
expectedOutput: "Invalid Bech32 string: data part is too short (minimum 6 characters for checksum).",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Hex"]
}
],
},
{
name: "From Bech32: wrong encoding specified",
input: "bc1fpjkcmr0gzsgcg",
expectedOutput: "Invalid Bech32m checksum.",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32m", "Hex"]
}
],
},
// ============= BIP-0173 Test Vectors (Bech32) =============
{
name: "From Bech32: BIP-0173 A12UEL5L (empty data)",
input: "A12UEL5L",
expectedOutput: "",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "Hex"]
}
],
},
{
name: "From Bech32: BIP-0173 a12uel5l lowercase",
input: "a12uel5l",
expectedOutput: "",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "Hex"]
}
],
},
{
name: "From Bech32: BIP-0173 long HRP with bio",
input: "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs",
expectedOutput: "",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "Hex"]
}
],
},
{
name: "From Bech32: BIP-0173 abcdef with data",
input: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
expectedOutput: "abcdef: 00443214c74254b635cf84653a56d7c675be77df",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "HRP: Hex"]
}
],
},
{
name: "From Bech32: BIP-0173 split HRP",
input: "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
expectedOutput: "split: c5f38b70305f519bf66d85fb6cf03058f3dde463ecd7918f2dc743918f2d",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "HRP: Hex"]
}
],
},
{
name: "From Bech32: BIP-0173 question mark HRP",
input: "?1ezyfcl",
expectedOutput: "",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32", "Hex"]
}
],
},
// ============= BIP-0350 Test Vectors (Bech32m) =============
{
name: "From Bech32m: BIP-0350 A1LQFN3A (empty data)",
input: "A1LQFN3A",
expectedOutput: "",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32m", "Hex"]
}
],
},
{
name: "From Bech32m: BIP-0350 a1lqfn3a lowercase",
input: "a1lqfn3a",
expectedOutput: "",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32m", "Hex"]
}
],
},
{
name: "From Bech32m: BIP-0350 long HRP",
input: "an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6",
expectedOutput: "",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32m", "Hex"]
}
],
},
{
name: "From Bech32m: BIP-0350 abcdef with data",
input: "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx",
expectedOutput: "abcdef: ffbbcdeb38bdab49ca307b9ac5a928398a418820",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32m", "HRP: Hex"]
}
],
},
{
name: "From Bech32m: BIP-0350 split HRP",
input: "split1checkupstagehandshakeupstreamerranterredcaperredlc445v",
expectedOutput: "split: c5f38b70305f519bf66d85fb6cf03058f3dde463ecd7918f2dc743918f2d",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32m", "HRP: Hex"]
}
],
},
{
name: "From Bech32m: BIP-0350 question mark HRP",
input: "?1v759aa",
expectedOutput: "",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Bech32m", "Hex"]
}
],
},
// ============= Bitcoin scriptPubKey Output Format Tests =============
// Test vectors from BIP-0173 and BIP-0350
{
name: "From Bech32: Bitcoin scriptPubKey v0 P2WPKH",
input: "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4",
expectedOutput: "0014751e76e8199196d454941c45d1b3a323f1433bd6",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Bitcoin scriptPubKey"]
}
],
},
{
name: "From Bech32: Bitcoin scriptPubKey v0 P2WSH",
input: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
expectedOutput: "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Bitcoin scriptPubKey"]
}
],
},
{
name: "From Bech32: Bitcoin scriptPubKey v1 Taproot (Bech32m)",
input: "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0",
expectedOutput: "512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Bitcoin scriptPubKey"]
}
],
},
{
name: "From Bech32: Bitcoin scriptPubKey v16",
input: "BC1SW50QGDZ25J",
expectedOutput: "6002751e",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Bitcoin scriptPubKey"]
}
],
},
{
name: "From Bech32: Bitcoin scriptPubKey v2",
input: "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs",
expectedOutput: "5210751e76e8199196d454941c45d1b3a323",
recipeConfig: [
{
"op": "From Bech32",
"args": ["Auto-detect", "Bitcoin scriptPubKey"]
}
],
},
// ============= Bitcoin SegWit Encoding Tests =============
{
name: "To Bech32: Bitcoin SegWit v0 P2WPKH",
input: "751e76e8199196d454941c45d1b3a323f1433bd6",
expectedOutput: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
recipeConfig: [
{
"op": "To Bech32",
"args": ["bc", "Bech32", "Hex", "Bitcoin SegWit", 0]
}
],
},
{
name: "To Bech32: Bitcoin SegWit v0 P2WSH testnet",
input: "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",
expectedOutput: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
recipeConfig: [
{
"op": "To Bech32",
"args": ["tb", "Bech32", "Hex", "Bitcoin SegWit", 0]
}
],
},
{
name: "To Bech32m: Bitcoin Taproot v1",
input: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
expectedOutput: "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0",
recipeConfig: [
{
"op": "To Bech32",
"args": ["bc", "Bech32m", "Hex", "Bitcoin SegWit", 1]
}
],
},
{
name: "To Bech32m: Bitcoin SegWit v16",
input: "751e",
expectedOutput: "bc1sw50qgdz25j",
recipeConfig: [
{
"op": "To Bech32",
"args": ["bc", "Bech32m", "Hex", "Bitcoin SegWit", 16]
}
],
},
// ============= Round-trip Tests =============
{
name: "Bech32: encode then decode round-trip",
input: "The quick brown fox jumps over the lazy dog",
expectedOutput: "The quick brown fox jumps over the lazy dog",
recipeConfig: [
{
"op": "To Bech32",
"args": ["test", "Bech32", "Raw bytes", "Generic", 0]
},
{
"op": "From Bech32",
"args": ["Bech32", "Raw"]
}
],
},
{
name: "Bech32m: encode then decode round-trip",
input: "The quick brown fox jumps over the lazy dog",
expectedOutput: "The quick brown fox jumps over the lazy dog",
recipeConfig: [
{
"op": "To Bech32",
"args": ["test", "Bech32m", "Raw bytes", "Generic", 0]
},
{
"op": "From Bech32",
"args": ["Bech32m", "Raw"]
}
],
},
{
name: "Bech32: binary data round-trip",
input: "0001020304050607",
expectedOutput: "0001020304050607",
recipeConfig: [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "To Bech32",
"args": ["bc", "Bech32", "Raw bytes", "Generic", 0]
},
{
"op": "From Bech32",
"args": ["Bech32", "Hex"]
}
],
},
{
name: "Bech32: auto-detect round-trip",
input: "CyberChef Bech32 Test",
expectedOutput: "CyberChef Bech32 Test",
recipeConfig: [
{
"op": "To Bech32",
"args": ["cyberchef", "Bech32", "Raw bytes", "Generic", 0]
},
{
"op": "From Bech32",
"args": ["Auto-detect", "Raw"]
}
],
},
{
name: "Bech32m: auto-detect round-trip",
input: "CyberChef Bech32m Test",
expectedOutput: "CyberChef Bech32m Test",
recipeConfig: [
{
"op": "To Bech32",
"args": ["cyberchef", "Bech32m", "Raw bytes", "Generic", 0]
},
{
"op": "From Bech32",
"args": ["Auto-detect", "Raw"]
}
],
},
]);

File diff suppressed because it is too large Load Diff

View File

@ -1,241 +0,0 @@
/**
* Checksum tests.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
const BASIC_STRING = "The ships hung in the sky in much the same way that bricks don't.";
const UTF8_STR = "ნუ პანიკას";
const ALL_BYTES = [
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
].join("");
TestRegister.addTests([
{
name: "CRC-8: nothing",
input: "",
expectedOutput: "00",
recipeConfig: [
{
"op": "CRC-8 Checksum",
"args": ["CRC-8"]
}
]
},
{
name: "CRC-8: default check",
input: "123456789",
expectedOutput: "f4",
recipeConfig: [
{
"op": "CRC-8 Checksum",
"args": ["CRC-8"]
}
]
},
{
name: "CRC-8: CDMA2000",
input: "123456789",
expectedOutput: "da",
recipeConfig: [
{
"op": "CRC-8 Checksum",
"args": ["CRC-8/CDMA2000"]
}
]
},
{
name: "CRC-8: DARC",
input: "123456789",
expectedOutput: "15",
recipeConfig: [
{
"op": "CRC-8 Checksum",
"args": ["CRC-8/DARC"]
}
]
},
{
name: "CRC-8: DVB-S2",
input: "123456789",
expectedOutput: "bc",
recipeConfig: [
{
"op": "CRC-8 Checksum",
"args": ["CRC-8/DVB-S2"]
}
]
},
{
name: "CRC-8: EBU",
input: "123456789",
expectedOutput: "97",
recipeConfig: [
{
"op": "CRC-8 Checksum",
"args": ["CRC-8/EBU"]
}
]
},
{
name: "CRC-8: I-CODE",
input: "123456789",
expectedOutput: "7e",
recipeConfig: [
{
"op": "CRC-8 Checksum",
"args": ["CRC-8/I-CODE"]
}
]
},
{
name: "CRC-8: ITU",
input: "123456789",
expectedOutput: "a1",
recipeConfig: [
{
"op": "CRC-8 Checksum",
"args": ["CRC-8/ITU"]
}
]
},
{
name: "CRC-8: MAXIM",
input: "123456789",
expectedOutput: "a1",
recipeConfig: [
{
"op": "CRC-8 Checksum",
"args": ["CRC-8/MAXIM"]
}
]
},
{
name: "CRC-8: ROHC",
input: "123456789",
expectedOutput: "d0",
recipeConfig: [
{
"op": "CRC-8 Checksum",
"args": ["CRC-8/ROHC"]
}
]
},
{
name: "CRC-8: WCDMA",
input: "123456789",
expectedOutput: "25",
recipeConfig: [
{
"op": "CRC-8 Checksum",
"args": ["CRC-8/WCDMA"]
}
]
},
{
name: "CRC-16: nothing",
input: "",
expectedOutput: "0000",
recipeConfig: [
{
"op": "CRC-16 Checksum",
"args": []
}
]
},
{
name: "CRC-16: basic string",
input: BASIC_STRING,
expectedOutput: "0c70",
recipeConfig: [
{
"op": "CRC-16 Checksum",
"args": []
}
]
},
{
name: "CRC-16: UTF-8",
input: UTF8_STR,
expectedOutput: "dcf6",
recipeConfig: [
{
"op": "CRC-16 Checksum",
"args": []
}
]
},
{
name: "CRC-16: all bytes",
input: ALL_BYTES,
expectedOutput: "bad3",
recipeConfig: [
{
"op": "CRC-16 Checksum",
"args": []
}
]
},
{
name: "CRC-32: nothing",
input: "",
expectedOutput: "00000000",
recipeConfig: [
{
"op": "CRC-32 Checksum",
"args": []
}
]
},
{
name: "CRC-32: basic string",
input: BASIC_STRING,
expectedOutput: "bf4b739c",
recipeConfig: [
{
"op": "CRC-32 Checksum",
"args": []
}
]
},
{
name: "CRC-32: UTF-8",
input: UTF8_STR,
expectedOutput: "87553290",
recipeConfig: [
{
"op": "CRC-32 Checksum",
"args": []
}
]
},
{
name: "CRC-32: all bytes",
input: ALL_BYTES,
expectedOutput: "29058c73",
recipeConfig: [
{
"op": "CRC-32 Checksum",
"args": []
}
]
}
]);

View File

@ -528,4 +528,15 @@ TestRegister.addTests([
}
],
},
{
name: "Rail Fence Cipher Encode: Normal with Offset with Spaces",
input: "No one expects the spanish Inquisition.",
expectedOutput: " e n ut.ooeepcstesaihIqiiinNnxthpsnso",
recipeConfig: [
{
"op": "Rail Fence Cipher Encode",
"args": [3, 2]
}
],
},
]);

View File

@ -322,8 +322,21 @@ TestRegister.addTests([
]
}
],
expectedMatch: /^Invalid JPath expression: jsonPath: self is not defined:/
expectedMatch: /^Invalid JPath expression: Unexpected "{" at character 1/
},
{
name: "JPath Expression: Script-based RCE",
input: "[{}]",
recipeConfig: [
{
"op": "JPath expression",
"args": [
"$..[?(p=\"console.log(this.process.mainModule.require('child_process').execSync('id').toString())\";a=''[['constructor']][['constructor']](p);a())]",
"\n"
]
}
],
expectedMatch: /^Invalid JPath expression: jsonPath: Cannot read properties of {2}\(reading 'constructor'\): / },
{
name: "CSS selector",
input: '<div id="test">\n<p class="a">hello</p>\n<p>world</p>\n<p class="a">again</p>\n</div>',

View File

@ -28,6 +28,28 @@ TestRegister.addTests([
args: ["From Leet Speak"]
}
]
},
{
name: "Convert to Leet Speak: basic text, keep case",
input: "HELLO",
expectedOutput: "H3LL0",
recipeConfig: [
{
op: "Convert Leet Speak",
args: ["To Leet Speak"]
}
]
},
{
name: "Convert from Leet Speak: basic leet, keep case",
input: "H3LL0",
expectedOutput: "HeLLo",
recipeConfig: [
{
op: "Convert Leet Speak",
args: ["From Leet Speak"]
}
]
}
]);

View File

@ -6,7 +6,10 @@
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
import { ASCII_TEXT } from "../../samples/Ciphers.mjs";
import {ALL_BYTES, ASCII_TEXT, UTF8_TEXT} from "../../samples/Ciphers.mjs";
const SOME_HEX_BYTES = "cdb23f958e018418621d9e489b7bba0f0c481f604eba2eb1ea35e38f99490cc0";
const SOME_BASE64_BYTES = "zbI/lY4BhBhiHZ5Im3u6DwxIH2BOui6x6jXjj5lJDMA=";
const P256 = {
// openssl ecparam -name prime256v1 -genkey -noout -out p256.priv.key
@ -104,7 +107,7 @@ TestRegister.addTests([
},
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "MD5", P256.publicKey, ASCII_TEXT]
"args": ["ASN.1 HEX", "MD5", P256.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -119,7 +122,7 @@ TestRegister.addTests([
},
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-1", P256.publicKey, ASCII_TEXT]
"args": ["ASN.1 HEX", "SHA-1", P256.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -134,7 +137,7 @@ TestRegister.addTests([
},
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-256", P256.publicKey, ASCII_TEXT]
"args": ["ASN.1 HEX", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -149,7 +152,7 @@ TestRegister.addTests([
},
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-384", P256.publicKey, ASCII_TEXT]
"args": ["ASN.1 HEX", "SHA-384", P256.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -164,7 +167,7 @@ TestRegister.addTests([
},
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-512", P256.publicKey, ASCII_TEXT]
"args": ["ASN.1 HEX", "SHA-512", P256.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -179,7 +182,7 @@ TestRegister.addTests([
},
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-256", P256.publicKey, ASCII_TEXT]
"args": ["ASN.1 HEX", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -194,7 +197,7 @@ TestRegister.addTests([
},
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-384", P384.publicKey, ASCII_TEXT]
"args": ["ASN.1 HEX", "SHA-384", P384.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -209,7 +212,7 @@ TestRegister.addTests([
},
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-512", P521.publicKey, ASCII_TEXT]
"args": ["ASN.1 HEX", "SHA-512", P521.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -246,7 +249,7 @@ TestRegister.addTests([
recipeConfig: [
{
"op": "ECDSA Verify",
"args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT]
"args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -257,7 +260,7 @@ TestRegister.addTests([
recipeConfig: [
{
"op": "ECDSA Verify",
"args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT]
"args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -268,7 +271,7 @@ TestRegister.addTests([
recipeConfig: [
{
"op": "ECDSA Verify",
"args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT]
"args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -279,7 +282,7 @@ TestRegister.addTests([
recipeConfig: [
{
"op": "ECDSA Verify",
"args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT]
"args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -290,7 +293,7 @@ TestRegister.addTests([
recipeConfig: [
{
"op": "ECDSA Verify",
"args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT]
"args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -301,7 +304,7 @@ TestRegister.addTests([
recipeConfig: [
{
"op": "ECDSA Verify",
"args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT]
"args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT, "Raw"]
}
]
},
@ -312,7 +315,7 @@ TestRegister.addTests([
recipeConfig: [
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-256", P256.privateKeyPkcs1, ASCII_TEXT]
"args": ["ASN.1 HEX", "SHA-256", P256.privateKeyPkcs1, ASCII_TEXT, "Raw"]
}
]
},
@ -323,7 +326,7 @@ TestRegister.addTests([
recipeConfig: [
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-256", PEM_PUB_RSA512, ASCII_TEXT]
"args": ["ASN.1 HEX", "SHA-256", PEM_PUB_RSA512, ASCII_TEXT, "Raw"]
}
]
},
@ -460,5 +463,73 @@ TestRegister.addTests([
"args": ["Auto", "Raw JSON"]
}
]
},
{
name: "ECDSA Sign/Verify: P-256 with SHA256 UTF8",
input: UTF8_TEXT,
expectedOutput: "Verified OK",
recipeConfig: [
{
"op": "ECDSA Sign",
"args": [P256.privateKeyPkcs1, "SHA-256", "ASN.1 HEX"]
},
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-256", P256.publicKey, UTF8_TEXT, "Raw"]
}
]
},
{
name: "ECDSA Sign/Verify: P-256 with SHA256 bytes raw",
input: ALL_BYTES,
expectedOutput: "Verified OK",
recipeConfig: [
{
"op": "ECDSA Sign",
"args": [P256.privateKeyPkcs1, "SHA-256", "ASN.1 HEX"]
},
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-256", P256.publicKey, ALL_BYTES, "Raw"]
}
]
},
{
name: "ECDSA Sign/Verify: P-256 with SHA256 bytes hex",
input: SOME_HEX_BYTES,
expectedOutput: "Verified OK",
recipeConfig: [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "ECDSA Sign",
"args": [P256.privateKeyPkcs1, "SHA-256", "ASN.1 HEX"]
},
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-256", P256.publicKey, SOME_HEX_BYTES, "Hex"]
}
]
},
{
name: "ECDSA Sign/Verify: P-256 with SHA256 bytes Base64",
input: SOME_BASE64_BYTES,
expectedOutput: "Verified OK",
recipeConfig: [
{
"op": "From Base64",
"args": ["A-Za-z0-9+/=", true]
},
{
"op": "ECDSA Sign",
"args": [P256.privateKeyPkcs1, "SHA-256", "ASN.1 HEX"]
},
{
"op": "ECDSA Verify",
"args": ["ASN.1 HEX", "SHA-256", P256.publicKey, SOME_BASE64_BYTES, "Base64"]
}
]
}
]);

View File

@ -0,0 +1,133 @@
/**
* ExtractIPAddresses tests.
*
* @author gchqdev365 [gchqdev365@outlook.com]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "ExtractIPAddress All Zeros",
input: "0.0.0.0",
expectedOutput: "0.0.0.0",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
{
name: "ExtractIPAddress All 10s",
input: "10.10.10.10",
expectedOutput: "10.10.10.10",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
{
name: "ExtractIPAddress All 10s",
input: "100.100.100.100",
expectedOutput: "100.100.100.100",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
{
name: "ExtractIPAddress 255s",
input: "255.255.255.255",
expectedOutput: "255.255.255.255",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
{
name: "ExtractIPAddress double digits",
input: "10.10.10.10 25.25.25.25 99.99.99.99",
expectedOutput: "10.10.10.10\n25.25.25.25\n99.99.99.99",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
{
name: "ExtractIPAddress 256 in middle",
input: "255.256.255.255 255.255.256.255",
expectedOutput: "",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
{
name: "ExtractIPAddress 256 at each end",
input: "256.255.255.255 255.255.255.256",
expectedOutput: "",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
{
name: "ExtractIPAddress silly example",
input: "710.65.0.456",
expectedOutput: "",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
{
name: "ExtractIPAddress longer dotted decimal",
input: "1.2.3.4.5.6.7.8",
expectedOutput: "1.2.3.4\n5.6.7.8",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
{
name: "ExtractIPAddress octal valid",
input: "01.01.01.01 0123.0123.0123.0123 0377.0377.0377.0377",
expectedOutput: "01.01.01.01\n0123.0123.0123.0123\n0377.0377.0377.0377",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
{
name: "ExtractIPAddress octal invalid",
input: "0378.01.01.01 03.0377.2.3",
expectedOutput: "",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
]);

View File

@ -0,0 +1,805 @@
/**
* GenerateAllChecksums tests.
*
* @author r4mos [2k95ljkhg@mozmail.com]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
const CHECK_STRING = "123456789";
TestRegister.addTests([
{
name: "Full generate all checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-3/GSM: 4
CRC-3/ROHC: 6
CRC-4/G-704: 7
CRC-4/INTERLAKEN: b
CRC-4/ITU: 7
CRC-5/EPC: 00
CRC-5/EPC-C1G2: 00
CRC-5/G-704: 07
CRC-5/ITU: 07
CRC-5/USB: 19
CRC-6/CDMA2000-A: 0d
CRC-6/CDMA2000-B: 3b
CRC-6/DARC: 26
CRC-6/G-704: 06
CRC-6/GSM: 13
CRC-6/ITU: 06
CRC-7/MMC: 75
CRC-7/ROHC: 53
CRC-7/UMTS: 61
CRC-8: f4
CRC-8/8H2F: df
CRC-8/AES: 97
CRC-8/AUTOSAR: df
CRC-8/BLUETOOTH: 26
CRC-8/CDMA2000: da
CRC-8/DARC: 15
CRC-8/DVB-S2: bc
CRC-8/EBU: 97
CRC-8/GSM-A: 37
CRC-8/GSM-B: 94
CRC-8/HITAG: b4
CRC-8/I-432-1: a1
CRC-8/I-CODE: 7e
CRC-8/ITU: a1
CRC-8/LTE: ea
CRC-8/MAXIM: a1
CRC-8/MAXIM-DOW: a1
CRC-8/MIFARE-MAD: 99
CRC-8/NRSC-5: f7
CRC-8/OPENSAFETY: 3e
CRC-8/ROHC: d0
CRC-8/SAE-J1850: 4b
CRC-8/SAE-J1850-ZERO: 37
CRC-8/SMBUS: f4
CRC-8/TECH-3250: 97
CRC-8/WCDMA: 25
Fletcher-8: 0c
CRC-10/ATM: 199
CRC-10/CDMA2000: 233
CRC-10/GSM: 12a
CRC-10/I-610: 199
CRC-11/FLEXRAY: 5a3
CRC-11/UMTS: 061
CRC-12/3GPP: daf
CRC-12/CDMA2000: d4d
CRC-12/DECT: f5b
CRC-12/GSM: b34
CRC-12/UMTS: daf
CRC-13/BBC: 04fa
CRC-14/DARC: 082d
CRC-14/GSM: 30ae
CRC-15/CAN: 059e
CRC-15/MPT1327: 2566
CRC-16: bb3d
CRC-16/A: bf05
CRC-16/ACORN: 31c3
CRC-16/ARC: bb3d
CRC-16/AUG-CCITT: e5cc
CRC-16/AUTOSAR: 29b1
CRC-16/B: 906e
CRC-16/BLUETOOTH: 2189
CRC-16/BUYPASS: fee8
CRC-16/CCITT: 2189
CRC-16/CCITT-FALSE: 29b1
CRC-16/CCITT-TRUE: 2189
CRC-16/CCITT-ZERO: 31c3
CRC-16/CDMA2000: 4c06
CRC-16/CMS: aee7
CRC-16/DARC: d64e
CRC-16/DDS-110: 9ecf
CRC-16/DECT-R: 007e
CRC-16/DECT-X: 007f
CRC-16/DNP: ea82
CRC-16/EN-13757: c2b7
CRC-16/EPC: d64e
CRC-16/EPC-C1G2: d64e
CRC-16/GENIBUS: d64e
CRC-16/GSM: ce3c
CRC-16/I-CODE: d64e
CRC-16/IBM: bb3d
CRC-16/IBM-3740: 29b1
CRC-16/IBM-SDLC: 906e
CRC-16/IEC-61158-2: a819
CRC-16/ISO-HDLC: 906e
CRC-16/ISO-IEC-14443-3-A: bf05
CRC-16/ISO-IEC-14443-3-B: 906e
CRC-16/KERMIT: 2189
CRC-16/LHA: bb3d
CRC-16/LJ1200: bdf4
CRC-16/LTE: 31c3
CRC-16/M17: 772b
CRC-16/MAXIM: 44c2
CRC-16/MAXIM-DOW: 44c2
CRC-16/MCRF4XX: 6f91
CRC-16/MODBUS: 4b37
CRC-16/NRSC-5: a066
CRC-16/OPENSAFETY-A: 5d38
CRC-16/OPENSAFETY-B: 20fe
CRC-16/PROFIBUS: a819
CRC-16/RIELLO: 63d0
CRC-16/SPI-FUJITSU: e5cc
CRC-16/T10-DIF: d0db
CRC-16/TELEDISK: 0fb3
CRC-16/TMS37157: 26b1
CRC-16/UMTS: fee8
CRC-16/USB: b4c8
CRC-16/V-41-LSB: 2189
CRC-16/V-41-MSB: 31c3
CRC-16/VERIFONE: fee8
CRC-16/X-25: 906e
CRC-16/XMODEM: 31c3
CRC-16/ZMODEM: 31c3
Fletcher-16: 1ede
CRC-17/CAN-FD: 04f03
CRC-21/CAN-FD: 0ed841
CRC-24/BLE: c25a56
CRC-24/FLEXRAY-A: 7979bd
CRC-24/FLEXRAY-B: 1f23b8
CRC-24/INTERLAKEN: b4f3e6
CRC-24/LTE-A: cde703
CRC-24/LTE-B: 23ef52
CRC-24/OPENPGP: 21cf02
CRC-24/OS-9: 200fa5
CRC-30/CDMA: 04c34abf
CRC-31/PHILIPS: 0ce9e46c
Adler-32: 091e01de
CRC-32: cbf43926
CRC-32/AAL5: fc891918
CRC-32/ADCCP: cbf43926
CRC-32/AIXM: 3010bf7f
CRC-32/AUTOSAR: 1697d06a
CRC-32/BASE91-C: e3069283
CRC-32/BASE91-D: 87315576
CRC-32/BZIP2: fc891918
CRC-32/C: e3069283
CRC-32/CASTAGNOLI: e3069283
CRC-32/CD-ROM-EDC: 6ec2edc4
CRC-32/CKSUM: 765e7680
CRC-32/D: 87315576
CRC-32/DECT-B: fc891918
CRC-32/INTERLAKEN: e3069283
CRC-32/ISCSI: e3069283
CRC-32/ISO-HDLC: cbf43926
CRC-32/JAMCRC: 340bc6d9
CRC-32/MEF: d2c22f51
CRC-32/MPEG-2: 0376e6e7
CRC-32/NVME: e3069283
CRC-32/PKZIP: cbf43926
CRC-32/POSIX: 765e7680
CRC-32/Q: 3010bf7f
CRC-32/SATA: cf72afe8
CRC-32/V-42: cbf43926
CRC-32/XFER: bd0be338
CRC-32/XZ: cbf43926
Fletcher-32: df09d509
CRC-40/GSM: d4164fc646
CRC-64/ECMA-182: 6c40df5f0b497347
CRC-64/GO-ECMA: 995dc9bbdf1939fa
CRC-64/GO-ISO: b90956c775a41001
CRC-64/MS: 75d4b74f024eceea
CRC-64/NVME: ae8b14860a799888
CRC-64/REDIS: e9c6d914c4b8d9ca
CRC-64/WE: 62ec59e3f1a4f00a
CRC-64/XZ: 995dc9bbdf1939fa
Fletcher-64: 0d0803376c6a689f
CRC-82/DARC: 09ea83f625023801fd612
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["All", true]
}
]
},
{
name: "Full generate all checksums without name",
input: CHECK_STRING,
expectedOutput: `4
6
7
b
7
00
00
07
07
19
0d
3b
26
06
13
06
75
53
61
f4
df
97
df
26
da
15
bc
97
37
94
b4
a1
7e
a1
ea
a1
a1
99
f7
3e
d0
4b
37
f4
97
25
0c
199
233
12a
199
5a3
061
daf
d4d
f5b
b34
daf
04fa
082d
30ae
059e
2566
bb3d
bf05
31c3
bb3d
e5cc
29b1
906e
2189
fee8
2189
29b1
2189
31c3
4c06
aee7
d64e
9ecf
007e
007f
ea82
c2b7
d64e
d64e
d64e
ce3c
d64e
bb3d
29b1
906e
a819
906e
bf05
906e
2189
bb3d
bdf4
31c3
772b
44c2
44c2
6f91
4b37
a066
5d38
20fe
a819
63d0
e5cc
d0db
0fb3
26b1
fee8
b4c8
2189
31c3
fee8
906e
31c3
31c3
1ede
04f03
0ed841
c25a56
7979bd
1f23b8
b4f3e6
cde703
23ef52
21cf02
200fa5
04c34abf
0ce9e46c
091e01de
cbf43926
fc891918
cbf43926
3010bf7f
1697d06a
e3069283
87315576
fc891918
e3069283
e3069283
6ec2edc4
765e7680
87315576
fc891918
e3069283
e3069283
cbf43926
340bc6d9
d2c22f51
0376e6e7
e3069283
cbf43926
765e7680
3010bf7f
cf72afe8
cbf43926
bd0be338
cbf43926
df09d509
d4164fc646
6c40df5f0b497347
995dc9bbdf1939fa
b90956c775a41001
75d4b74f024eceea
ae8b14860a799888
e9c6d914c4b8d9ca
62ec59e3f1a4f00a
995dc9bbdf1939fa
0d0803376c6a689f
09ea83f625023801fd612
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["All", false]
}
]
},
{
name: "Full generate 3 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-3/GSM: 4
CRC-3/ROHC: 6
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["3", true]
}
]
},
{
name: "Full generate 4 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-4/G-704: 7
CRC-4/INTERLAKEN: b
CRC-4/ITU: 7
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["4", true]
}
]
},
{
name: "Full generate 5 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-5/EPC: 00
CRC-5/EPC-C1G2: 00
CRC-5/G-704: 07
CRC-5/ITU: 07
CRC-5/USB: 19
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["5", true]
}
]
},
{
name: "Full generate 6 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-6/CDMA2000-A: 0d
CRC-6/CDMA2000-B: 3b
CRC-6/DARC: 26
CRC-6/G-704: 06
CRC-6/GSM: 13
CRC-6/ITU: 06
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["6", true]
}
]
},
{
name: "Full generate 7 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-7/MMC: 75
CRC-7/ROHC: 53
CRC-7/UMTS: 61
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["7", true]
}
]
},
{
name: "Full generate 8 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-8: f4
CRC-8/8H2F: df
CRC-8/AES: 97
CRC-8/AUTOSAR: df
CRC-8/BLUETOOTH: 26
CRC-8/CDMA2000: da
CRC-8/DARC: 15
CRC-8/DVB-S2: bc
CRC-8/EBU: 97
CRC-8/GSM-A: 37
CRC-8/GSM-B: 94
CRC-8/HITAG: b4
CRC-8/I-432-1: a1
CRC-8/I-CODE: 7e
CRC-8/ITU: a1
CRC-8/LTE: ea
CRC-8/MAXIM: a1
CRC-8/MAXIM-DOW: a1
CRC-8/MIFARE-MAD: 99
CRC-8/NRSC-5: f7
CRC-8/OPENSAFETY: 3e
CRC-8/ROHC: d0
CRC-8/SAE-J1850: 4b
CRC-8/SAE-J1850-ZERO: 37
CRC-8/SMBUS: f4
CRC-8/TECH-3250: 97
CRC-8/WCDMA: 25
Fletcher-8: 0c
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["8", true]
}
]
},
{
name: "Full generate 10 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-10/ATM: 199
CRC-10/CDMA2000: 233
CRC-10/GSM: 12a
CRC-10/I-610: 199
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["10", true]
}
]
},
{
name: "Full generate 11 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-11/FLEXRAY: 5a3
CRC-11/UMTS: 061
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["11", true]
}
]
},
{
name: "Full generate 12 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-12/3GPP: daf
CRC-12/CDMA2000: d4d
CRC-12/DECT: f5b
CRC-12/GSM: b34
CRC-12/UMTS: daf
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["12", true]
}
]
},
{
name: "Full generate 13 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-13/BBC: 04fa
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["13", true]
}
]
},
{
name: "Full generate 14 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-14/DARC: 082d
CRC-14/GSM: 30ae
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["14", true]
}
]
},
{
name: "Full generate 15 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-15/CAN: 059e
CRC-15/MPT1327: 2566
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["15", true]
}
]
},
{
name: "Full generate 16 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-16: bb3d
CRC-16/A: bf05
CRC-16/ACORN: 31c3
CRC-16/ARC: bb3d
CRC-16/AUG-CCITT: e5cc
CRC-16/AUTOSAR: 29b1
CRC-16/B: 906e
CRC-16/BLUETOOTH: 2189
CRC-16/BUYPASS: fee8
CRC-16/CCITT: 2189
CRC-16/CCITT-FALSE: 29b1
CRC-16/CCITT-TRUE: 2189
CRC-16/CCITT-ZERO: 31c3
CRC-16/CDMA2000: 4c06
CRC-16/CMS: aee7
CRC-16/DARC: d64e
CRC-16/DDS-110: 9ecf
CRC-16/DECT-R: 007e
CRC-16/DECT-X: 007f
CRC-16/DNP: ea82
CRC-16/EN-13757: c2b7
CRC-16/EPC: d64e
CRC-16/EPC-C1G2: d64e
CRC-16/GENIBUS: d64e
CRC-16/GSM: ce3c
CRC-16/I-CODE: d64e
CRC-16/IBM: bb3d
CRC-16/IBM-3740: 29b1
CRC-16/IBM-SDLC: 906e
CRC-16/IEC-61158-2: a819
CRC-16/ISO-HDLC: 906e
CRC-16/ISO-IEC-14443-3-A: bf05
CRC-16/ISO-IEC-14443-3-B: 906e
CRC-16/KERMIT: 2189
CRC-16/LHA: bb3d
CRC-16/LJ1200: bdf4
CRC-16/LTE: 31c3
CRC-16/M17: 772b
CRC-16/MAXIM: 44c2
CRC-16/MAXIM-DOW: 44c2
CRC-16/MCRF4XX: 6f91
CRC-16/MODBUS: 4b37
CRC-16/NRSC-5: a066
CRC-16/OPENSAFETY-A: 5d38
CRC-16/OPENSAFETY-B: 20fe
CRC-16/PROFIBUS: a819
CRC-16/RIELLO: 63d0
CRC-16/SPI-FUJITSU: e5cc
CRC-16/T10-DIF: d0db
CRC-16/TELEDISK: 0fb3
CRC-16/TMS37157: 26b1
CRC-16/UMTS: fee8
CRC-16/USB: b4c8
CRC-16/V-41-LSB: 2189
CRC-16/V-41-MSB: 31c3
CRC-16/VERIFONE: fee8
CRC-16/X-25: 906e
CRC-16/XMODEM: 31c3
CRC-16/ZMODEM: 31c3
Fletcher-16: 1ede
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["16", true]
}
]
},
{
name: "Full generate 17 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-17/CAN-FD: 04f03
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["17", true]
}
]
},
{
name: "Full generate 21 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-21/CAN-FD: 0ed841
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["21", true]
}
]
},
{
name: "Full generate 24 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-24/BLE: c25a56
CRC-24/FLEXRAY-A: 7979bd
CRC-24/FLEXRAY-B: 1f23b8
CRC-24/INTERLAKEN: b4f3e6
CRC-24/LTE-A: cde703
CRC-24/LTE-B: 23ef52
CRC-24/OPENPGP: 21cf02
CRC-24/OS-9: 200fa5
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["24", true]
}
]
},
{
name: "Full generate 30 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-30/CDMA: 04c34abf
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["30", true]
}
]
},
{
name: "Full generate 31 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-31/PHILIPS: 0ce9e46c
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["31", true]
}
]
},
{
name: "Full generate 32 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `Adler-32: 091e01de
CRC-32: cbf43926
CRC-32/AAL5: fc891918
CRC-32/ADCCP: cbf43926
CRC-32/AIXM: 3010bf7f
CRC-32/AUTOSAR: 1697d06a
CRC-32/BASE91-C: e3069283
CRC-32/BASE91-D: 87315576
CRC-32/BZIP2: fc891918
CRC-32/C: e3069283
CRC-32/CASTAGNOLI: e3069283
CRC-32/CD-ROM-EDC: 6ec2edc4
CRC-32/CKSUM: 765e7680
CRC-32/D: 87315576
CRC-32/DECT-B: fc891918
CRC-32/INTERLAKEN: e3069283
CRC-32/ISCSI: e3069283
CRC-32/ISO-HDLC: cbf43926
CRC-32/JAMCRC: 340bc6d9
CRC-32/MEF: d2c22f51
CRC-32/MPEG-2: 0376e6e7
CRC-32/NVME: e3069283
CRC-32/PKZIP: cbf43926
CRC-32/POSIX: 765e7680
CRC-32/Q: 3010bf7f
CRC-32/SATA: cf72afe8
CRC-32/V-42: cbf43926
CRC-32/XFER: bd0be338
CRC-32/XZ: cbf43926
Fletcher-32: df09d509
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["32", true]
}
]
},
{
name: "Full generate 40 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-40/GSM: d4164fc646
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["40", true]
}
]
},
{
name: "Full generate 64 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-64/ECMA-182: 6c40df5f0b497347
CRC-64/GO-ECMA: 995dc9bbdf1939fa
CRC-64/GO-ISO: b90956c775a41001
CRC-64/MS: 75d4b74f024eceea
CRC-64/NVME: ae8b14860a799888
CRC-64/REDIS: e9c6d914c4b8d9ca
CRC-64/WE: 62ec59e3f1a4f00a
CRC-64/XZ: 995dc9bbdf1939fa
Fletcher-64: 0d0803376c6a689f
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["64", true]
}
]
},
{
name: "Full generate 82 bits checksums with name",
input: CHECK_STRING,
expectedOutput: `CRC-82/DARC: 09ea83f625023801fd612
`,
recipeConfig: [
{
"op": "Generate all checksums",
"args": ["82", true]
}
]
}
]);

View File

@ -54,16 +54,6 @@ LM Hash: 01FC5A6BE7BC6929AAD3B435B51404EE
NT Hash: 0CB6948805F797BF2A82807973B89537
SSDEEP: 3:Hn:Hn
CTPH: A:E:E
Checksums:
Fletcher-8: 3d
Fletcher-16: 5dc1
Fletcher-32: 3f5cd9e7
Fletcher-64: 7473657474736574
Adler-32: 045d01c1
CRC-8: b9
CRC-16: f82e
CRC-32: d87f7e0c
`,
recipeConfig: [
{

View File

@ -152,6 +152,17 @@ TestRegister.addTests([
}
],
},
{
name: "From Hexdump: xxd format, odd number of bytes",
input: "00000000: 6162 6364 65 abcde",
expectedOutput: "abcde",
recipeConfig: [
{
op: "From Hexdump",
args: []
}
],
},
{
name: "From Hexdump: Wireshark",
input: `00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ........ ........

View File

@ -30,6 +30,28 @@ TestRegister.addTests([
}
],
},
{
name: "JA4 Fingerprint: TLS 1.3 with whitespace-only ALPN",
input: "1603010200010001fc0303ed338a18e711d670cdc472ff570a5b59f1ace12e5365918bf68bf845019147b6207e4437bfb062d98a4aeb753be8f09022a9dc9413d7694dad4db57fcdcf076e820024130213031301c02cc030c02bc02fcca9cca8c024c028c023c027009f009e006b006700ff0100018f0000001800160000136465762e636f6e74656e74677261622e6e6574000b000403000102000a00160014001d0017001e00190018010001010102010301040023000000100004000201200016000000170000000d002a0028040305030603080708080809080a080b080408050806040105010601030303010302040205020602002b00050403040303002d00020101003300260024001d00207af053336d5e2c1675aa4c6ce78de5e5fdbd296538113f051ea17ccb64289f22001500d2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
expectedOutput: "t13d181220_85036bcba153_d41ae481755e",
recipeConfig: [
{
"op": "JA4 Fingerprint",
"args": ["Hex", "JA4"]
}
],
},
{
name: "JA4 Fingerprint: TLS 1.3 with ALPN containing a whitespace",
input: "1603010200010001fc0303273682a603be3f64dd025df4ad0f4d2d13043c3a233405a68bb29b865808749a20f4dfc40242b2fce38fae26c516ef9bef20a1b9349eba3c003780168d72471f5c0024130213031301c02cc030c02bc02fcca9cca8c024c028c023c027009f009e006b006700ff0100018f0000001800160000136465762e636f6e74656e74677261622e6e6574000b000403000102000a00160014001d0017001e0019001801000101010201030104002300000010000500030261200016000000170000000d002a0028040305030603080708080809080a080b080408050806040105010601030303010302040205020602002b00050403040303002d00020101003300260024001d0020f4dd1567bd858d3a9f1d88db1fee6a10ab0ea1aa6afe96ffb6a7c4d79dea4075001500d10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
expectedOutput: "t13d181260_85036bcba153_d41ae481755e",
recipeConfig: [
{
"op": "JA4 Fingerprint",
"args": ["Hex", "JA4"]
}
],
},
{
name: "JA4 Fingerprint: TLS 1.2",
input: "1603010200010001fc0303ecb2691addb2bf6c599c7aaae23de5f42561cc04eb41029acc6fc050a16ac1d22046f8617b580ac9358e2aa44e306d52466bcc989c87c8ca64309f5faf50ba7b4d0022130113031302c02bc02fcca9cca8c02cc030c00ac009c013c014009c009d002f00350100019100000021001f00001c636f6e74696c652e73657276696365732e6d6f7a696c6c612e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000022000a000804030503060302030033006b0069001d00208909858fbeb6ed2f1248ba5b9e2978bead0e840110192c61daed0096798b184400170041044d183d91f5eed35791fa982464e3b0214aaa5f5d1b78616d9b9fbebc22d11f535b2f94c686143136aa795e6e5a875d6c08064ad5b76d44caad766e2483012748002b00050403040303000d0018001604030503060308040805080604010501060102030201002d00020101001c000240010015007a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",

View File

@ -0,0 +1,41 @@
/**
* YAML tests.
*
* @author ccarpo [ccarpo@gmx.net]
*
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
const EXAMPLE_YAML = `number: 3\nplain: string\nblock: |\n two\n lines`;
const EXAMPLE_JSON = `{ "number": 3, "plain": "string" }`;
TestRegister.addTests([
{
name: "YAML to JSON",
input: EXAMPLE_YAML,
expectedOutput: JSON.stringify({
"number": 3,
"plain": "string",
"block": "two\nlines\n"
}, null, 4),
recipeConfig: [
{
op: "YAML to JSON",
args: [],
}
],
},
{
name: "JSON to YAML",
input: EXAMPLE_JSON,
expectedOutput: `number: 3\nplain: string\n`,
recipeConfig: [
{
op: "JSON to YAML",
args: [],
}
],
},
]);

View File

@ -0,0 +1,551 @@
/**
* Jsonata Query tests.
*
* @author Jon King [jon@ajarsoftware.com]
*
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
const INPUT_JSON_OBJECT_WITH_ARRAYS = `{
"FirstName": "Fred",
"Surname": "Smith",
"Age": 28,
"Address": {
"Street": "Hursley Park",
"City": "Winchester",
"Postcode": "SO21 2JN"
},
"Phone": [
{
"type": "home",
"number": "0203 544 1234"
},
{
"type": "office",
"number": "01962 001234"
},
{
"type": "office",
"number": "01962 001235"
},
{
"type": "mobile",
"number": "077 7700 1234"
}
],
"Email": [
{
"type": "work",
"address": ["fred.smith@my-work.com", "fsmith@my-work.com"]
},
{
"type": "home",
"address": ["freddy@my-social.com", "frederic.smith@very-serious.com"]
}
],
"Other": {
"Over 18 ?": true,
"Misc": null,
"Alternative.Address": {
"Street": "Brick Lane",
"City": "London",
"Postcode": "E1 6RF"
}
}
}`;
const INPUT_ARRAY_OF_OBJECTS = `[
{ "ref": [ 1,2 ] },
{ "ref": [ 3,4 ] }
]`;
const INPUT_NUMBER_ARRAY = `{
"Numbers": [1, 2.4, 3.5, 10, 20.9, 30]
}`;
TestRegister.addTests([
{
name: "Jsonata: Returns a JSON string (double quoted)",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '"Smith"',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Surname"],
},
],
},
{
name: "Jsonata: Returns a JSON number",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: "28",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Age"],
},
],
},
{
name: "Jsonata: Field references are separated by '.'",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '"Winchester"',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Address.City"],
},
],
},
{
name: "Jsonata: Matched the path and returns the null value",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: "null",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Other.Misc"],
},
],
},
{
name: "Jsonata: Path not found. Returns nothing",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '""',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Other.DoesntExist"],
},
],
},
{
name: "Jsonata: Field references containing whitespace or reserved tokens can be enclosed in backticks",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: "true",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Other.`Over 18 ?`"],
},
],
},
{
name: "Jsonata: Returns the first item (an object)",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '{"type":"home","number":"0203 544 1234"}',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Phone[0]"],
},
],
},
{
name: "Jsonata: Returns the second item",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '{"type":"office","number":"01962 001234"}',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Phone[1]"],
},
],
},
{
name: "Jsonata: Returns the last item",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '{"type":"mobile","number":"077 7700 1234"}',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Phone[-1]"],
},
],
},
{
name: "Jsonata: Negative indexed count from the end",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '{"type":"office","number":"01962 001235"}',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Phone[-2]"],
},
],
},
{
name: "Jsonata: Doesn't exist - returns nothing",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '""',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Phone[8]"],
},
],
},
{
name: "Jsonata: Selects the number field in the first item",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '"0203 544 1234"',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Phone[0].number"],
},
],
},
{
name: "Jsonata: No index is given to Phone so it selects all of them (the whole array), then it selects all the number fields for each of them",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput:
'["0203 544 1234","01962 001234","01962 001235","077 7700 1234"]',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Phone.number"],
},
],
},
{
name: "Jsonata: Might expect it to just return the first number, but it returns the first number of each of the items selected by Phone",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput:
'["0203 544 1234","01962 001234","01962 001235","077 7700 1234"]',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Phone.number[0]"],
},
],
},
{
name: "Jsonata: Applies the index to the array returned by Phone.number.",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '"0203 544 1234"',
recipeConfig: [
{
op: "Jsonata Query",
args: ["(Phone.number)[0]"],
},
],
},
{
name: "Jsonata: Returns a range of items by creating an array of indexes",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput:
'[{"type":"home","number":"0203 544 1234"},{"type":"office","number":"01962 001234"}]',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Phone[[0..1]]"],
},
],
},
// Predicates
{
name: "Jsonata: Select the Phone items that have a type field that equals 'mobile'",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '{"type":"mobile","number":"077 7700 1234"}',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Phone[type='mobile']"],
},
],
},
{
name: "Jsonata: Select the mobile phone number",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '"077 7700 1234"',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Phone[type='mobile'].number"],
},
],
},
{
name: "Jsonata: Select the office phone numbers - there are two of them",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '["01962 001234","01962 001235"]',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Phone[type='office'].number"],
},
],
},
// Wildcards
{
name: "Jsonata: Select the values of all the fields of 'Address'",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '["Hursley Park","Winchester","SO21 2JN"]',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Address.*"],
},
],
},
{
name: "Jsonata: Select the 'Postcode' value of any child object",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '"SO21 2JN"',
recipeConfig: [
{
op: "Jsonata Query",
args: ["*.Postcode"],
},
],
},
{
name: "Jsonata: Select all Postcode values, regardless of how deeply nested they are in the structure",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '["SO21 2JN","E1 6RF"]',
recipeConfig: [
{
op: "Jsonata Query",
args: ["**.Postcode"],
},
],
},
// String Expressions
{
name: "Jsonata: Concatenate 'FirstName' followed by space followed by 'Surname'",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '"Fred Smith"',
recipeConfig: [
{
op: "Jsonata Query",
args: ["FirstName & ' ' & Surname"],
},
],
},
{
name: "Jsonata: Concatenates the 'Street' and 'City' from the 'Address' object with a comma separator",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '"Hursley Park, Winchester"',
recipeConfig: [
{
op: "Jsonata Query",
args: ["Address.(Street & ', ' & City)"],
},
],
},
{
name: "Jsonata: Casts the operands to strings, if necessary",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: '"50true"',
recipeConfig: [
{
op: "Jsonata Query",
args: ["5&0&true"],
},
],
},
// Numeric Expressions
{
name: "Jsonata: Addition",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "3.4",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Numbers[0] + Numbers[1]"],
},
],
},
{
name: "Jsonata: Subtraction",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "-19.9",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Numbers[0] - Numbers[4]"],
},
],
},
{
name: "Jsonata: Multiplication",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "30",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Numbers[0] * Numbers[5]"],
},
],
},
{
name: "Jsonata: Division",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "0.04784688995215311",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Numbers[0] / Numbers[4]"],
},
],
},
{
name: "Jsonata: Modulus",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "3.5",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Numbers[2] % Numbers[5]"],
},
],
},
{
name: "Jsonata: Equality",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "false",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Numbers[0] = Numbers[5]"],
},
],
},
{
name: "Jsonata: Inequality",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "true",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Numbers[0] != Numbers[4]"],
},
],
},
{
name: "Jsonata: Less than",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "true",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Numbers[0] < Numbers[4]"],
},
],
},
{
name: "Jsonata: Less than or equal to",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "true",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Numbers[0] <= Numbers[4]"],
},
],
},
{
name: "Jsonata: Greater than",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "false",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Numbers[0] > Numbers[4]"],
},
],
},
{
name: "Jsonata: Greater than or equal to",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "false",
recipeConfig: [
{
op: "Jsonata Query",
args: ["Numbers[2] >= Numbers[4]"],
},
],
},
{
name: "Jsonata: Value is contained in",
input: INPUT_JSON_OBJECT_WITH_ARRAYS,
expectedOutput: "true",
recipeConfig: [
{
op: "Jsonata Query",
args: ['"01962 001234" in Phone.number'],
},
],
},
// Boolean Expressions
{
name: "Jsonata: and",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "true",
recipeConfig: [
{
op: "Jsonata Query",
args: ["(Numbers[2] != 0) and (Numbers[5] != Numbers[1])"],
},
],
},
{
name: "Jsonata: or",
input: INPUT_NUMBER_ARRAY,
expectedOutput: "true",
recipeConfig: [
{
op: "Jsonata Query",
args: ["(Numbers[2] != 0) or (Numbers[5] = Numbers[1])"],
},
],
},
// Array tests
{
name: "Jsonata: $ at the start of an expression refers to the entire input document, subscripting it with 0 selects the first item",
input: INPUT_ARRAY_OF_OBJECTS,
expectedOutput: '{"ref":[1,2]}',
recipeConfig: [
{
op: "Jsonata Query",
args: ["$[0]"],
},
],
},
{
name: "Jsonata: .ref here returns the entire internal array",
input: INPUT_ARRAY_OF_OBJECTS,
expectedOutput: "[1,2]",
recipeConfig: [
{
op: "Jsonata Query",
args: ["$[0].ref"],
},
],
},
{
name: "Jsonata: returns element on first position of the internal array",
input: INPUT_ARRAY_OF_OBJECTS,
expectedOutput: "1",
recipeConfig: [
{
op: "Jsonata Query",
args: ["$[0].ref[0]"],
},
],
},
{
name: "Jsonata: $.field_reference flattens the result into a single array",
input: INPUT_ARRAY_OF_OBJECTS,
expectedOutput: "[1,2,3,4]",
recipeConfig: [
{
op: "Jsonata Query",
args: ["$.ref"],
},
],
},
]);

View File

@ -0,0 +1,112 @@
/**
* PHP Serialization tests.
*
* @author brun0ne [brunonblok@gmail.com]
*
* @copyright Crown Copyright 2023
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "PHP Serialize empty array",
input: "[]",
expectedOutput: "a:0:{}",
recipeConfig: [
{
op: "PHP Serialize",
args: []
}
]
},
{
name: "PHP Serialize empty object",
input: "{}",
expectedOutput: "a:0:{}",
recipeConfig: [
{
op: "PHP Serialize",
args: []
}
]
},
{
name: "PHP Serialize null",
input: "null",
expectedOutput: "N;",
recipeConfig: [
{
op: "PHP Serialize",
args: []
}
]
},
{
name: "PHP Serialize integer",
input: "10",
expectedOutput: "i:10;",
recipeConfig: [
{
op: "PHP Serialize",
args: []
}
]
},
{
name: "PHP Serialize float",
input: "14.523",
expectedOutput: "d:14.523;",
recipeConfig: [
{
op: "PHP Serialize",
args: []
}
]
},
{
name: "PHP Serialize boolean",
input: "[true, false]",
expectedOutput: "a:2:{i:0;b:1;i:1;b:0;}",
recipeConfig: [
{
op: "PHP Serialize",
args: []
}
]
},
{
name: "PHP Serialize string",
input: "\"Test string to serialize\"",
expectedOutput: "s:24:\"Test string to serialize\";",
recipeConfig: [
{
op: "PHP Serialize",
args: []
}
]
},
{
name: "PHP Serialize object",
input: "{\"a\": 10,\"0\": {\"ab\": true}}",
expectedOutput: "a:2:{s:1:\"0\";a:1:{s:2:\"ab\";b:1;}s:1:\"a\";i:10;}",
recipeConfig: [
{
op: "PHP Serialize",
args: []
}
]
},
{
name: "PHP Serialize array",
input: "[1,\"abc\",true,{\"x\":1,\"y\":2}]",
expectedOutput: "a:4:{i:0;i:1;i:1;s:3:\"abc\";i:2;b:1;i:3;a:2:{s:1:\"x\";i:1;s:1:\"y\";i:2;}}",
recipeConfig: [
{
op: "PHP Serialize",
args: []
}
]
}
]);

View File

@ -0,0 +1,135 @@
/**
* SM2 Tests
*
* @author flakjacket95 [dflack95@gmail.com]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
/* Plaintexts */
const SMALL_PLAIN = "I am a small plaintext";
const LARGE_PLAIN = "I am a larger plaintext, that will require the encryption KDF to generate a much larger key to properly encrypt me";
/* Test Key Parameters */
const PUBLIC_X = "f7d903cab7925066c31150a92b31e548e63f954f92d01eaa0271fb2a336baef8";
const PUBLIC_Y = "fb0c45e410ef7a6cdae724e6a78dbff52562e97ede009e762b667d9b14adea6c";
const PRIVATE_K = "e74a72505084c3269aa9b696d603e3e08c74c6740212c11a31e26cdfe08bdf6a";
const CURVE = "sm2p256v1";
/* Decryption Test Ciphertext*/
const CIPHERTEXT_1 = "9a31bc0adb4677cdc4141479e3949572a55c3e6fb52094721f741c2bd2e179aaa87be6263bc1be602e473be3d5de5dce97f8248948b3a7e15f9f67f64aef21575e0c05e6171870a10ff9ab778dbef24267ad90e1a9d47d68f757d57c4816612e9829f804025dea05a511cda39371c22a2828f976f72e";
const CIPHERTEXT_2 = "d3647d68568a2e7a4f8e843286be7bf2b4d80256697d19a73df306ae1a7e6d0364d942e23d2340606e7a2502a838b132f9242587b2ea7e4c207e87242eea8cae68f5ff4da2a95a7f6d350608ae5b6777e1d925bf9c560087af84aba7befba713130106ddb4082d803811bca3864594722f3198d58257fe4ba37f4aa540adf4cb0568bddd2d8140ad3030deea0a87e3198655cc4d22bfc3d73b1c4afec2ff15d68c8d1298d97132cace922ee8a4e41ca288a7e748b77ca94aa81dc283439923ae7939e00898e16fe5111fbe1d928d152b216a";
const CIPHERTEXT_3 = "5f340eeb4398fa8950ee3408d0e3fe34bf7728c9fdb060c94b916891b5c693610274160b52a7132a2bf16ad5cdb57d1e00da2f3ddbd55350729aa9c268b53e40c05ccce9912daa14406e8c132e389484e69757350be25351755dcc6c25c94b3c1a448b2cf8c2017582125eb6cf782055b199a875e966";
const CIPHERTEXT_4 = "0649bac46c3f9fd7fb3b2be4bff27414d634651efd02ca67d8c802bbc5468e77d035c39b581d6b56227f5d87c0b4efbea5032c0761139295ae194b9f1fce698f2f4b51d89fa5554171a1aad2e61fe9de89831aec472ecc5ab178ebf4d2230c1fb94fca03e536b87b9eba6db71ba9939260a08ffd230ca86cb45cf754854222364231bdb8b873791d63ad57a4b3fa5b6375388dc879373f5f1be9051bc5072a8afbec5b7b034e4907aa5bb4b6b1f50e725d09cb6a02e07ce20263005f6c9157ce05d3ea739d231d4f09396fb72aa680884d78";
TestRegister.addTests([
{
name: "SM2 Decrypt: Small Input; Format One",
input: CIPHERTEXT_1,
expectedOutput: SMALL_PLAIN,
recipeConfig: [
{
"op": "SM2 Decrypt",
"args": [PRIVATE_K, "C1C3C2", CURVE]
}
]
},
{
name: "SM2 Decrypt: Large Input; Format One",
input: CIPHERTEXT_2,
expectedOutput: LARGE_PLAIN,
recipeConfig: [
{
"op": "SM2 Decrypt",
"args": [PRIVATE_K, "C1C3C2", CURVE]
}
]
},
{
name: "SM2 Decrypt: Small Input; Format Two",
input: CIPHERTEXT_3,
expectedOutput: SMALL_PLAIN,
recipeConfig: [
{
"op": "SM2 Decrypt",
"args": [PRIVATE_K, "C1C2C3", CURVE]
}
]
},
{
name: "SM2 Decrypt: Large Input; Format Two",
input: CIPHERTEXT_4,
expectedOutput: LARGE_PLAIN,
recipeConfig: [
{
"op": "SM2 Decrypt",
"args": [PRIVATE_K, "C1C2C3", CURVE]
}
]
},
{
name: "SM2 Encrypt And Decrypt: Small Input; Format One",
input: SMALL_PLAIN,
expectedOutput: SMALL_PLAIN,
recipeConfig: [
{
"op": "SM2 Encrypt",
"args": [PUBLIC_X, PUBLIC_Y, "C1C3C2", CURVE],
},
{
"op": "SM2 Decrypt",
"args": [PRIVATE_K, "C1C3C2", CURVE]
}
]
},
{
name: "SM2 Encrypt And Decrypt: Large Input; Format One",
input: LARGE_PLAIN,
expectedOutput: LARGE_PLAIN,
recipeConfig: [
{
"op": "SM2 Encrypt",
"args": [PUBLIC_X, PUBLIC_Y, "C1C3C2", CURVE],
},
{
"op": "SM2 Decrypt",
"args": [PRIVATE_K, "C1C3C2", CURVE]
}
]
},
{
name: "SM2 Encrypt And Decrypt: Small Input; Format Two",
input: SMALL_PLAIN,
expectedOutput: SMALL_PLAIN,
recipeConfig: [
{
"op": "SM2 Encrypt",
"args": [PUBLIC_X, PUBLIC_Y, "C1C2C3", CURVE],
},
{
"op": "SM2 Decrypt",
"args": [PRIVATE_K, "C1C2C2", CURVE]
}
]
},
{
name: "SM2 Encrypt And Decrypt: Large Input; Format Two",
input: LARGE_PLAIN,
expectedOutput: LARGE_PLAIN,
recipeConfig: [
{
"op": "SM2 Encrypt",
"args": [PUBLIC_X, PUBLIC_Y, "C1C2C3", CURVE],
},
{
"op": "SM2 Decrypt",
"args": [PRIVATE_K, "C1C2C3", CURVE]
}
]
},
]);

View File

@ -0,0 +1,53 @@
/**
* @author kendallgoto [k@kgo.to]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
"name": "Template: Simple Print",
"input": "{}",
"expectedOutput": "Hello, world!",
"recipeConfig": [
{
"op": "Template",
"args": ["Hello, world!"]
}
]
},
{
"name": "Template: Print Basic Variables",
"input": "{\"one\": 1, \"two\": 2}",
"expectedOutput": "1 2",
"recipeConfig": [
{
"op": "Template",
"args": ["{{ one }} {{ two }}"]
}
]
},
{
"name": "Template: Partials",
"input": "{\"users\":[{\"name\":\"Someone\",\"age\":25},{\"name\":\"Someone Else\",\"age\":32}]}",
"expectedOutput": "Name: Someone\nAge: 25\n\nName: Someone Else\nAge: 32\n\n",
"recipeConfig": [
{
"op": "Template",
"args": ["{{#*inline \"user\"}}\nName: {{ name }}\nAge: {{ age }}\n{{/inline}}\n{{#each users}}\n{{> user}}\n\n{{/each}}"]
}
]
},
{
"name": "Template: Disallow XSS",
"input": "{\"test\": \"<script></script>\"}",
"expectedOutput": "<script></script>&lt;script&gt;&lt;/script&gt;",
"recipeConfig": [
{
"op": "Template",
"args": ["<script></script>{{ test }}"]
}
]
}
]);

View File

@ -0,0 +1,92 @@
/**
* URLEncode and URLDecode tests.
*
* @author es45411 [135977478+es45411@users.noreply.github.com]
*
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
// URL Decode
{
name: "URLDecode: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "URL Decode",
args: [],
},
],
},
{
name: "URLDecode: spaces without special chars",
input: "Hello%20world%21",
expectedOutput: "Hello world!",
recipeConfig: [
{
op: "URL Decode",
args: [],
},
],
},
{
name: "URLDecode: spaces with special chars",
input: "Hello%20world!",
expectedOutput: "Hello world!",
recipeConfig: [
{
op: "URL Decode",
args: [],
},
],
},
{
name: "URLDecode: decode plus as space",
input: "Hello%20world!",
expectedOutput: "Hello world!",
recipeConfig: [
{
op: "URL Decode",
args: [],
},
],
},
// URL Encode
{
name: "URLEncode: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "URL Encode",
args: [],
},
],
},
{
name: "URLEncode: spaces without special chars",
input: "Hello world!",
expectedOutput: "Hello%20world!",
recipeConfig: [
{
op: "URL Encode",
args: [],
},
],
},
{
name: "URLEncode: spaces with special chars",
input: "Hello world!",
expectedOutput: "Hello%20world%21",
recipeConfig: [
{
op: "URL Encode",
args: [true],
},
],
},
]);

View File

@ -0,0 +1,120 @@
/**
* Checksum tests.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
const BASIC_STRING = "The ships hung in the sky in much the same way that bricks don't.";
const UTF8_STR = "ნუ პანიკას";
const ALL_BYTES = [
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
].join("");
TestRegister.addTests([
{
name: "XOR Checksum (1): nothing",
input: "",
expectedOutput: "00",
recipeConfig: [
{
"op": "XOR Checksum",
"args": [1]
}
]
},
{
name: "XOR Checksum (1): basic string",
input: BASIC_STRING,
expectedOutput: "08",
recipeConfig: [
{
"op": "XOR Checksum",
"args": [1]
}
]
},
{
name: "XOR Checksum (1): UTF-8",
input: UTF8_STR,
expectedOutput: "df",
recipeConfig: [
{
"op": "XOR Checksum",
"args": [1]
}
]
},
{
name: "XOR Checksum (1): all bytes",
input: ALL_BYTES,
expectedOutput: "00",
recipeConfig: [
{
"op": "XOR Checksum",
"args": [1]
}
]
},
{
name: "XOR Checksum (4): nothing",
input: "",
expectedOutput: "00000000",
recipeConfig: [
{
"op": "XOR Checksum",
"args": [4]
}
]
},
{
name: "XOR Checksum (4): basic string",
input: BASIC_STRING,
expectedOutput: "4918421b",
recipeConfig: [
{
"op": "XOR Checksum",
"args": [4]
}
]
},
{
name: "XOR Checksum (4): UTF-8",
input: UTF8_STR,
expectedOutput: "83a424dc",
recipeConfig: [
{
"op": "XOR Checksum",
"args": [4]
}
]
},
{
name: "XOR Checksum (4): all bytes",
input: ALL_BYTES,
expectedOutput: "00000000",
recipeConfig: [
{
"op": "XOR Checksum",
"args": [4]
}
]
},
]);