Skip to content

Commit ad2e3aa

Browse files
Merge pull request #74 from gchq/release/1.1.0
Release 1.1.0 Adds Batch Baking endpoint to more efficiently process large amounts of data in a single request
2 parents 8bce1a0 + f67a4fb commit ad2e3aa

11 files changed

Lines changed: 602 additions & 34 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ All major and minor version changes will be documented in this file. Details of
1313

1414
## Details
1515

16-
### [1.0.0-beta] - 2024-10-04
16+
### [1.1.0] - 2025-02-26
17+
- Added Batch Baking functionality
18+
- Fixed some typos in Readme
19+
- Improved Swagger spec
20+
21+
### [1.0.0] - 2024-10-04
1722
- CyberChef functionality brought in line with the mainline release
1823
- Upgraded to use ESM modules throughout
1924
- Works with recent versions of NodeJS

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
FROM node:18.20.4-alpine
2-
LABEL author "Wes Lambert, wlambertts@gmail.com"
2+
LABEL author="Wes Lambert, wlambertts@gmail.com"
33
LABEL description="Dockerised version of Cyberchef server (https://github.com/gchq/CyberChef-server)"
4-
LABEL copyright "Crown Copyright 2020"
5-
LABEL license "Apache-2.0"
4+
LABEL copyright="Crown Copyright 2020"
5+
LABEL license="Apache-2.0"
66
COPY . /CyberChef-server
77
WORKDIR /CyberChef-server
88
RUN npm cache clean --force && \

