2
0
mirror of https://github.com/gchq/CyberChef synced 2026-02-04 10:44:01 +00:00

Compare commits

..

40 Commits

Author SHA1 Message Date
C85297
188b25d7a3 Build for ARM on pull requests 2026-02-03 15:37:44 +00:00
Alex Gustafsson
967f93aa63 Use recommended GitHub actions to build image
- The redhat actions no longer provide anything not provided by the
  official docker action
- The redhat action creates OCI images that are not following best
  practices from the OCI specification
2025-05-26 17:53:27 +02:00
jg42526
c57556f49f Merge pull request #2021 from kendallgoto/kgoto/add-handlebar
Add new operation: Template
2025-05-16 11:22:50 +01:00
jg42526
18e5b9f6ec Merge branch 'master' into kgoto/add-handlebar 2025-05-16 11:03:44 +01:00
a3957273
411f78d27a Merge pull request #2011 from bartvanandel/feat/1216-1531-upgrade-uuid 2025-05-12 16:44:22 +01:00
a3957273
7ed7fca3ad Merge branch 'master' into feat/1216-1531-upgrade-uuid 2025-05-12 16:24:53 +01:00
a3957273
5d3353189b Merge pull request #2015 from bartvanandel/fix/use-default-index-in-option-transform 2025-05-12 16:24:35 +01:00
a3957273
f92238bf04 Merge pull request #2023 from xumptex/feature/add-BLAKE3 2025-05-12 16:14:00 +01:00
a3957273
2b1ceef6a3 Merge pull request #2025 from ericli-splunk/patch-1 2025-05-12 16:06:53 +01:00
a3957273
f4e73eef54 Merge pull request #1986 from Odyhibit/master 2025-05-12 16:05:42 +01:00
gchqdev364
d751117219 Merge pull request #2041 from gchq/octal-ip-addresses
Addresses bug report #2008
Added explicit support for octal IP addresses.
Changed approach to IPv4 regex to be string manipulation generated.
Added some unit tests for IP address parsing - probably not full coverage.
Added lookahead and lookbehind tricks to resolve warned issue that 1.2.3.256 would still be extracted as 1.2.3.25. Now only accepts valid IP addresses. Warning replaced with clause about infinite length dotted decimal forms.
2025-05-12 15:51:03 +01:00
a3957273
a55075fdb6 Merge pull request #2042 from Sma-Das/patch-1 2025-05-12 15:45:26 +01:00
Sma Das
a7443778b8 update: dockerfile
Updated Dockerfile to correctly build on ARM64 platforms. The previous version failed.
2025-05-12 10:22:22 -04:00
a3957273
06f1982acf Merge pull request #2038 from GCHQDeveloper94872/Issue-2036-Safari-load-bug 2025-05-12 15:04:14 +01:00
es45411
761173bce7 Merge pull request #2040 from gchq/feature/add-toggle-plus-to-urldecode
Add toggle "+" character to URLDecode operation
2025-05-12 14:42:15 +01:00
es45411
3a55b34214 Add tests for URLDecode and URLEncode 2025-05-12 13:09:59 +00:00
es45411
95d5fd1789 Add treat space as plus URLDecode option 2025-05-12 13:09:49 +00:00
GCHQDeveloper94872
f24bd92e34 Merge branch 'master' into Issue-2036-Safari-load-bug 2025-05-12 13:51:00 +01:00
GCHQDeveloper94872
e4f4d9c1c5 Workaround for Safari load bug 2025-05-12 12:15:41 +00:00
Josh Bloom
0a94c4a7c9 Merge branch 'master' into master 2025-04-22 23:33:28 -05:00
ericli-splunk
64355c9dde Fix email regex 2025-04-17 15:46:37 -07:00
Jeremy Giudicelli
bd8906f46b fix : add Categories and index 2025-04-17 13:40:01 +02:00
Jeremy Giudicelli
26d8fa51b0 fix : delete space and add new line 2025-04-17 13:27:45 +02:00
Jeremy Giudicelli
ad91099d5e feat : add Blake3 hashing
import hash-wasm to hash inputs in utf-8 with optional key also in utf-8 and outputs in Hex
2025-04-17 11:37:35 +02:00
Kendall Goto
361a3b2929 Add Template operation for basic JSON rendering 2025-04-09 15:54:39 -07:00
Bart van Andel
b9ed1fde62 fix: use defaultIndex instead of 0 in transformArgs 2025-04-05 23:01:52 +02:00
Bart van Andel
a0ade26505 fix: improve UUID description
- Link to uuid package at npmjs
- Briefly explain supported UUID versions for generation
2025-04-05 22:59:03 +02:00
Bart van Andel
575eade84d fix: simplify UUID argument names, use hint for more info 2025-04-05 22:51:59 +02:00
Bart van Andel
2cf3642493 fix: use UUID v4 by default, for backward compatibility 2025-04-05 22:49:54 +02:00
Bart van Andel
de5b03f854 chore: fix class name for analyse UUID operation 2025-04-04 10:24:46 +02:00
Bart van Andel
457d28fc55 feat: support UUID v1, v3, v4, v5, v6, v7
- Add v6 and v7 as valid options
- Add unit tests for generating and analyzing all supported versions
2025-04-04 10:24:46 +02:00
victorlpgazolli
e0bdde89c4 testing only class default value 2025-04-04 10:24:46 +02:00
victorlpgazolli
68db8e93b7 ref assert validation uuid tests 2025-04-04 10:24:46 +02:00
victorlpgazolli
9069d8e3ec ref uuid tests 2025-04-04 10:24:46 +02:00
victorlpgazolli
e146636250 add more uuid tests 2025-04-04 10:24:45 +02:00
victorlpgazolli
c0af2a9eb5 add analyse uuid category 2025-04-04 10:24:45 +02:00
victorlpgazolli
ae5648ede1 add analyse uuid version 2025-04-04 10:24:45 +02:00
victorlpgazolli
197d6735d2 ref generate uuid versions v1, v3, v4 and v5 2025-04-04 10:24:45 +02:00
victorlpgazolli
75b52bef6d add uuid dependency 2025-04-04 10:24:45 +02:00
Odyhibit
2c32a64bfe Remove trim from rail fence. 2025-02-21 20:40:37 -06:00
23 changed files with 563 additions and 117 deletions

