mirror of
https://github.com/gchq/CyberChef
synced 2026-02-15 16:03:51 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b2868a178 | ||
|
|
7ba58cd4ce | ||
|
|
bb41c36578 | ||
|
|
293f304841 | ||
|
|
324a23585e | ||
|
|
fe69ec5881 | ||
|
|
595c90a464 | ||
|
|
042afe4157 | ||
|
|
cc2c6d20fd | ||
|
|
abbb8496cc | ||
|
|
4ca5157508 | ||
|
|
eeb39a0b2b | ||
|
|
0cf7bcaddc | ||
|
|
4e8f0c34f3 | ||
|
|
de3a5ff634 | ||
|
|
693b7d86dd | ||
|
|
fa34e2fafc | ||
|
|
1542cadde8 | ||
|
|
64399ad60e | ||
|
|
96c93b95f2 | ||
|
|
9df82113c4 | ||
|
|
55ef47f645 | ||
|
|
5e53fe113d | ||
|
|
5c49f87727 | ||
|
|
e0c4957da4 | ||
|
|
9512444eee | ||
|
|
dd26c09003 | ||
|
|
a30f5f1b50 | ||
|
|
b885e8423d |
@@ -12,3 +12,7 @@ indent_size = 4
|
|||||||
[{package.json,.travis.yml,nightwatch.json}]
|
[{package.json,.travis.yml,nightwatch.json}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
[.github/**.yml]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|||||||
40
.github/workflows/codeql.yml
vendored
40
.github/workflows/codeql.yml
vendored
@@ -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}}"
|
|
||||||
83
.github/workflows/master.yml
vendored
83
.github/workflows/master.yml
vendored
@@ -4,55 +4,62 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
main:
|
main:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pages: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set node version
|
- name: Set node version
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '18.x'
|
node-version: 18
|
||||||
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: |
|
run: |
|
||||||
export DETECT_CHROMEDRIVER_VERSION=true
|
export DETECT_CHROMEDRIVER_VERSION=true
|
||||||
npm install
|
npm install
|
||||||
npm run setheapsize
|
npm run setheapsize
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: npx grunt lint
|
run: npx grunt lint
|
||||||
|
|
||||||
- name: Unit Tests
|
- name: Unit Tests
|
||||||
run: |
|
run: |
|
||||||
npm test
|
npm test
|
||||||
npm run testnodeconsumer
|
npm run testnodeconsumer
|
||||||
|
|
||||||
- name: Production Build
|
- name: Production Build
|
||||||
if: success()
|
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>"
|
run: npx grunt prod --msg=""
|
||||||
|
|
||||||
- name: Generate sitemap
|
- name: Generate sitemap
|
||||||
run: npx grunt exec:sitemap
|
run: npx grunt exec:sitemap
|
||||||
|
|
||||||
- name: UI Tests
|
- name: UI Tests
|
||||||
if: success()
|
if: success()
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install xvfb
|
sudo apt-get install xvfb
|
||||||
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
|
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
|
||||||
|
|
||||||
- name: Prepare for GitHub Pages
|
- name: Prepare for GitHub Pages
|
||||||
if: success()
|
if: success()
|
||||||
run: npx grunt copy:ghPages
|
run: npx grunt copy:ghPages
|
||||||
|
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
if: success() && github.ref == 'refs/heads/master'
|
if: success() && github.ref == 'refs/heads/master'
|
||||||
uses: crazy-max/ghaction-github-pages@v3
|
uses: crazy-max/ghaction-github-pages@v3
|
||||||
with:
|
with:
|
||||||
target_branch: gh-pages
|
target_branch: gh-pages
|
||||||
build_dir: ./build/prod
|
build_dir: ./build/prod
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
76
.github/workflows/pull_requests.yml
vendored
76
.github/workflows/pull_requests.yml
vendored
@@ -1,5 +1,8 @@
|
|||||||
name: "Pull Requests"
|
name: "Pull Requests"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -9,47 +12,46 @@ jobs:
|
|||||||
main:
|
main:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set node version
|
- name: Set node version
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '18.x'
|
node-version: 18
|
||||||
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: |
|
run: |
|
||||||
export DETECT_CHROMEDRIVER_VERSION=true
|
export DETECT_CHROMEDRIVER_VERSION=true
|
||||||
npm install
|
npm install
|
||||||
npm run setheapsize
|
npm run setheapsize
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: npx grunt lint
|
run: npx grunt lint
|
||||||
|
|
||||||
- name: Unit Tests
|
- name: Unit Tests
|
||||||
run: |
|
run: |
|
||||||
npm test
|
npm test
|
||||||
npm run testnodeconsumer
|
npm run testnodeconsumer
|
||||||
|
|
||||||
- name: Production Build
|
- name: Production Build
|
||||||
if: success()
|
if: success()
|
||||||
run: npx grunt prod
|
run: npx grunt prod
|
||||||
|
|
||||||
- name: Production Image Build
|
- name: Set up Docker Buildx
|
||||||
if: success()
|
uses: docker/setup-buildx-action@v3
|
||||||
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: UI Tests
|
- name: Set up QEMU
|
||||||
if: success()
|
uses: docker/setup-qemu-action@v3
|
||||||
run: |
|
|
||||||
sudo apt-get install xvfb
|
- name: Production Image Build
|
||||||
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
|
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
|
||||||
|
|||||||
166
.github/workflows/releases.yml
vendored
166
.github/workflows/releases.yml
vendored
@@ -4,7 +4,10 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- "v*"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
@@ -14,83 +17,110 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
main:
|
main:
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set node version
|
- name: Set node version
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '18.x'
|
node-version: 18
|
||||||
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: |
|
run: |
|
||||||
export DETECT_CHROMEDRIVER_VERSION=true
|
export DETECT_CHROMEDRIVER_VERSION=true
|
||||||
npm ci
|
npm ci
|
||||||
npm run setheapsize
|
npm run setheapsize
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: npx grunt lint
|
run: npx grunt lint
|
||||||
|
|
||||||
- name: Unit Tests
|
- name: Unit Tests
|
||||||
run: |
|
run: |
|
||||||
npm test
|
npm test
|
||||||
npm run testnodeconsumer
|
npm run testnodeconsumer
|
||||||
|
|
||||||
- name: Production Build
|
- name: Production Build
|
||||||
run: npx grunt prod
|
run: npx grunt prod
|
||||||
|
|
||||||
- name: UI Tests
|
- name: UI Tests
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install xvfb
|
sudo apt-get install xvfb
|
||||||
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
|
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
|
||||||
|
|
||||||
- name: Image Metadata
|
- name: Set up Docker Buildx
|
||||||
id: image-metadata
|
uses: docker/setup-buildx-action@v3
|
||||||
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: Production Image Build
|
- name: Set up QEMU
|
||||||
id: build-image
|
uses: docker/setup-qemu-action@v3
|
||||||
uses: redhat-actions/buildah-build@v2
|
|
||||||
with:
|
|
||||||
tags: ${{ steps.image-metadata.outputs.tags }}
|
|
||||||
labels: ${{ steps.image-metadata.outputs.labels }}
|
|
||||||
containerfiles: ./Dockerfile
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
oci: true
|
|
||||||
# enable build layer caching between platforms
|
|
||||||
layers: true
|
|
||||||
# Webpack seems to use a lot of open files, increase the max open file limit to accomodate.
|
|
||||||
extra-args: |
|
|
||||||
--ulimit nofile=10000
|
|
||||||
|
|
||||||
- name: Publish to GHCR
|
- name: Image Metadata
|
||||||
uses: redhat-actions/push-to-registry@v2
|
id: image-metadata
|
||||||
with:
|
uses: docker/metadata-action@v4
|
||||||
image: ${{ steps.build-image.outputs.image }}
|
with:
|
||||||
tags: ${{ steps.build-image.outputs.tags }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
registry: ${{ env.REGISTRY }}
|
tags: |
|
||||||
username: ${{ env.REGISTRY_USER }}
|
type=semver,pattern={{major}}
|
||||||
password: ${{ env.REGISTRY_PASSWORD }}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
|
||||||
- name: Upload Release Assets
|
- name: Log in to GHCR
|
||||||
id: upload-release-assets
|
uses: docker/login-action@v3
|
||||||
uses: svenstaro/upload-release-action@v2
|
with:
|
||||||
with:
|
registry: ${{ env.REGISTRY }}
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
username: ${{ env.REGISTRY_USER }}
|
||||||
file: build/prod/*.zip
|
password: ${{ env.REGISTRY_PASSWORD }}
|
||||||
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
|
- name: Publish to GHCR
|
||||||
uses: JS-DevTools/npm-publish@v1
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.NPM_TOKEN }}
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.image-metadata.outputs.tags }}
|
||||||
|
labels: ${{ steps.image-metadata.outputs.labels }}
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|
||||||
|
- 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."
|
||||||
|
|
||||||
|
npm-publish:
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
|
needs: main
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Set node version
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
|
- name: Install
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Create machine generated files
|
||||||
|
run: npm run node
|
||||||
|
|
||||||
|
- name: Reset node version ready for publish
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: ^24.5
|
||||||
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
|
- name: Publish to NPM
|
||||||
|
run: npm publish
|
||||||
|
|||||||
69
CHANGELOG.md
69
CHANGELOG.md
@@ -13,6 +13,35 @@ All major and minor version changes will be documented in this file. Details of
|
|||||||
|
|
||||||
## Details
|
## Details
|
||||||
|
|
||||||
|
### [10.22.0] - 2026-02-11
|
||||||
|
- Separate npm publish out into separate job and run with Node 24.5 [@GCHQDeveloper581] | [#2188]
|
||||||
|
- Fixed Percent delimiter for hex encoding [@beneri] [@C85297] | [#2137]
|
||||||
|
- Added the ability to paste one or more Images from the Clipboard [@t-martine] [@a3957273] [@C85297] | [#1876]
|
||||||
|
- Quoted Printable - consistent reference to 'email' [@wesinator] | [#2186]
|
||||||
|
- Fix freeze when output text decoding fails [@Raka-loah] | [#1573]
|
||||||
|
- Update Browserslist DB [@C85297] | [#2183]
|
||||||
|
- Add contents write permission to releases workflow [@C85297] | [#2182]
|
||||||
|
- Fix release workflow permissions [@C85297] | [#2181]
|
||||||
|
|
||||||
|
### [10.21.0] - 2026-02-05
|
||||||
|
- Fix import operations with special chars in them [@d98762625] [@jg42526] | [#1040]
|
||||||
|
- Remove custom CodeQL workflow [@C85297] | [#2176]
|
||||||
|
- Fix code scanning warnings in workflows [@GCHQDeveloper581] | [#2177]
|
||||||
|
- Use NPM trusted publishing [@C85297] [@GCHQDeveloper581] | [#2174]
|
||||||
|
- Fix: Correctly parse xxd odd byte hexdumps [@ThomasNotTom] [@GCHQDeveloper581] | [#2058]
|
||||||
|
- Update Sitemap URLs to Use Valid Paths in sitemap.mjs [@rbpi] [@C85297] | [#1861]
|
||||||
|
- Use recommended GitHub Actions to build image [@AlexGustafsson] [@C85297] | [#2055]
|
||||||
|
- Remove version 10 message from banner [@C85297] | [#2169]
|
||||||
|
- Bump form-data from 4.0.1 to 4.0.5 | [#2175]
|
||||||
|
- Bump node-forge from 1.3.1 to 1.3.3 | [#2173]
|
||||||
|
- Update crypto browserify [@C85297] | [#2172]
|
||||||
|
- Update kbpgp package (resolves #2135) [@GCHQDeveloper581] | [#2136]
|
||||||
|
- Fix the processing of ALPNs for JA4 to align with new specification update [@tuliperis] | [#2165]
|
||||||
|
- Add Bech32 and Bech32m encoding/decoding operations [@thomasxm] | [#2159]
|
||||||
|
- Exclude Delete character from hex dump output [@mikecat] [@C85297] | [#2086]
|
||||||
|
- Tiny typo fix in "To Base85" operation [@twostraws] | [#2118]
|
||||||
|
- Bump jsonpath-plus [@C85297] | [#2166]
|
||||||
|
|
||||||
### [10.20.0] - 2026-01-28
|
### [10.20.0] - 2026-01-28
|
||||||
- Fixed Optical Character Recognition and added tests [@n1474335] | [ab37c1e]
|
- Fixed Optical Character Recognition and added tests [@n1474335] | [ab37c1e]
|
||||||
- Fixed JA4 version fallback value [@n1474335] | [7a5225c]
|
- Fixed JA4 version fallback value [@n1474335] | [7a5225c]
|
||||||
@@ -509,6 +538,8 @@ All major and minor version changes will be documented in this file. Details of
|
|||||||
## [4.0.0] - 2016-11-28
|
## [4.0.0] - 2016-11-28
|
||||||
- Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306)
|
- Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306)
|
||||||
|
|
||||||
|
[10.22.0]: https://github.com/gchq/CyberChef/releases/tag/v10.22.0
|
||||||
|
[10.21.0]: https://github.com/gchq/CyberChef/releases/tag/v10.21.0
|
||||||
[10.20.0]: https://github.com/gchq/CyberChef/releases/tag/v10.20.0
|
[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.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.18.0]: https://github.com/gchq/CyberChef/releases/tag/v10.18.0
|
||||||
@@ -754,6 +785,18 @@ All major and minor version changes will be documented in this file. Details of
|
|||||||
[@remingtr]: https://github.com/remingtr
|
[@remingtr]: https://github.com/remingtr
|
||||||
[@0xff1ce]: https://github.com/0xff1ce
|
[@0xff1ce]: https://github.com/0xff1ce
|
||||||
[@starplanet]: https://github.com/starplanet
|
[@starplanet]: https://github.com/starplanet
|
||||||
|
[@C85297]: https://github.com/C85297
|
||||||
|
[@GCHQDeveloper581]: https://github.com/GCHQDeveloper581
|
||||||
|
[@ThomasNotTom]: https://github.com/ThomasNotTom
|
||||||
|
[@rbpi]: https://github.com/rbpi
|
||||||
|
[@AlexGustafsson]: https://github.com/AlexGustafsson
|
||||||
|
[@tuliperis]: https://github.com/tuliperis
|
||||||
|
[@thomasxm]: https://github.com/thomasxm
|
||||||
|
[@twostraws]: https://github.com/twostraws
|
||||||
|
[@beneri]: https://github.com/beneri
|
||||||
|
[@t-martine]: https://github.com/t-martine
|
||||||
|
[@wesinator]: https://github.com/wesinator
|
||||||
|
[@Raka-loah]: https://github.com/Raka-loah
|
||||||
|
|
||||||
|
|
||||||
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7
|
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7
|
||||||
@@ -942,3 +985,29 @@ All major and minor version changes will be documented in this file. Details of
|
|||||||
[#512]: https://github.com/gchq/CyberChef/issues/512
|
[#512]: https://github.com/gchq/CyberChef/issues/512
|
||||||
[#1732]: https://github.com/gchq/CyberChef/issues/1732
|
[#1732]: https://github.com/gchq/CyberChef/issues/1732
|
||||||
[#1789]: https://github.com/gchq/CyberChef/issues/1789
|
[#1789]: https://github.com/gchq/CyberChef/issues/1789
|
||||||
|
[#1040]: https://github.com/gchq/CyberChef/pull/1040
|
||||||
|
[#2176]: https://github.com/gchq/CyberChef/pull/2176
|
||||||
|
[#2177]: https://github.com/gchq/CyberChef/pull/2177
|
||||||
|
[#2174]: https://github.com/gchq/CyberChef/pull/2174
|
||||||
|
[#2058]: https://github.com/gchq/CyberChef/pull/2058
|
||||||
|
[#1861]: https://github.com/gchq/CyberChef/pull/1861
|
||||||
|
[#2055]: https://github.com/gchq/CyberChef/pull/2055
|
||||||
|
[#2169]: https://github.com/gchq/CyberChef/pull/2169
|
||||||
|
[#2175]: https://github.com/gchq/CyberChef/pull/2175
|
||||||
|
[#2173]: https://github.com/gchq/CyberChef/pull/2173
|
||||||
|
[#2172]: https://github.com/gchq/CyberChef/pull/2172
|
||||||
|
[#2136]: https://github.com/gchq/CyberChef/pull/2136
|
||||||
|
[#2165]: https://github.com/gchq/CyberChef/pull/2165
|
||||||
|
[#2159]: https://github.com/gchq/CyberChef/pull/2159
|
||||||
|
[#2086]: https://github.com/gchq/CyberChef/pull/2086
|
||||||
|
[#2118]: https://github.com/gchq/CyberChef/pull/2118
|
||||||
|
[#2166]: https://github.com/gchq/CyberChef/pull/2166
|
||||||
|
[#2188]: https://github.com/gchq/CyberChef/pull/2188
|
||||||
|
[#2137]: https://github.com/gchq/CyberChef/pull/2137
|
||||||
|
[#1876]: https://github.com/gchq/CyberChef/pull/1876
|
||||||
|
[#2186]: https://github.com/gchq/CyberChef/pull/2186
|
||||||
|
[#1573]: https://github.com/gchq/CyberChef/pull/1573
|
||||||
|
[#2183]: https://github.com/gchq/CyberChef/pull/2183
|
||||||
|
[#2182]: https://github.com/gchq/CyberChef/pull/2182
|
||||||
|
[#2181]: https://github.com/gchq/CyberChef/pull/2181
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,6 @@ RUN npm run build
|
|||||||
#########################################
|
#########################################
|
||||||
# Package static build files into nginx #
|
# Package static build files into nginx #
|
||||||
#########################################
|
#########################################
|
||||||
# We are using Github Actions: redhat-actions/buildah-build@v2 which needs manual selection of arch in base image
|
FROM nginx:stable-alpine AS cyberchef
|
||||||
# Remove TARGETARCH if docker buildx is supported in the CI release as --platform=$TARGETPLATFORM will be automatically set
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
FROM --platform=${TARGETPLATFORM} nginx:stable-alpine AS cyberchef
|
|
||||||
|
|
||||||
COPY --from=builder /app/build/prod /usr/share/nginx/html/
|
COPY --from=builder /app/build/prod /usr/share/nginx/html/
|
||||||
|
|||||||
184
package-lock.json
generated
184
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cyberchef",
|
"name": "cyberchef",
|
||||||
"version": "10.20.0",
|
"version": "10.22.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cyberchef",
|
"name": "cyberchef",
|
||||||
"version": "10.20.0",
|
"version": "10.22.1",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"chi-squared": "^1.1.0",
|
"chi-squared": "^1.1.0",
|
||||||
"codepage": "^1.15.0",
|
"codepage": "^1.15.0",
|
||||||
"crypto-api": "^0.8.5",
|
"crypto-api": "^0.8.5",
|
||||||
"crypto-browserify": "^3.12.0",
|
"crypto-browserify": "^3.12.1",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"ctph.js": "0.0.5",
|
"ctph.js": "0.0.5",
|
||||||
"d3": "7.9.0",
|
"d3": "7.9.0",
|
||||||
@@ -57,11 +57,11 @@
|
|||||||
"jsesc": "^3.0.2",
|
"jsesc": "^3.0.2",
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"jsonata": "^2.0.3",
|
"jsonata": "^2.0.3",
|
||||||
"jsonpath-plus": "^9.0.0",
|
"jsonpath-plus": "^10.3.0",
|
||||||
"jsonwebtoken": "8.5.1",
|
"jsonwebtoken": "8.5.1",
|
||||||
"jsqr": "^1.4.0",
|
"jsqr": "^1.4.0",
|
||||||
"jsrsasign": "^11.1.0",
|
"jsrsasign": "^11.1.0",
|
||||||
"kbpgp": "2.1.15",
|
"kbpgp": "^2.1.17",
|
||||||
"libbzip2-wasm": "0.0.4",
|
"libbzip2-wasm": "0.0.4",
|
||||||
"libyara-wasm": "^1.2.1",
|
"libyara-wasm": "^1.2.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
@@ -5137,7 +5137,6 @@
|
|||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||||
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
|
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"possible-typed-array-names": "^1.0.0"
|
"possible-typed-array-names": "^1.0.0"
|
||||||
@@ -6224,9 +6223,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001695",
|
"version": "1.0.30001769",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz",
|
||||||
"integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==",
|
"integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -8741,6 +8740,22 @@
|
|||||||
"node": ">= 0.4"
|
"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": {
|
"node_modules/es6-object-assign": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
|
||||||
@@ -9844,7 +9859,6 @@
|
|||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||||
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
|
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-callable": "^1.1.3"
|
"is-callable": "^1.1.3"
|
||||||
@@ -9904,14 +9918,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/form-data": {
|
"node_modules/form-data": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||||
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
|
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.8",
|
"combined-stream": "^1.0.8",
|
||||||
|
"es-set-tostringtag": "^2.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -11706,7 +11722,6 @@
|
|||||||
"version": "1.2.7",
|
"version": "1.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
||||||
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -12057,6 +12072,21 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/is-unc-path": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
|
||||||
@@ -12503,21 +12533,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsonpath-plus": {
|
"node_modules/jsonpath-plus": {
|
||||||
"version": "9.0.0",
|
"version": "10.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-9.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz",
|
||||||
"integrity": "sha512-bqE77VIDStrOTV/czspZhTn+o27Xx9ZJRGVkdVShEtPoqsIx5yALv3lWVU6y+PqYvWPJNWE7ORCQheQkEe0DDA==",
|
"integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsep-plugin/assignment": "^1.2.1",
|
"@jsep-plugin/assignment": "^1.3.0",
|
||||||
"@jsep-plugin/regex": "^1.0.3",
|
"@jsep-plugin/regex": "^1.0.4",
|
||||||
"jsep": "^1.3.8"
|
"jsep": "^1.4.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"jsonpath": "bin/jsonpath-cli.js",
|
"jsonpath": "bin/jsonpath-cli.js",
|
||||||
"jsonpath-plus": "bin/jsonpath-cli.js"
|
"jsonpath-plus": "bin/jsonpath-cli.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsonwebtoken": {
|
"node_modules/jsonwebtoken": {
|
||||||
@@ -12601,9 +12631,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/kbpgp": {
|
"node_modules/kbpgp": {
|
||||||
"version": "2.1.15",
|
"version": "2.1.17",
|
||||||
"resolved": "https://registry.npmjs.org/kbpgp/-/kbpgp-2.1.15.tgz",
|
"resolved": "https://registry.npmjs.org/kbpgp/-/kbpgp-2.1.17.tgz",
|
||||||
"integrity": "sha512-iFdQT+m2Mi2DB14kEFydF2joNe9x3E2VZCGZUt7UXsiZnQx5TtSl4KofP7EPtjHvf7weCxNKlEPSYiiCNMZ2jA==",
|
"integrity": "sha512-pnjH7amyg6dZLXyF42BKbCTST0l0r1ErunqtFRrJCkHkGJb83cZZmx1pnqNFr+d/ls+5gvcHrZLPfUG5q7oRYw==",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bn": "^1.0.5",
|
"bn": "^1.0.5",
|
||||||
@@ -13983,9 +14013,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-forge": {
|
"node_modules/node-forge": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz",
|
||||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
|
"integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==",
|
||||||
"license": "(BSD-3-Clause OR GPL-2.0)",
|
"license": "(BSD-3-Clause OR GPL-2.0)",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6.13.0"
|
"node": ">= 6.13.0"
|
||||||
@@ -14900,19 +14930,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pbkdf2": {
|
"node_modules/pbkdf2": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz",
|
||||||
"integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
|
"integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"create-hash": "^1.1.2",
|
"create-hash": "^1.2.0",
|
||||||
"create-hmac": "^1.1.4",
|
"create-hmac": "^1.1.7",
|
||||||
"ripemd160": "^2.0.1",
|
"ripemd160": "^2.0.3",
|
||||||
"safe-buffer": "^5.0.1",
|
"safe-buffer": "^5.2.1",
|
||||||
"sha.js": "^2.4.8"
|
"sha.js": "^2.4.12",
|
||||||
|
"to-buffer": "^1.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/peek-readable": {
|
"node_modules/peek-readable": {
|
||||||
@@ -15170,7 +15201,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
|
||||||
"integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
|
"integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -16149,13 +16179,31 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ripemd160": {
|
"node_modules/ripemd160": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz",
|
||||||
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
|
"integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hash-base": "^3.0.0",
|
"hash-base": "^3.1.2",
|
||||||
"inherits": "^2.0.1"
|
"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": {
|
"node_modules/rison": {
|
||||||
@@ -16617,16 +16665,23 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/sha.js": {
|
"node_modules/sha.js": {
|
||||||
"version": "2.4.11",
|
"version": "2.4.12",
|
||||||
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz",
|
||||||
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
|
"integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==",
|
||||||
"license": "(MIT AND BSD-3-Clause)",
|
"license": "(MIT AND BSD-3-Clause)",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"inherits": "^2.0.1",
|
"inherits": "^2.0.4",
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.2.1",
|
||||||
|
"to-buffer": "^1.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"sha.js": "bin.js"
|
"sha.js": "bin.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
@@ -17663,6 +17718,26 @@
|
|||||||
"node": ">=14.14"
|
"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": {
|
"node_modules/to-fast-properties": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
|
||||||
@@ -17845,6 +17920,20 @@
|
|||||||
"node": ">= 0.6"
|
"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": {
|
"node_modules/ua-parser-js": {
|
||||||
"version": "1.0.40",
|
"version": "1.0.40",
|
||||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz",
|
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz",
|
||||||
@@ -18855,7 +18944,6 @@
|
|||||||
"version": "1.1.18",
|
"version": "1.1.18",
|
||||||
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz",
|
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz",
|
||||||
"integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==",
|
"integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"available-typed-arrays": "^1.0.7",
|
"available-typed-arrays": "^1.0.7",
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cyberchef",
|
"name": "cyberchef",
|
||||||
"version": "10.20.0",
|
"version": "10.22.1",
|
||||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||||
"author": "n1474335 <n1474335@gmail.com>",
|
"author": "n1474335 <n1474335@gmail.com>",
|
||||||
"homepage": "https://gchq.github.io/CyberChef",
|
"homepage": "https://gchq.github.io/CyberChef",
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
"chi-squared": "^1.1.0",
|
"chi-squared": "^1.1.0",
|
||||||
"codepage": "^1.15.0",
|
"codepage": "^1.15.0",
|
||||||
"crypto-api": "^0.8.5",
|
"crypto-api": "^0.8.5",
|
||||||
"crypto-browserify": "^3.12.0",
|
"crypto-browserify": "^3.12.1",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"ctph.js": "0.0.5",
|
"ctph.js": "0.0.5",
|
||||||
"d3": "7.9.0",
|
"d3": "7.9.0",
|
||||||
@@ -143,11 +143,11 @@
|
|||||||
"jsesc": "^3.0.2",
|
"jsesc": "^3.0.2",
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"jsonata": "^2.0.3",
|
"jsonata": "^2.0.3",
|
||||||
"jsonpath-plus": "^9.0.0",
|
"jsonpath-plus": "^10.3.0",
|
||||||
"jsonwebtoken": "8.5.1",
|
"jsonwebtoken": "8.5.1",
|
||||||
"jsqr": "^1.4.0",
|
"jsqr": "^1.4.0",
|
||||||
"jsrsasign": "^11.1.0",
|
"jsrsasign": "^11.1.0",
|
||||||
"kbpgp": "2.1.15",
|
"kbpgp": "^2.1.17",
|
||||||
"libbzip2-wasm": "0.0.4",
|
"libbzip2-wasm": "0.0.4",
|
||||||
"libyara-wasm": "^1.2.1",
|
"libyara-wasm": "^1.2.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
@@ -202,7 +202,8 @@
|
|||||||
"lint:grammar": "cspell ./src",
|
"lint:grammar": "cspell ./src",
|
||||||
"postinstall": "npx grunt exec:fixCryptoApiImports && npx grunt exec:fixSnackbarMarkup && npx grunt exec:fixJimpModule",
|
"postinstall": "npx grunt exec:fixCryptoApiImports && npx grunt exec:fixSnackbarMarkup && npx grunt exec:fixJimpModule",
|
||||||
"newop": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newOperation.mjs",
|
"newop": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newOperation.mjs",
|
||||||
"minor": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newMinorVersion.mjs",
|
"minor": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newMinorVersion.mjs && npm version minor --git-tag-version=false && echo \"Updated to version v$(npm pkg get version | xargs), please create a pull request and once merged use 'npm run tag'\"",
|
||||||
|
"tag": "git tag -s \"v$(npm pkg get version | xargs)\" -m \"$(npm pkg get version | xargs)\" && echo \"Created v$(npm pkg get version | xargs), now check and push the tag\"",
|
||||||
"getheapsize": "node -e 'console.log(`node heap limit = ${require(\"v8\").getHeapStatistics().heap_size_limit / (1024 * 1024)} Mb`)'",
|
"getheapsize": "node -e 'console.log(`node heap limit = ${require(\"v8\").getHeapStatistics().heap_size_limit / (1024 * 1024)} Mb`)'",
|
||||||
"setheapsize": "export NODE_OPTIONS=--max_old_space_size=2048"
|
"setheapsize": "export NODE_OPTIONS=--max_old_space_size=2048"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ class Utils {
|
|||||||
*/
|
*/
|
||||||
static printable(str, preserveWs=false, onlyAscii=false) {
|
static printable(str, preserveWs=false, onlyAscii=false) {
|
||||||
if (onlyAscii) {
|
if (onlyAscii) {
|
||||||
return str.replace(/[^\x20-\x7f]/g, ".");
|
return str.replace(/[^\x20-\x7e]/g, ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-misleading-character-class
|
// eslint-disable-next-line no-misleading-character-class
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
"From Base45",
|
"From Base45",
|
||||||
"To Base58",
|
"To Base58",
|
||||||
"From Base58",
|
"From Base58",
|
||||||
|
"To Bech32",
|
||||||
|
"From Bech32",
|
||||||
"To Base62",
|
"To Base62",
|
||||||
"From Base62",
|
"From Base62",
|
||||||
"To Base64",
|
"To Base64",
|
||||||
|
|||||||
@@ -7,138 +7,197 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint no-console: ["off"] */
|
/* eslint no-console: ["off"] */
|
||||||
|
/* eslint jsdoc/require-jsdoc: ["off"] */
|
||||||
|
|
||||||
import prompt from "prompt";
|
|
||||||
import colors from "colors";
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import process from "process";
|
import process from "process";
|
||||||
|
import { execSync } from "child_process";
|
||||||
|
|
||||||
const dir = path.join(process.cwd() + "/src/core/config/");
|
const ignoredAuthors = ["github-advanced-security[bot]", "dependabot[bot]"];
|
||||||
if (!fs.existsSync(dir)) {
|
|
||||||
console.log("\nCWD: " + process.cwd());
|
|
||||||
console.log("Error: newMinorVersion.mjs should be run from the project root");
|
|
||||||
console.log("Example> node --experimental-modules src/core/config/scripts/newMinorVersion.mjs");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let changelogData = fs.readFileSync(path.join(process.cwd(), "CHANGELOG.md"), "utf8");
|
async function main() {
|
||||||
const lastVersion = changelogData.match(/## Details\s+### \[(\d+)\.(\d+)\.(\d+)\]/);
|
const dir = path.join(process.cwd() + "/src/core/config/");
|
||||||
const newVersion = [
|
if (!fs.existsSync(dir)) {
|
||||||
parseInt(lastVersion[1], 10),
|
console.log("\nCWD: " + process.cwd());
|
||||||
parseInt(lastVersion[2], 10) + 1,
|
console.log(
|
||||||
0
|
"Error: newMinorVersion.mjs should be run from the project root",
|
||||||
];
|
);
|
||||||
|
console.log(
|
||||||
|
"Example> node --experimental-modules src/core/config/scripts/newMinorVersion.mjs",
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
let knownContributors = changelogData.match(/^\[@([^\]]+)\]/gm);
|
let changelogData = fs.readFileSync(
|
||||||
knownContributors = knownContributors.map(c => c.slice(2, -1));
|
path.join(process.cwd(), "CHANGELOG.md"),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
const lastVersion = changelogData.match(
|
||||||
|
/## Details\s+### \[(\d+)\.(\d+)\.(\d+)\]/,
|
||||||
|
);
|
||||||
|
const newVersion = [
|
||||||
|
parseInt(lastVersion[1], 10),
|
||||||
|
parseInt(lastVersion[2], 10) + 1,
|
||||||
|
0,
|
||||||
|
];
|
||||||
|
|
||||||
const date = (new Date()).toISOString().split("T")[0];
|
let knownContributors = changelogData.match(/^\[@([^\]]+)\]/gm);
|
||||||
|
knownContributors = knownContributors.map((c) => c.slice(2, -1));
|
||||||
|
|
||||||
const schema = {
|
const date = new Date().toISOString().split("T")[0];
|
||||||
properties: {
|
|
||||||
message: {
|
const lastVersionSha = execSync(
|
||||||
description: "A short but descriptive summary of a feature in this version",
|
`git rev-list -n 1 v${lastVersion[1]}.${lastVersion[2]}.${lastVersion[3]}`,
|
||||||
example: "Added 'Op name' operation",
|
{
|
||||||
prompt: "Feature description",
|
encoding: "utf8",
|
||||||
type: "string",
|
|
||||||
required: true,
|
|
||||||
},
|
},
|
||||||
author: {
|
).trim();
|
||||||
description: "The author of the feature (only one supported, edit manually to add more)",
|
if (lastVersionSha.length !== 40) {
|
||||||
example: "n1474335",
|
throw new Error(
|
||||||
prompt: "Author",
|
`Unexpected output from git rev-list: ${lastVersionSha}`,
|
||||||
type: "string",
|
);
|
||||||
default: "n1474335"
|
}
|
||||||
},
|
|
||||||
id: {
|
const features = [];
|
||||||
description: "The PR number or full commit hash for this feature.",
|
|
||||||
example: "1200",
|
const commits = await (
|
||||||
prompt: "Pull request or commit ID",
|
await fetch(`https://api.github.com/repos/gchq/cyberchef/commits`)
|
||||||
type: "string"
|
).json();
|
||||||
},
|
let foundLast = false;
|
||||||
another: {
|
for (const commit of commits) {
|
||||||
description: "y/n",
|
if (commit.sha === lastVersionSha) {
|
||||||
example: "y",
|
foundLast = true;
|
||||||
prompt: "Add another feature?",
|
break;
|
||||||
type: "string",
|
} else {
|
||||||
pattern: /^[yn]$/,
|
const feature = {
|
||||||
|
message: "",
|
||||||
|
authors: [],
|
||||||
|
id: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const msgparts = commit.commit.message.split("\n\n");
|
||||||
|
feature.message = msgparts[0];
|
||||||
|
const prIdMatch = feature.message.match(/\(#(\d+)\)$/);
|
||||||
|
if (prIdMatch !== null) {
|
||||||
|
feature.message = feature.message
|
||||||
|
.replace(prIdMatch[0], "")
|
||||||
|
.trim();
|
||||||
|
feature.id = prIdMatch[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ignoredAuthors.includes(commit.author.login)) {
|
||||||
|
feature.authors.push(commit.author.login);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msgparts.length > 1) {
|
||||||
|
msgparts[1]
|
||||||
|
.split("\n")
|
||||||
|
.filter((line) => line.startsWith("Co-authored-by: "))
|
||||||
|
.forEach((line) => {
|
||||||
|
let coAuthor = line.slice("Co-authored-by: ".length);
|
||||||
|
if (coAuthor.indexOf(">") !== -1) {
|
||||||
|
const email = coAuthor.slice(
|
||||||
|
coAuthor.indexOf("<") + 1,
|
||||||
|
coAuthor.indexOf(">"),
|
||||||
|
);
|
||||||
|
if (email.endsWith("@users.noreply.github.com")) {
|
||||||
|
coAuthor = email.slice(
|
||||||
|
email.indexOf("+") + 1,
|
||||||
|
-"@users.noreply.github.com".length,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
"Could not get ID of co-author: " +
|
||||||
|
coAuthor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
"Could not get email of co-author: " + coAuthor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!ignoredAuthors.includes(coAuthor)) {
|
||||||
|
feature.authors.push(coAuthor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
features.push(feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
if (!foundLast) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find last version commit: ${lastVersionSha} - need to add paging functionality`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Build schema
|
let message = `### [${newVersion[0]}.${newVersion[1]}.${newVersion[2]}] - ${date}\n`;
|
||||||
for (const prop in schema.properties) {
|
|
||||||
const p = schema.properties[prop];
|
|
||||||
p.description = "\n" + colors.white(p.description) + colors.cyan("\nExample: " + p.example) + "\n" + colors.green(p.prompt);
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt.message = "";
|
const authors = [];
|
||||||
prompt.delimiter = ":".green;
|
const prIDs = [];
|
||||||
|
const commitIDs = [];
|
||||||
|
|
||||||
const features = [];
|
features.forEach((feature) => {
|
||||||
const authors = [];
|
const id =
|
||||||
const prIDs = [];
|
feature.id.length > 10 ? feature.id.slice(0, 7) : "#" + feature.id;
|
||||||
const commitIDs = [];
|
message += `- ${feature.message} ${feature.authors.map((a) => `[@${a}]`).join(" ")} | [${id}]\n`;
|
||||||
|
|
||||||
prompt.start();
|
feature.authors.forEach((author) => {
|
||||||
|
if (!knownContributors.includes(author)) {
|
||||||
|
knownContributors.push(author);
|
||||||
|
authors.push(`[@${author}]: https://github.com/${author}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const getFeature = function() {
|
if (feature.id.length > 10) {
|
||||||
prompt.get(schema, (err, result) => {
|
commitIDs.push(
|
||||||
if (err) {
|
`[${id}]: https://github.com/gchq/CyberChef/commit/${feature.id}`,
|
||||||
console.log("\nExiting script.");
|
);
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
features.push(result);
|
|
||||||
|
|
||||||
if (result.another === "y") {
|
|
||||||
getFeature();
|
|
||||||
} else {
|
} else {
|
||||||
let message = `### [${newVersion[0]}.${newVersion[1]}.${newVersion[2]}] - ${date}\n`;
|
prIDs.push(
|
||||||
|
`[#${feature.id}]: https://github.com/gchq/CyberChef/pull/${feature.id}`,
|
||||||
features.forEach(feature => {
|
);
|
||||||
const id = feature.id.length > 10 ? feature.id.slice(0, 7) : "#" + feature.id;
|
|
||||||
message += `- ${feature.message} [@${feature.author}] | [${id}]\n`;
|
|
||||||
|
|
||||||
if (!knownContributors.includes(feature.author)) {
|
|
||||||
authors.push(`[@${feature.author}]: https://github.com/${feature.author}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (feature.id.length > 10) {
|
|
||||||
commitIDs.push(`[${id}]: https://github.com/gchq/CyberChef/commit/${feature.id}`);
|
|
||||||
} else {
|
|
||||||
prIDs.push(`[#${feature.id}]: https://github.com/gchq/CyberChef/pull/${feature.id}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Message
|
|
||||||
changelogData = changelogData.replace(/## Details\n\n/, "## Details\n\n" + message + "\n");
|
|
||||||
|
|
||||||
// Tag
|
|
||||||
const newTag = `[${newVersion[0]}.${newVersion[1]}.${newVersion[2]}]: https://github.com/gchq/CyberChef/releases/tag/v${newVersion[0]}.${newVersion[1]}.${newVersion[2]}\n`;
|
|
||||||
changelogData = changelogData.replace(/\n\n(\[\d+\.\d+\.\d+\]: https)/, "\n\n" + newTag + "$1");
|
|
||||||
|
|
||||||
// Author
|
|
||||||
authors.forEach(author => {
|
|
||||||
changelogData = changelogData.replace(/(\n\[@[^\]]+\]: https:\/\/github\.com\/[^\n]+\n)\n/, "$1" + author + "\n\n");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Commit IDs
|
|
||||||
commitIDs.forEach(commitID => {
|
|
||||||
changelogData = changelogData.replace(/(\n\[[^\].]+\]: https:\/\/github.com\/gchq\/CyberChef\/commit\/[^\n]+\n)\n/, "$1" + commitID + "\n\n");
|
|
||||||
});
|
|
||||||
|
|
||||||
// PR IDs
|
|
||||||
prIDs.forEach(prID => {
|
|
||||||
changelogData = changelogData.replace(/(\n\[#[^\]]+\]: https:\/\/github.com\/gchq\/CyberChef\/pull\/[^\n]+\n)\n*$/, "$1" + prID + "\n\n");
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.writeFileSync(path.join(process.cwd(), "CHANGELOG.md"), changelogData);
|
|
||||||
|
|
||||||
console.log("Written CHANGELOG.md\nCommit changes and then run `npm version minor`.");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
getFeature();
|
// Message
|
||||||
|
changelogData = changelogData.replace(
|
||||||
|
/## Details\n\n/,
|
||||||
|
"## Details\n\n" + message + "\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Tag
|
||||||
|
const newTag = `[${newVersion[0]}.${newVersion[1]}.${newVersion[2]}]: https://github.com/gchq/CyberChef/releases/tag/v${newVersion[0]}.${newVersion[1]}.${newVersion[2]}\n`;
|
||||||
|
changelogData = changelogData.replace(
|
||||||
|
/\n\n(\[\d+\.\d+\.\d+\]: https)/,
|
||||||
|
"\n\n" + newTag + "$1",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Author
|
||||||
|
authors.forEach((author) => {
|
||||||
|
changelogData = changelogData.replace(
|
||||||
|
/(\n\[@[^\]]+\]: https:\/\/github\.com\/[^\n]+\n)\n/,
|
||||||
|
"$1" + author + "\n\n",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Commit IDs
|
||||||
|
commitIDs.forEach((commitID) => {
|
||||||
|
changelogData = changelogData.replace(
|
||||||
|
/(\n\[[^\].]+\]: https:\/\/github.com\/gchq\/CyberChef\/commit\/[^\n]+\n)\n/,
|
||||||
|
"$1" + commitID + "\n\n",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// PR IDs
|
||||||
|
prIDs.forEach((prID) => {
|
||||||
|
changelogData = changelogData.replace(
|
||||||
|
/(\n\[#[^\]]+\]: https:\/\/github.com\/gchq\/CyberChef\/(?:pull|issues)\/[^\n]+\n)\n*$/,
|
||||||
|
"$1" + prID + "\n\n",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(process.cwd(), "CHANGELOG.md"), changelogData);
|
||||||
|
}
|
||||||
|
main().catch(console.error);
|
||||||
|
|||||||
371
src/core/lib/Bech32.mjs
Normal file
371
src/core/lib/Bech32.mjs
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ export function toHex(data, delim=" ", padding=2, extraDelim="", lineSize=0) {
|
|||||||
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
|
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
|
||||||
|
|
||||||
let output = "";
|
let output = "";
|
||||||
const prepend = (delim === "0x" || delim === "\\x");
|
const prepend = (delim === "0x" || delim === "\\x" || delim === "%");
|
||||||
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
for (let i = 0; i < data.length; i++) {
|
||||||
const hex = data[i].toString(16).padStart(padding, "0");
|
const hex = data[i].toString(16).padStart(padding, "0");
|
||||||
|
|||||||
@@ -91,9 +91,7 @@ export function toJA4(bytes) {
|
|||||||
let alpn = "00";
|
let alpn = "00";
|
||||||
for (const ext of tlsr.handshake.value.extensions.value) {
|
for (const ext of tlsr.handshake.value.extensions.value) {
|
||||||
if (ext.type.value === "application_layer_protocol_negotiation") {
|
if (ext.type.value === "application_layer_protocol_negotiation") {
|
||||||
alpn = parseFirstALPNValue(ext.value.data);
|
alpn = alpnFingerprint(parseFirstALPNValue(ext.value.data));
|
||||||
alpn = alpn.charAt(0) + alpn.charAt(alpn.length - 1);
|
|
||||||
if (alpn.charCodeAt(0) > 127) alpn = "99";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,9 +210,7 @@ export function toJA4S(bytes) {
|
|||||||
let alpn = "00";
|
let alpn = "00";
|
||||||
for (const ext of tlsr.handshake.value.extensions.value) {
|
for (const ext of tlsr.handshake.value.extensions.value) {
|
||||||
if (ext.type.value === "application_layer_protocol_negotiation") {
|
if (ext.type.value === "application_layer_protocol_negotiation") {
|
||||||
alpn = parseFirstALPNValue(ext.value.data);
|
alpn = alpnFingerprint(parseFirstALPNValue(ext.value.data));
|
||||||
alpn = alpn.charAt(0) + alpn.charAt(alpn.length - 1);
|
|
||||||
if (alpn.charCodeAt(0) > 127) alpn = "99";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,3 +258,33 @@ function tlsVersionMapper(version) {
|
|||||||
default: return "00"; // Unknown
|
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];
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
* @param {Uint8Array} bytes
|
||||||
* @returns {number}
|
* @returns {Uint8Array|null}
|
||||||
*/
|
*/
|
||||||
export function parseFirstALPNValue(bytes) {
|
export function parseFirstALPNValue(bytes) {
|
||||||
const s = new Stream(bytes);
|
const s = new Stream(bytes);
|
||||||
const alpnExtLen = s.readInt(2);
|
const alpnExtLen = s.readInt(2);
|
||||||
if (alpnExtLen < 3) return "00";
|
if (alpnExtLen < 2) return null;
|
||||||
const strLen = s.readInt(1);
|
const strLen = s.readInt(1);
|
||||||
if (strLen < 2) return "00";
|
if (strLen < 1) return null;
|
||||||
return s.readString(strLen);
|
return s.getBytes(strLen);
|
||||||
}
|
}
|
||||||
|
|||||||
149
src/core/operations/FromBech32.mjs
Normal file
149
src/core/operations/FromBech32.mjs
Normal 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;
|
||||||
@@ -43,7 +43,7 @@ class FromHexdump extends Operation {
|
|||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const output = [],
|
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;
|
let block, line;
|
||||||
|
|
||||||
while ((block = regex.exec(input))) {
|
while ((block = regex.exec(input))) {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class FromQuotedPrintable extends Operation {
|
|||||||
|
|
||||||
this.name = "From Quoted Printable";
|
this.name = "From Quoted Printable";
|
||||||
this.module = "Default";
|
this.module = "Default";
|
||||||
this.description = "Converts QP-encoded text back to standard text.<br><br>e.g. The quoted-printable encoded string <code>hello=20world</code> becomes <code>hello world</code>";
|
this.description = "Converts QP-encoded text back to standard text. This format is a content transfer encoding common in email messages.<br><br>e.g. The quoted-printable encoded string <code>hello=20world</code> becomes <code>hello world</code>";
|
||||||
this.infoURL = "https://wikipedia.org/wiki/Quoted-printable";
|
this.infoURL = "https://wikipedia.org/wiki/Quoted-printable";
|
||||||
this.inputType = "string";
|
this.inputType = "string";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "byteArray";
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class ToBase85 extends Operation {
|
|||||||
value: ALPHABET_OPTIONS
|
value: ALPHABET_OPTIONS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Include delimeter",
|
name: "Include delimiter",
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
value: false
|
value: false
|
||||||
}
|
}
|
||||||
|
|||||||
92
src/core/operations/ToBech32.mjs
Normal file
92
src/core/operations/ToBech32.mjs
Normal 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;
|
||||||
@@ -23,7 +23,7 @@ class ToQuotedPrintable extends Operation {
|
|||||||
|
|
||||||
this.name = "To Quoted Printable";
|
this.name = "To Quoted Printable";
|
||||||
this.module = "Default";
|
this.module = "Default";
|
||||||
this.description = "Quoted-Printable, or QP encoding, is an encoding using printable ASCII characters (alphanumeric and the equals sign '=') to transmit 8-bit data over a 7-bit data path or, generally, over a medium which is not 8-bit clean. It is defined as a MIME content transfer encoding for use in e-mail.<br><br>QP works by using the equals sign '=' as an escape character. It also limits line length to 76, as some software has limits on line length.";
|
this.description = "Quoted-Printable, or QP encoding, is an encoding using printable ASCII characters (alphanumeric and the equals sign '=') to transmit 8-bit data over a 7-bit data path or, generally, over a medium which is not 8-bit clean. It is defined as a MIME content transfer encoding for use in email.<br><br>QP works by using the equals sign '=' as an escape character. It also limits line length to 76, as some software has limits on line length.";
|
||||||
this.infoURL = "https://wikipedia.org/wiki/Quoted-printable";
|
this.infoURL = "https://wikipedia.org/wiki/Quoted-printable";
|
||||||
this.inputType = "ArrayBuffer";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "string";
|
this.outputType = "string";
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export function removeSubheadingsFromArray(array) {
|
|||||||
* @param str
|
* @param str
|
||||||
*/
|
*/
|
||||||
export function sanitise(str) {
|
export function sanitise(str) {
|
||||||
return str.replace(/ /g, "").toLowerCase();
|
return str.replace(/[/\s.-]/g, "").toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -650,7 +650,7 @@ class App {
|
|||||||
|
|
||||||
// const compareURL = `https://github.com/gchq/CyberChef/compare/v${prev.join(".")}...v${PKG_VERSION}`;
|
// 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 !== "") {
|
if (window.compileMessage !== "") {
|
||||||
compileInfo += " - " + window.compileMessage;
|
compileInfo += " - " + window.compileMessage;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import sm from "sitemap";
|
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.
|
* 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
|
* @license Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const smStream = new sm.SitemapStream({
|
const baseUrl = "https://gchq.github.io/CyberChef/";
|
||||||
hostname: "https://gchq.github.io/CyberChef",
|
|
||||||
});
|
const smStream = new sm.SitemapStream({});
|
||||||
|
|
||||||
smStream.write({
|
smStream.write({
|
||||||
url: "/",
|
url: baseUrl,
|
||||||
changefreq: "weekly",
|
changefreq: "weekly",
|
||||||
priority: 1.0
|
priority: 1.0,
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const op in OperationConfig) {
|
for (const op in OperationConfig) {
|
||||||
smStream.write({
|
smStream.write({
|
||||||
url: `/?op=${encodeURIComponent(op)}`,
|
url: `${baseUrl}?op=${encodeURIComponent(op)}`,
|
||||||
changeFreq: "yearly",
|
changeFreq: "yearly",
|
||||||
priority: 0.5
|
priority: 0.5,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
smStream.end();
|
smStream.end();
|
||||||
|
|
||||||
sm.streamToPromise(smStream).then(
|
sm.streamToPromise(smStream).then(
|
||||||
buffer => console.log(buffer.toString()) // eslint-disable-line no-console
|
(buffer) => console.log(buffer.toString()), // eslint-disable-line no-console
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -151,8 +151,20 @@ class InputWaiter {
|
|||||||
// Event handlers
|
// Event handlers
|
||||||
EditorView.domEventHandlers({
|
EditorView.domEventHandlers({
|
||||||
paste(event, view) {
|
paste(event, view) {
|
||||||
|
const clipboardData = event.clipboardData;
|
||||||
|
const items = clipboardData.items;
|
||||||
|
const files = [];
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const item = items[i];
|
||||||
|
if (item.kind === "file") {
|
||||||
|
const file = item.getAsFile();
|
||||||
|
files.push(file);
|
||||||
|
|
||||||
|
event.preventDefault(); // Prevent the default paste behavior
|
||||||
|
}
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
self.afterPaste(event);
|
self.afterPaste(files);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -914,9 +926,12 @@ class InputWaiter {
|
|||||||
* Handler that fires just after input paste events.
|
* Handler that fires just after input paste events.
|
||||||
* Checks whether the EOL separator or character encoding should be updated.
|
* Checks whether the EOL separator or character encoding should be updated.
|
||||||
*
|
*
|
||||||
* @param {event} e
|
* @param {File[]} files - An array of any files that were included in the paste event
|
||||||
*/
|
*/
|
||||||
afterPaste(e) {
|
afterPaste(files) {
|
||||||
|
if (files.length > 0) {
|
||||||
|
this.loadUIFiles(files);
|
||||||
|
}
|
||||||
// If EOL has been fixed, skip this.
|
// If EOL has been fixed, skip this.
|
||||||
if (this.eolState > 1) return;
|
if (this.eolState > 1) return;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Dish from "../../core/Dish.mjs";
|
import Dish from "../../core/Dish.mjs";
|
||||||
|
import DishError from "../../core/errors/DishError.mjs";
|
||||||
|
import { CHR_ENC_SIMPLE_REVERSE_LOOKUP } from "../../core/lib/ChrEnc.mjs";
|
||||||
import Utils from "../../core/Utils.mjs";
|
import Utils from "../../core/Utils.mjs";
|
||||||
import cptable from "codepage";
|
import cptable from "codepage";
|
||||||
import loglevelMessagePrefix from "loglevel-message-prefix";
|
import loglevelMessagePrefix from "loglevel-message-prefix";
|
||||||
@@ -98,7 +100,7 @@ async function bufferToStr(data) {
|
|||||||
try {
|
try {
|
||||||
str = cptable.utils.decode(data.encoding, new Uint8Array(data.buffer));
|
str = cptable.utils.decode(data.encoding, new Uint8Array(data.buffer));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
str = err;
|
str = new DishError(`Error decoding buffer with encoding ${CHR_ENC_SIMPLE_REVERSE_LOOKUP[data.encoding]}: ${err.message}`).toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,4 +20,10 @@ TestRegister.addApiTests([
|
|||||||
assert.equal(Utils.parseEscapedChars("\\\\\\'"), "\\'");
|
assert.equal(Utils.parseEscapedChars("\\\\\\'"), "\\'");
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
it("Utils: should replace delete character", () => {
|
||||||
|
assert.equal(
|
||||||
|
Utils.printable("\x7e\x7f\x80\xa7", false, true),
|
||||||
|
"\x7e...",
|
||||||
|
);
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -345,6 +345,42 @@ TestRegister.addApiTests([
|
|||||||
assert.strictEqual(result.toString(), "begin_something_aaaaaaaaaaaaaa_end_something");
|
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", () => {
|
it("chef.bake: cannot accept flowControl operations in recipe", () => {
|
||||||
assert.throws(() => chef.bake("some input", "magic"), {
|
assert.throws(() => chef.bake("some input", "magic"), {
|
||||||
name: "TypeError",
|
name: "TypeError",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import "./tests/Base64.mjs";
|
|||||||
import "./tests/Base85.mjs";
|
import "./tests/Base85.mjs";
|
||||||
import "./tests/Base92.mjs";
|
import "./tests/Base92.mjs";
|
||||||
import "./tests/BCD.mjs";
|
import "./tests/BCD.mjs";
|
||||||
|
import "./tests/Bech32.mjs";
|
||||||
import "./tests/BitwiseOp.mjs";
|
import "./tests/BitwiseOp.mjs";
|
||||||
import "./tests/BLAKE2b.mjs";
|
import "./tests/BLAKE2b.mjs";
|
||||||
import "./tests/BLAKE2s.mjs";
|
import "./tests/BLAKE2s.mjs";
|
||||||
|
|||||||
702
tests/operations/tests/Bech32.mjs
Normal file
702
tests/operations/tests/Bech32.mjs
Normal 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"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
@@ -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",
|
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>',
|
input: '<div id="test">\n<p class="a">hello</p>\n<p>world</p>\n<p class="a">again</p>\n</div>',
|
||||||
|
|||||||
@@ -43,6 +43,20 @@ TestRegister.addTests([
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ASCII to Hex with percent deliminator",
|
||||||
|
input: "aberystwyth",
|
||||||
|
expectedOutput: "%61%62%65%72%79%73%74%77%79%74%68",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "To Hex",
|
||||||
|
"args": [
|
||||||
|
"Percent",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ASCII to 0x Hex with comma and line breaks",
|
name: "ASCII to 0x Hex with comma and line breaks",
|
||||||
input: "aberystwyth",
|
input: "aberystwyth",
|
||||||
|
|||||||
@@ -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",
|
name: "From Hexdump: Wireshark",
|
||||||
input: `00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ........ ........
|
input: `00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ........ ........
|
||||||
|
|||||||
@@ -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",
|
name: "JA4 Fingerprint: TLS 1.2",
|
||||||
input: "1603010200010001fc0303ecb2691addb2bf6c599c7aaae23de5f42561cc04eb41029acc6fc050a16ac1d22046f8617b580ac9358e2aa44e306d52466bcc989c87c8ca64309f5faf50ba7b4d0022130113031302c02bc02fcca9cca8c02cc030c00ac009c013c014009c009d002f00350100019100000021001f00001c636f6e74696c652e73657276696365732e6d6f7a696c6c612e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000022000a000804030503060302030033006b0069001d00208909858fbeb6ed2f1248ba5b9e2978bead0e840110192c61daed0096798b184400170041044d183d91f5eed35791fa982464e3b0214aaa5f5d1b78616d9b9fbebc22d11f535b2f94c686143136aa795e6e5a875d6c08064ad5b76d44caad766e2483012748002b00050403040303000d0018001604030503060308040805080604010501060102030201002d00020101001c000240010015007a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
input: "1603010200010001fc0303ecb2691addb2bf6c599c7aaae23de5f42561cc04eb41029acc6fc050a16ac1d22046f8617b580ac9358e2aa44e306d52466bcc989c87c8ca64309f5faf50ba7b4d0022130113031302c02bc02fcca9cca8c02cc030c00ac009c013c014009c009d002f00350100019100000021001f00001c636f6e74696c652e73657276696365732e6d6f7a696c6c612e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000022000a000804030503060302030033006b0069001d00208909858fbeb6ed2f1248ba5b9e2978bead0e840110192c61daed0096798b184400170041044d183d91f5eed35791fa982464e3b0214aaa5f5d1b78616d9b9fbebc22d11f535b2f94c686143136aa795e6e5a875d6c08064ad5b76d44caad766e2483012748002b00050403040303000d0018001604030503060308040805080604010501060102030201002d00020101001c000240010015007a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
|||||||
Reference in New Issue
Block a user