README.md

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ After using [CyberChef](https://gchq.github.io/CyberChef/) to experiment and fin
3030

3131

3232
## Installing
33-
While the instructions below are straightforward, it's worth understanding what happens under the hood as quirks in the installation process can cause issues with upgrades etc. The upstream CyberChef library uses a postinstall script to manipulate some dependenciesafter installation. That postinstal script doesn't work when install CC as a dependency into another project, so we now have our own postinstall process to install the CyberChef library (hence it does not appear in the package.json dependency list).
33+
While the instructions below are straightforward, it's worth understanding what happens under the hood as quirks in the installation process can cause issues with upgrades etc. The upstream CyberChef library uses a postinstall script to manipulate some dependencies after installation. That postinstall script doesn't work when installing CC as a dependency into another project, so we now have our own postinstall process to install the CyberChef library (hence it does not appear in the package.json dependency list).
3434

3535
This process can cause problems when updating libs, so the recommended approach is to remove node_modules and package-lock.json and install from scratch.
3636

@@ -170,6 +170,92 @@ Response:
170170
}
171171
```
172172

173+
### `/batch/bake`
174+
175+
`/batch/bake` allows a user to POST multiple input values and a configuration for a CyberChef Recipe. The application will run each elemnt of the input through the recipe and return the results as an array of output objects.
176+
177+
This endpoint accepts a POST request with the following body:
178+
179+
|Parameter|Type|Description|
180+
|---|---|---|
181+
input|Array|The input data for the recipe. Currently accepts an array of Strings.
182+
recipe|String or Object or Array|One or more operations, with optional arguments. Uses default arguments if they're not defined here.
183+
outputType (optional)|String|The [Data Type](https://github.com/gchq/CyberChef/wiki/Adding-a-new-operation#data-types) that you would like the result of the bakes to be returned as. This will not work with `File` or `List<File>` at the moment.
184+
185+
#### Example: one operation, default arguments
186+
```javascript
187+
{
188+
"input": ["One", "two", "three", "four"],
189+
"recipe": "to decimal"
190+
}
191+
```
192+
193+
Response:
194+
```javascript
195+
[
196+
{
197+
"success": true,
198+
"value": "79 110 101",
199+
"type": "string"
200+
},
201+
{
202+
"success": true,
203+
"value": "116 119 111",
204+
"type": "string"
205+
},
206+
{
207+
"success": true,
208+
"value": "116 104 114 101 101",
209+
"type": "string"
210+
},
211+
{
212+
"success": true,
213+
"value": "102 111 117 114",
214+
"type": "string"
215+
}
216+
]
217+
218+
```
219+
> For more information on how operation names are handled, see the [Node API docs](https://github.com/gchq/CyberChef/wiki/Node-API#operation-names)
220+
221+
#### Example: one operation, non-default arguments by name
222+
```javascript
223+
{
224+
"input": ["One", "two", "three", "four"],
225+
"recipe": {
226+
"op": "to decimal",
227+
"args": {
228+
"delimiter": "Colon"
229+
}
230+
}
231+
}
232+
```
233+
Response:
234+
```javascript
235+
[
236+
{
237+
"success": true,
238+
"value": "79:110:101",
239+
"type": "string"
240+
},
241+
{
242+
"success": true,
243+
"value": "116:119:111",
244+
"type": "string"
245+
},
246+
{
247+
"success": true,
248+
"value": "116:104:114:101:101",
249+
"type": "string"
250+
},
251+
{
252+
"success": true,
253+
"value": "102:111:117:114",
254+
"type": "string"
255+
}
256+
]
257+
```
258+
173259
### `/magic`
174260

175261
[Find more information about what the Magic operation does here](https://github.com/gchq/CyberChef/wiki/Automatic-detection-of-encoded-data-using-CyberChef-Magic)

package-lock.json

Lines changed: 43 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cyberchef-server",
3-
"version": "1.0.0-beta",
3+
"version": "1.1.0",
44
"description": "An application providing API access to CyberChef",
55
"author": "d98762625 <d98762625@gmail.com>",
66
"license": "Apache-2.0",

src/app.mjs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import helmet from "helmet";
1313
import bakeRouter from "./routes/bake.mjs";
1414
import magicRouter from "./routes/magic.mjs";
1515
import healthRouter from "./routes/health.mjs";
16+
import batchBakeRouter from "./routes/batchBake.mjs";
17+
18+
const isProduction = process.env.NODE_ENV === "production";
1619

1720
const app = express();
1821
app.disable("x-powered-by");
@@ -22,7 +25,7 @@ app.use(cors({
2225
origin: "*"
2326
}));
2427

25-
if (process.env.NODE_ENV === "production") {
28+
if (isProduction) {
2629
app.use(helmet({ crossOriginResourcePolicy: { policy: "cross-origin" } }));
2730
app.use(logger({
2831
level: "error",
@@ -45,6 +48,8 @@ const swaggerFile = fs.readFileSync("./swagger.yml", "utf8");
4548
app.use("/health", healthRouter);
4649
app.use("/bake", bakeRouter);
4750
app.use("/magic", magicRouter);
51+
// Batch routes
52+
app.use("/batch/bake", batchBakeRouter);
4853

4954

5055
// Default route

src/lib/errorHandler.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default function errorHandler(err, req, res, next) {
2424
) {
2525
res.status(400).send(err.message).end();
2626
} else {
27-
res.status(500).send(err.stack).end();
27+
req.log.error(err.stack);
28+
res.status(500).send("Internal Server Error").end();
2829
}
2930
}

src/routes/bake.mjs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,9 @@ import { bake, Dish } from "cyberchef";
77
*/
88
router.post("/", async function bakePost(req, res, next) {
99
try {
10-
if (!req.body.input) {
11-
throw new TypeError("'input' property is required in request body");
12-
}
13-
14-
if (!req.body.recipe) {
15-
throw new TypeError("'recipe' property is required in request body");
10+
const noRecipeOrInput = !req.body.input || !req.body.recipe;
11+
if (noRecipeOrInput) {
12+
throw new TypeError(`'${!req.body.input ? "input" : "recipe"}' property is required in request body`);
1613
}
1714

1815
const dish = await bake(req.body.input, req.body.recipe);

src/routes/batchBake.mjs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Router } from "express";
2+
const router = Router();
3+
import { bake, Dish } from "cyberchef";
4+
5+
/**
6+
* batchBakePost
7+
*/
8+
router.post("/", async function batchBakePost(req, res, next) {
9+
try {
10+
if (!req.body.input || !Array.isArray(req.body.input)) {
11+
throw new TypeError("'input' property of type 'Array' is required in request body");
12+
}
13+
14+
if (req.body.input.length === 0) {
15+
throw new TypeError("'input' array must be non-empty");
16+
}
17+
18+
if (!req.body.recipe) {
19+
throw new TypeError("'recipe' property is required in request body");
20+
}
21+
22+
// Results are objects with a result param if the result of the bake on the data
23+
// item was successful and and error param if not that contains the string exception
24+
// Result also contains the type, in line with the single bake endpoint
25+
const retArr = req.body.input.map((input) => {
26+
try {
27+
const dish = bake(input, req.body.recipe);
28+
let retVal = dish.value;
29+
// Attempt to translate to another type. Any translation errors
30+
// propagate through to the errorHandler.
31+
if (req.body.outputType) {
32+
retVal = dish.get(req.body.outputType);
33+
}
34+
return {
35+
success: true,
36+
value: retVal,
37+
type: Dish.enumLookup(dish.type),
38+
};
39+
} catch (err) {
40+
// Chef uses TypeError to indicate a problem with recipes. Safe to assume that a bad recipe
41+
// will be a showstopper for all data so use the global error handler to handle this.
42+
if (err instanceof TypeError) {
43+
req.log.error("Bad recipe");
44+
// Rethrow to activate the outer try/catch and exit the handling of this request
45+
throw err;
46+
}
47+
return {
48+
success: false,
49+
error: err.message,
50+
};
51+
}
52+
});
53+
54+
res.send(retArr);
55+
56+
} catch (e) {
57+
next(e);
58+
}
59+
});
60+
61+
export default router;

0 commit comments

Comments
 (0)