View File

@@ -34,20 +34,20 @@ jobs:
if: success()
run: npx grunt prod
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Production Image Build
if: success()
id: build-image
uses: redhat-actions/buildah-build@v2
uses: docker/build-push-action@v6
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
platforms: linux/amd64,linux/arm64
- name: UI Tests
if: success()
run: |

View File

@@ -45,6 +45,12 @@ jobs:
sudo apt-get install xvfb
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Image Metadata
id: image-metadata
uses: docker/metadata-action@v4
@@ -55,30 +61,22 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{version}}
- name: Production Image Build
id: build-image
uses: redhat-actions/buildah-build@v2
- name: Log in to GHCR
uses: docker/login-action@v3
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
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: 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

View File

@@ -27,10 +27,6 @@ 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 TARGETARCH
ARG TARGETPLATFORM
FROM ${TARGETARCH}/nginx:stable-alpine AS cyberchef
FROM nginx:stable-alpine AS cyberchef
COPY --from=builder /app/build/prod /usr/share/nginx/html/

69
package-lock.json generated
View File

@@ -46,6 +46,8 @@
"file-saver": "^2.0.5",
"flat": "^6.0.1",
"geodesy": "1.1.3",
"handlebars": "^4.7.8",
"hash-wasm": "^4.12.0",
"highlight.js": "^11.9.0",
"ieee754": "^1.2.1",
"jimp": "^0.22.12",
@@ -95,6 +97,7 @@
"ua-parser-js": "^1.0.38",
"unorm": "^1.6.0",
"utf8": "^3.0.0",
"uuid": "^11.1.0",
"vkbeautify": "^0.99.3",
"xpath": "0.0.34",
"xregexp": "^5.1.1",
@@ -10843,6 +10846,27 @@
"dev": true,
"license": "MIT"
},
"node_modules/handlebars": {
"version": "4.7.8",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
"integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
"license": "MIT",
"dependencies": {
"minimist": "^1.2.5",
"neo-async": "^2.6.2",
"source-map": "^0.6.1",
"wordwrap": "^1.0.0"
},
"bin": {
"handlebars": "bin/handlebars"
},
"engines": {
"node": ">=0.4.7"
},
"optionalDependencies": {
"uglify-js": "^3.1.4"
}
},
"node_modules/has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
@@ -10941,6 +10965,11 @@
"node": ">= 0.10"
}
},
"node_modules/hash-wasm": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.12.0.tgz",
"integrity": "sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ=="
},
"node_modules/hash.js": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
@@ -13663,7 +13692,6 @@
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true,
"license": "MIT"
},
"node_modules/netmask": {
@@ -13885,6 +13913,15 @@
"node": ">=8"
}
},
"node_modules/nightwatch/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/nightwatch/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
@@ -16828,6 +16865,15 @@
"node": ">=0.8.0"
}
},
"node_modules/sockjs/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/socks": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz",
@@ -16868,7 +16914,6 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"devOptional": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -18115,13 +18160,15 @@
}
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"license": "MIT",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
"uuid": "dist/esm/bin/uuid"
}
},
"node_modules/v8flags": {
@@ -18886,6 +18933,12 @@
"node": ">=0.10.0"
}
},
"node_modules/wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
"license": "MIT"
},
"node_modules/worker-loader": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-3.0.8.tgz",

View File

