diff --git a/.editorconfig b/.editorconfig
index b50059bb..cef4cab0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -12,3 +12,7 @@ indent_size = 4
[{package.json,.travis.yml,nightwatch.json}]
indent_style = space
indent_size = 2
+
+[.github/**.yml]
+indent_style = space
+indent_size = 2
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
deleted file mode 100644
index 1350e976..00000000
--- a/.github/workflows/codeql.yml
+++ /dev/null
@@ -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}}"
diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
index a63c588b..13d280f6 100644
--- a/.github/workflows/master.yml
+++ b/.github/workflows/master.yml
@@ -4,55 +4,62 @@ on:
workflow_dispatch:
push:
branches:
- - master
+ - master
+
+permissions:
+ contents: read
jobs:
main:
+ permissions:
+ contents: write
+ pages: write
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@v6
- - name: Set node version
- uses: actions/setup-node@v6
- with:
- node-version: 18
+ - name: Set node version
+ uses: actions/setup-node@v6
+ with:
+ node-version: 18
+ registry-url: "https://registry.npmjs.org"
- - name: Install
- run: |
- export DETECT_CHROMEDRIVER_VERSION=true
- npm install
- npm run setheapsize
+ - name: Install
+ run: |
+ export DETECT_CHROMEDRIVER_VERSION=true
+ npm install
+ npm run setheapsize
- - name: Lint
- run: npx grunt lint
+ - name: Lint
+ run: npx grunt lint
- - name: Unit Tests
- run: |
- npm test
- npm run testnodeconsumer
+ - name: Unit Tests
+ run: |
+ npm test
+ npm run testnodeconsumer
- - name: Production Build
- if: success()
- run: npx grunt prod --msg="Version 10 is here! Read about the new features here"
+ - name: Production Build
+ if: success()
+ run: npx grunt prod --msg=""
- - name: Generate sitemap
- run: npx grunt exec:sitemap
+ - name: Generate sitemap
+ run: npx grunt exec:sitemap
- - name: UI Tests
- if: success()
- run: |
- sudo apt-get install xvfb
- xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
+ - name: UI Tests
+ if: success()
+ run: |
+ sudo apt-get install xvfb
+ xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
- - name: Prepare for GitHub Pages
- if: success()
- run: npx grunt copy:ghPages
+ - name: Prepare for GitHub Pages
+ if: success()
+ run: npx grunt copy:ghPages
- - name: Deploy to GitHub Pages
- if: success() && github.ref == 'refs/heads/master'
- uses: crazy-max/ghaction-github-pages@v3
- with:
- target_branch: gh-pages
- build_dir: ./build/prod
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Deploy to GitHub Pages
+ if: success() && github.ref == 'refs/heads/master'
+ uses: crazy-max/ghaction-github-pages@v3
+ with:
+ target_branch: gh-pages
+ build_dir: ./build/prod
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/pull_requests.yml b/.github/workflows/pull_requests.yml
index 296e60b9..8f04df72 100644
--- a/.github/workflows/pull_requests.yml
+++ b/.github/workflows/pull_requests.yml
@@ -1,5 +1,8 @@
name: "Pull Requests"
+permissions:
+ contents: read
+
on:
workflow_dispatch:
pull_request:
@@ -9,47 +12,46 @@ jobs:
main:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v6
- - name: Set node version
- uses: actions/setup-node@v3
- with:
- node-version: '18.x'
+ - name: Set node version
+ uses: actions/setup-node@v6
+ with:
+ node-version: 18
+ registry-url: "https://registry.npmjs.org"
- - name: Install
- run: |
- export DETECT_CHROMEDRIVER_VERSION=true
- npm install
- npm run setheapsize
+ - name: Install
+ run: |
+ export DETECT_CHROMEDRIVER_VERSION=true
+ npm install
+ npm run setheapsize
- - name: Lint
- run: npx grunt lint
+ - name: Lint
+ run: npx grunt lint
- - name: Unit Tests
- run: |
- npm test
- npm run testnodeconsumer
+ - name: Unit Tests
+ run: |
+ npm test
+ npm run testnodeconsumer
- - name: Production Build
- if: success()
- run: npx grunt prod
+ - name: Production Build
+ if: success()
+ run: npx grunt prod
- - name: Production Image Build
- if: success()
- id: build-image
- uses: redhat-actions/buildah-build@v2
- with:
- # Not being uploaded to any registry, use a simple name to allow Buildah to build correctly.
- image: cyberchef
- containerfiles: ./Dockerfile
- platforms: linux/amd64
- oci: true
- # Webpack seems to use a lot of open files, increase the max open file limit to accomodate.
- extra-args: |
- --ulimit nofile=10000
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
- - name: UI Tests
- if: success()
- run: |
- sudo apt-get install xvfb
- xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
+ - name: Production Image Build
+ if: success()
+ id: build-image
+ uses: docker/build-push-action@v6
+ with:
+ platforms: linux/amd64,linux/arm64
+ - name: UI Tests
+ if: success()
+ run: |
+ sudo apt-get install xvfb
+ xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml
index a77f4984..ef397a16 100644
--- a/.github/workflows/releases.yml
+++ b/.github/workflows/releases.yml
@@ -4,7 +4,10 @@ on:
workflow_dispatch:
push:
tags:
- - 'v*'
+ - "v*"
+
+permissions:
+ contents: read
env:
REGISTRY: ghcr.io
@@ -14,83 +17,110 @@ env:
jobs:
main:
+ permissions:
+ packages: write
+ contents: write
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v6
- - name: Set node version
- uses: actions/setup-node@v3
- with:
- node-version: '18.x'
+ - name: Set node version
+ uses: actions/setup-node@v6
+ with:
+ node-version: 18
+ registry-url: "https://registry.npmjs.org"
- - name: Install
- run: |
- export DETECT_CHROMEDRIVER_VERSION=true
- npm ci
- npm run setheapsize
+ - name: Install
+ run: |
+ export DETECT_CHROMEDRIVER_VERSION=true
+ npm ci
+ npm run setheapsize
- - name: Lint
- run: npx grunt lint
+ - name: Lint
+ run: npx grunt lint
- - name: Unit Tests
- run: |
- npm test
- npm run testnodeconsumer
+ - name: Unit Tests
+ run: |
+ npm test
+ npm run testnodeconsumer
- - name: Production Build
- run: npx grunt prod
+ - name: Production Build
+ run: npx grunt prod
- - name: UI Tests
- run: |
- sudo apt-get install xvfb
- xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
+ - name: UI Tests
+ run: |
+ sudo apt-get install xvfb
+ xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
- - name: Image Metadata
- id: image-metadata
- uses: docker/metadata-action@v4
- with:
- images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- tags: |
- type=semver,pattern={{major}}
- type=semver,pattern={{major}}.{{minor}}
- type=semver,pattern={{version}}
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
- - name: Production Image Build
- id: build-image
- uses: redhat-actions/buildah-build@v2
- with:
- tags: ${{ steps.image-metadata.outputs.tags }}
- labels: ${{ steps.image-metadata.outputs.labels }}
- containerfiles: ./Dockerfile
- platforms: linux/amd64,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: Set up QEMU
+ uses: docker/setup-qemu-action@v3
- - name: Publish to GHCR
- uses: redhat-actions/push-to-registry@v2
- with:
- image: ${{ steps.build-image.outputs.image }}
- tags: ${{ steps.build-image.outputs.tags }}
- registry: ${{ env.REGISTRY }}
- username: ${{ env.REGISTRY_USER }}
- password: ${{ env.REGISTRY_PASSWORD }}
+ - name: Image Metadata
+ id: image-metadata
+ uses: docker/metadata-action@v4
+ with:
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+ tags: |
+ type=semver,pattern={{major}}
+ type=semver,pattern={{major}}.{{minor}}
+ type=semver,pattern={{version}}
- - name: Upload Release Assets
- id: upload-release-assets
- uses: svenstaro/upload-release-action@v2
- with:
- repo_token: ${{ secrets.GITHUB_TOKEN }}
- file: build/prod/*.zip
- tag: ${{ github.ref }}
- overwrite: true
- file_glob: true
- body: "See the [CHANGELOG](https://github.com/gchq/CyberChef/blob/master/CHANGELOG.md) and [commit messages](https://github.com/gchq/CyberChef/commits/master) for details."
+ - name: Log in to GHCR
+ uses: docker/login-action@v3
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ env.REGISTRY_USER }}
+ password: ${{ env.REGISTRY_PASSWORD }}
- - name: Publish to NPM
- uses: JS-DevTools/npm-publish@v1
- with:
- token: ${{ secrets.NPM_TOKEN }}
+ - name: Publish to GHCR
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ push: true
+ tags: ${{ steps.image-metadata.outputs.tags }}
+ labels: ${{ steps.image-metadata.outputs.labels }}
+ platforms: linux/amd64,linux/arm64
+
+ - name: 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
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 535a11a3..960e922b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,35 @@ All major and minor version changes will be documented in this file. Details of
## 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
- Fixed Optical Character Recognition and added tests [@n1474335] | [ab37c1e]
- 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
- 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.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
@@ -754,6 +785,18 @@ All major and minor version changes will be documented in this file. Details of
[@remingtr]: https://github.com/remingtr
[@0xff1ce]: https://github.com/0xff1ce
[@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
@@ -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
[#1732]: https://github.com/gchq/CyberChef/issues/1732
[#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
+
diff --git a/Dockerfile b/Dockerfile
index ba605fd7..856a16ce 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -27,9 +27,8 @@ RUN npm run build
#########################################
# 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
-# 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
+FROM nginx:stable-alpine AS cyberchef
+
+LABEL maintainer="GCHQ "
COPY --from=builder /app/build/prod /usr/share/nginx/html/
diff --git a/package-lock.json b/package-lock.json
index f6a87e09..9e2a433b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "cyberchef",
- "version": "10.20.0",
+ "version": "10.22.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cyberchef",
- "version": "10.20.0",
+ "version": "10.22.1",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -5139,7 +5139,6 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"possible-typed-array-names": "^1.0.0"
@@ -6240,9 +6239,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001766",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz",
- "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==",
+ "version": "1.0.30001769",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz",
+ "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==",
"dev": true,
"funding": [
{
@@ -8738,6 +8737,22 @@
"node": ">= 0.4"
}
},
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/es6-object-assign": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
@@ -9778,7 +9793,6 @@
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"is-callable": "^1.1.3"
@@ -9808,14 +9822,16 @@
}
},
"node_modules/form-data": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
- "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"dev": true,
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
@@ -11580,7 +11596,6 @@
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -11912,6 +11927,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-unc-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
@@ -13763,9 +13793,9 @@
}
},
"node_modules/node-forge": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
- "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz",
+ "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==",
"license": "(BSD-3-Clause OR GPL-2.0)",
"engines": {
"node": ">= 6.13.0"
@@ -14599,19 +14629,20 @@
}
},
"node_modules/pbkdf2": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
- "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz",
+ "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==",
"license": "MIT",
"dependencies": {
- "create-hash": "^1.1.2",
- "create-hmac": "^1.1.4",
- "ripemd160": "^2.0.1",
- "safe-buffer": "^5.0.1",
- "sha.js": "^2.4.8"
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "ripemd160": "^2.0.3",
+ "safe-buffer": "^5.2.1",
+ "sha.js": "^2.4.12",
+ "to-buffer": "^1.2.1"
},
"engines": {
- "node": ">=0.12"
+ "node": ">= 0.10"
}
},
"node_modules/peek-readable": {
@@ -14784,7 +14815,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
"integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -15791,13 +15821,31 @@
}
},
"node_modules/ripemd160": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
- "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz",
+ "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==",
"license": "MIT",
"dependencies": {
- "hash-base": "^3.0.0",
- "inherits": "^2.0.1"
+ "hash-base": "^3.1.2",
+ "inherits": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/ripemd160/node_modules/hash-base": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz",
+ "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "^2.3.8",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
}
},
"node_modules/rison": {
@@ -16259,16 +16307,23 @@
"license": "ISC"
},
"node_modules/sha.js": {
- "version": "2.4.11",
- "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
- "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz",
+ "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==",
"license": "(MIT AND BSD-3-Clause)",
"dependencies": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.0"
},
"bin": {
"sha.js": "bin.js"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/shallow-clone": {
@@ -17236,6 +17291,26 @@
"node": ">=14.14"
}
},
+ "node_modules/to-buffer": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz",
+ "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==",
+ "license": "MIT",
+ "dependencies": {
+ "isarray": "^2.0.5",
+ "safe-buffer": "^5.2.1",
+ "typed-array-buffer": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/to-buffer/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "license": "MIT"
+ },
"node_modules/to-fast-properties": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
@@ -17438,6 +17513,20 @@
"node": ">= 0.6"
}
},
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/ua-parser-js": {
"version": "1.0.40",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz",
@@ -18305,7 +18394,6 @@
"version": "1.1.18",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz",
"integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"available-typed-arrays": "^1.0.7",
diff --git a/package.json b/package.json
index 0e23f898..31308b8f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cyberchef",
- "version": "10.20.0",
+ "version": "10.22.1",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 ",
"homepage": "https://gchq.github.io/CyberChef",
@@ -202,7 +202,8 @@
"lint:grammar": "cspell ./src",
"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",
- "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`)'",
"setheapsize": "export NODE_OPTIONS=--max_old_space_size=2048"
}
diff --git a/src/core/config/scripts/newMinorVersion.mjs b/src/core/config/scripts/newMinorVersion.mjs
index 67754890..d22ef43b 100644
--- a/src/core/config/scripts/newMinorVersion.mjs
+++ b/src/core/config/scripts/newMinorVersion.mjs
@@ -7,138 +7,197 @@
*/
/* eslint no-console: ["off"] */
+/* eslint jsdoc/require-jsdoc: ["off"] */
-import prompt from "prompt";
-import colors from "colors";
import path from "path";
-import fs from "fs";
+import fs from "fs";
import process from "process";
+import { execSync } from "child_process";
-const dir = path.join(process.cwd() + "/src/core/config/");
-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);
-}
+const ignoredAuthors = ["github-advanced-security[bot]", "dependabot[bot]"];
-let changelogData = fs.readFileSync(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
-];
+async function main() {
+ const dir = path.join(process.cwd() + "/src/core/config/");
+ 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 knownContributors = changelogData.match(/^\[@([^\]]+)\]/gm);
-knownContributors = knownContributors.map(c => c.slice(2, -1));
+ let changelogData = fs.readFileSync(
+ 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 = {
- properties: {
- message: {
- description: "A short but descriptive summary of a feature in this version",
- example: "Added 'Op name' operation",
- prompt: "Feature description",
- type: "string",
- required: true,
+ const date = new Date().toISOString().split("T")[0];
+
+ const lastVersionSha = execSync(
+ `git rev-list -n 1 v${lastVersion[1]}.${lastVersion[2]}.${lastVersion[3]}`,
+ {
+ encoding: "utf8",
},
- author: {
- description: "The author of the feature (only one supported, edit manually to add more)",
- example: "n1474335",
- prompt: "Author",
- type: "string",
- default: "n1474335"
- },
- id: {
- description: "The PR number or full commit hash for this feature.",
- example: "1200",
- prompt: "Pull request or commit ID",
- type: "string"
- },
- another: {
- description: "y/n",
- example: "y",
- prompt: "Add another feature?",
- type: "string",
- pattern: /^[yn]$/,
+ ).trim();
+ if (lastVersionSha.length !== 40) {
+ throw new Error(
+ `Unexpected output from git rev-list: ${lastVersionSha}`,
+ );
+ }
+
+ const features = [];
+
+ const commits = await (
+ await fetch(`https://api.github.com/repos/gchq/cyberchef/commits`)
+ ).json();
+ let foundLast = false;
+ for (const commit of commits) {
+ if (commit.sha === lastVersionSha) {
+ foundLast = true;
+ break;
+ } else {
+ 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
-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);
-}
+ let message = `### [${newVersion[0]}.${newVersion[1]}.${newVersion[2]}] - ${date}\n`;
-prompt.message = "";
-prompt.delimiter = ":".green;
+ const authors = [];
+ const prIDs = [];
+ const commitIDs = [];
-const features = [];
-const authors = [];
-const prIDs = [];
-const commitIDs = [];
+ features.forEach((feature) => {
+ const id =
+ feature.id.length > 10 ? feature.id.slice(0, 7) : "#" + feature.id;
+ 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() {
- prompt.get(schema, (err, result) => {
- if (err) {
- console.log("\nExiting script.");
- process.exit(0);
- }
-
- features.push(result);
-
- if (result.another === "y") {
- getFeature();
+ if (feature.id.length > 10) {
+ commitIDs.push(
+ `[${id}]: https://github.com/gchq/CyberChef/commit/${feature.id}`,
+ );
} else {
- let message = `### [${newVersion[0]}.${newVersion[1]}.${newVersion[2]}] - ${date}\n`;
-
- 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`.");
+ prIDs.push(
+ `[#${feature.id}]: https://github.com/gchq/CyberChef/pull/${feature.id}`,
+ );
}
});
-};
-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);
diff --git a/src/core/lib/Hex.mjs b/src/core/lib/Hex.mjs
index 78e1ad58..6f998e2b 100644
--- a/src/core/lib/Hex.mjs
+++ b/src/core/lib/Hex.mjs
@@ -33,7 +33,7 @@ export function toHex(data, delim=" ", padding=2, extraDelim="", lineSize=0) {
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
let output = "";
- const prepend = (delim === "0x" || delim === "\\x");
+ const prepend = (delim === "0x" || delim === "\\x" || delim === "%");
for (let i = 0; i < data.length; i++) {
const hex = data[i].toString(16).padStart(padding, "0");
diff --git a/src/core/operations/FromHexdump.mjs b/src/core/operations/FromHexdump.mjs
index e8c25441..6fd3c1dc 100644
--- a/src/core/operations/FromHexdump.mjs
+++ b/src/core/operations/FromHexdump.mjs
@@ -43,7 +43,7 @@ class FromHexdump extends Operation {
*/
run(input, args) {
const output = [],
- regex = /^\s*(?:[\dA-F]{4,16}h?:?)?[ \t]+((?:[\dA-F]{2} ){1,8}(?:[ \t]|[\dA-F]{2}-)(?:[\dA-F]{2} ){1,8}|(?:[\dA-F]{4} )*[\dA-F]{4}|(?:[\dA-F]{2} )*[\dA-F]{2})/igm;
+ regex = /^\s*(?:[\dA-F]{4,16}h?:?)?[ \t]+((?:[\dA-F]{2} ){1,8}(?:[ \t]|[\dA-F]{2}-)(?:[\dA-F]{2} ){1,8}|(?:[\dA-F]{4} )+(?:[\dA-F]{2})?|(?:[\dA-F]{2} )*[\dA-F]{2})/igm;
let block, line;
while ((block = regex.exec(input))) {
diff --git a/src/core/operations/FromQuotedPrintable.mjs b/src/core/operations/FromQuotedPrintable.mjs
index 7dce45b6..4854c150 100644
--- a/src/core/operations/FromQuotedPrintable.mjs
+++ b/src/core/operations/FromQuotedPrintable.mjs
@@ -23,7 +23,7 @@ class FromQuotedPrintable extends Operation {
this.name = "From Quoted Printable";
this.module = "Default";
- this.description = "Converts QP-encoded text back to standard text.
e.g. The quoted-printable encoded string hello=20world becomes hello world";
+ this.description = "Converts QP-encoded text back to standard text. This format is a content transfer encoding common in email messages.
e.g. The quoted-printable encoded string hello=20world becomes hello world";
this.infoURL = "https://wikipedia.org/wiki/Quoted-printable";
this.inputType = "string";
this.outputType = "byteArray";
diff --git a/src/core/operations/ToQuotedPrintable.mjs b/src/core/operations/ToQuotedPrintable.mjs
index 9db5c5a5..2ea204f9 100644
--- a/src/core/operations/ToQuotedPrintable.mjs
+++ b/src/core/operations/ToQuotedPrintable.mjs
@@ -23,7 +23,7 @@ class ToQuotedPrintable extends Operation {
this.name = "To Quoted Printable";
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.
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.
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.inputType = "ArrayBuffer";
this.outputType = "string";
diff --git a/src/node/apiUtils.mjs b/src/node/apiUtils.mjs
index 64688073..9d1c43cc 100644
--- a/src/node/apiUtils.mjs
+++ b/src/node/apiUtils.mjs
@@ -66,7 +66,7 @@ export function removeSubheadingsFromArray(array) {
* @param str
*/
export function sanitise(str) {
- return str.replace(/ /g, "").toLowerCase();
+ return str.replace(/[/\s.-]/g, "").toLowerCase();
}
diff --git a/src/web/App.mjs b/src/web/App.mjs
index 7071854a..143545d6 100644
--- a/src/web/App.mjs
+++ b/src/web/App.mjs
@@ -650,7 +650,7 @@ class App {
// const compareURL = `https://github.com/gchq/CyberChef/compare/v${prev.join(".")}...v${PKG_VERSION}`;
- let compileInfo = `Last build: ${timeSinceCompile.substr(0, 1).toUpperCase() + timeSinceCompile.substr(1)} ago`;
+ let compileInfo = `Last build: ${timeSinceCompile.substring(0, 1).toUpperCase() + timeSinceCompile.substring(1)} ago`;
if (window.compileMessage !== "") {
compileInfo += " - " + window.compileMessage;
diff --git a/src/web/static/sitemap.mjs b/src/web/static/sitemap.mjs
index b96047fc..4f8101d4 100644
--- a/src/web/static/sitemap.mjs
+++ b/src/web/static/sitemap.mjs
@@ -1,6 +1,5 @@
import sm from "sitemap";
-import OperationConfig from "../../core/config/OperationConfig.json" assert {type: "json"};
-
+import OperationConfig from "../../core/config/OperationConfig.json" assert { type: "json" };
/**
* Generates an XML sitemap for all CyberChef operations and a number of recipes.
@@ -10,25 +9,25 @@ import OperationConfig from "../../core/config/OperationConfig.json" assert {typ
* @license Apache-2.0
*/
-const smStream = new sm.SitemapStream({
- hostname: "https://gchq.github.io/CyberChef",
-});
+const baseUrl = "https://gchq.github.io/CyberChef/";
+
+const smStream = new sm.SitemapStream({});
smStream.write({
- url: "/",
+ url: baseUrl,
changefreq: "weekly",
- priority: 1.0
+ priority: 1.0,
});
for (const op in OperationConfig) {
smStream.write({
- url: `/?op=${encodeURIComponent(op)}`,
+ url: `${baseUrl}?op=${encodeURIComponent(op)}`,
changeFreq: "yearly",
- priority: 0.5
+ priority: 0.5,
});
}
smStream.end();
sm.streamToPromise(smStream).then(
- buffer => console.log(buffer.toString()) // eslint-disable-line no-console
+ (buffer) => console.log(buffer.toString()), // eslint-disable-line no-console
);
diff --git a/src/web/waiters/InputWaiter.mjs b/src/web/waiters/InputWaiter.mjs
index d32ed9d1..083fb495 100644
--- a/src/web/waiters/InputWaiter.mjs
+++ b/src/web/waiters/InputWaiter.mjs
@@ -151,8 +151,20 @@ class InputWaiter {
// Event handlers
EditorView.domEventHandlers({
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(() => {
- self.afterPaste(event);
+ self.afterPaste(files);
});
}
})
@@ -914,9 +926,12 @@ class InputWaiter {
* Handler that fires just after input paste events.
* 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 (this.eolState > 1) return;
diff --git a/src/web/workers/DishWorker.mjs b/src/web/workers/DishWorker.mjs
index 4bfb701f..662f3ffc 100644
--- a/src/web/workers/DishWorker.mjs
+++ b/src/web/workers/DishWorker.mjs
@@ -7,6 +7,8 @@
*/
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 cptable from "codepage";
import loglevelMessagePrefix from "loglevel-message-prefix";
@@ -98,7 +100,7 @@ async function bufferToStr(data) {
try {
str = cptable.utils.decode(data.encoding, new Uint8Array(data.buffer));
} catch (err) {
- str = err;
+ str = new DishError(`Error decoding buffer with encoding ${CHR_ENC_SIMPLE_REVERSE_LOOKUP[data.encoding]}: ${err.message}`).toString();
}
}
diff --git a/tests/node/tests/nodeApi.mjs b/tests/node/tests/nodeApi.mjs
index 29a47ffc..92d4d991 100644
--- a/tests/node/tests/nodeApi.mjs
+++ b/tests/node/tests/nodeApi.mjs
@@ -345,6 +345,42 @@ TestRegister.addApiTests([
assert.strictEqual(result.toString(), "begin_something_aaaaaaaaaaaaaa_end_something");
}),
+ it("chef.bake: should accept operation names from Chef Website which contain forward slash", () => {
+ const result = chef.bake("I'll have the test salmon", [
+ { "op": "Find / Replace",
+ "args": [{ "option": "Regex", "string": "test" }, "good", true, false, true, false]}
+ ]);
+ assert.strictEqual(result.toString(), "I'll have the good salmon");
+ }),
+
+ it("chef.bake: should accept operation names from Chef Website which contain a hyphen", () => {
+ const result = chef.bake("I'll have the test salmon", [
+ { "op": "Adler-32 Checksum",
+ "args": [] }
+ ]);
+ assert.strictEqual(result.toString(), "6e4208f8");
+ }),
+
+ it("chef.bake: should accept operation names from Chef Website which contain a period", () => {
+ const result = chef.bake("30 13 02 01 05 16 0e 41 6e 79 62 6f 64 79 20 74 68 65 72 65 3f", [
+ { "op": "Parse ASN.1 hex string",
+ "args": [0, 32] }
+ ]);
+ assert.strictEqual(result.toString(), `SEQUENCE
+ INTEGER 05
+ IA5String 'Anybody there?'
+`);
+ }),
+
+ it("Excluded operations: throw a sensible error when you try and call one", () => {
+ try {
+ chef.fork();
+ } catch (e) {
+ assert.strictEqual(e.type, "ExcludedOperationError");
+ assert.strictEqual(e.message, "Sorry, the Fork operation is not available in the Node.js version of CyberChef.");
+ }
+ }),
+
it("chef.bake: cannot accept flowControl operations in recipe", () => {
assert.throws(() => chef.bake("some input", "magic"), {
name: "TypeError",
diff --git a/tests/operations/tests/Hex.mjs b/tests/operations/tests/Hex.mjs
index 3bb89544..6e49f6f8 100644
--- a/tests/operations/tests/Hex.mjs
+++ b/tests/operations/tests/Hex.mjs
@@ -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",
input: "aberystwyth",
diff --git a/tests/operations/tests/Hexdump.mjs b/tests/operations/tests/Hexdump.mjs
index 90523a08..6eb486db 100644
--- a/tests/operations/tests/Hexdump.mjs
+++ b/tests/operations/tests/Hexdump.mjs
@@ -152,6 +152,17 @@ TestRegister.addTests([
}
],
},
+ {
+ name: "From Hexdump: xxd format, odd number of bytes",
+ input: "00000000: 6162 6364 65 abcde",
+ expectedOutput: "abcde",
+ recipeConfig: [
+ {
+ op: "From Hexdump",
+ args: []
+ }
+ ],
+ },
{
name: "From Hexdump: Wireshark",
input: `00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ........ ........