@@ -132,6 +132,8 @@
"file-saver": "^2.0.5",
"flat": "^6.0.1",
"geodesy": "1.1.3",
"handlebars": "^4.7.8",
"hash-wasm": "^4.12.0",
"highlight.js": "^11.9.0",
"ieee754": "^1.2.1",
"jimp": "^0.22.12",
@@ -181,6 +183,7 @@
"ua-parser-js": "^1.0.38",
"unorm": "^1.6.0",
"utf8": "^3.0.0",
"uuid": "^11.1.0",
"vkbeautify": "^0.99.3",
"xpath": "0.0.34",
"xregexp": "^5.1.1",

View File

@@ -375,7 +375,8 @@
"Extract EXIF",
"Extract ID3",
"Extract Files",
"RAKE"
"RAKE",
"Template"
]
},
{
@@ -425,6 +426,7 @@
"Snefru",
"BLAKE2b",
"BLAKE2s",
"BLAKE3",
"GOST Hash",
"Streebog",
"SSDEEP",
@@ -549,6 +551,7 @@
"Pseudo-Random Number Generator",
"Generate De Bruijn Sequence",
"Generate UUID",
"Analyse UUID",
"Generate TOTP",
"Generate HOTP",
"Generate QR Code",

View File

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

View File

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

View File

@@ -51,7 +51,7 @@ class ExtractEmailAddresses extends Operation {
run(input, args) {
const [displayTotal, sort, unique] = args,
// email regex from: https://www.regextester.com/98066
regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}\])/ig;
regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\])/ig;
const results = search(
input,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -580,10 +580,25 @@ Password: 282760`;
assert.strictEqual(result.toString().substr(0, 37), "-----BEGIN PGP PRIVATE KEY BLOCK-----");
}),
it("Generate UUID", () => {
const result = chef.generateUUID();
assert.ok(result.toString());
assert.strictEqual(result.toString().length, 36);
...[1, 3, 4, 5, 6, 7].map(version => it(`Generate UUID v${version}`, () => {
const result = chef.generateUUID("", { "version": `v${version}` }).toString();
assert.ok(result);
assert.strictEqual(result.length, 36);
})),
...[1, 3, 4, 5, 6, 7].map(version => it(`Analyze UUID v${version}`, () => {
const uuid = chef.generateUUID("", { "version": `v${version}` }).toString();
const result = chef.analyseUUID(uuid).toString();
const expected = `UUID version: ${version}`;
assert.strictEqual(result, expected);
})),
it("Generate UUID using defaults", () => {
const uuid = chef.generateUUID();
assert.ok(uuid);
const analysis = chef.analyseUUID(uuid).toString();
assert.strictEqual(analysis, "UUID version: 4");
}),
it("Gzip, Gunzip", () => {

View File

@@ -29,6 +29,7 @@ import "./tests/BCD.mjs";
import "./tests/BitwiseOp.mjs";
import "./tests/BLAKE2b.mjs";
import "./tests/BLAKE2s.mjs";
import "./tests/BLAKE3.mjs";
import "./tests/Bombe.mjs";
import "./tests/BSON.mjs";
import "./tests/ByteRepr.mjs";
@@ -158,12 +159,14 @@ import "./tests/Subsection.mjs";
import "./tests/SwapCase.mjs";
import "./tests/SymmetricDifference.mjs";
import "./tests/TakeNthBytes.mjs";
import "./tests/Template.mjs";
import "./tests/TextEncodingBruteForce.mjs";
import "./tests/ToFromInsensitiveRegex.mjs";
import "./tests/TranslateDateTimeFormat.mjs";
import "./tests/Typex.mjs";
import "./tests/UnescapeString.mjs";
import "./tests/Unicode.mjs";
import "./tests/URLEncodeDecode.mjs";
import "./tests/RSA.mjs";
import "./tests/CBOREncode.mjs";
import "./tests/CBORDecode.mjs";

View File

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

View File

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

View File

@@ -129,38 +129,5 @@ TestRegister.addTests([
},
],
},
{
name: "ExtractIPAddress IPv6 full form",
input: "This 2001:0db8:0001:0000:0000:0ab9:C0A8:0102 is a valid address.",
expectedOutput: "2001:0db8:0001:0000:0000:0ab9:C0A8:0102",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
{
name: "ExtractIPAddress IPv6 short form",
input: "Another valid style is the short form 2001:db8:1::ab9:C0A8:102 is a valid address.",
expectedOutput: "2001:db8:1::ab9:C0A8:102",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
{
name: "ExtractIPAddress IPv6 both forms",
input: "2001:0db8:0001:0000:0000:0ab9:C0A8:0102 can be compressed as follows: 2001:db8:1::ab9:C0A8:102.",
expectedOutput: "2001:0db8:0001:0000:0000:0ab9:C0A8:0102\n2001:db8:1::ab9:C0A8:102",
recipeConfig: [
{
"op": "Extract IP addresses",
"args": [true, true, false, false, false, false]
},
],
},
]);

View File

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

View File

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