Skip to content

Commit 583b16b

Browse files
committed
bake file: recipe must be file, add more test cases
1 parent 83d32d3 commit 583b16b

15 files changed

+251
-42
lines changed

app.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@ const app = express();
1616
app.disable("x-powered-by");
1717

1818

19-
if (process.env.NODE_ENV === "production" || process.env.TEST_LOGGING) {
19+
if (process.env.DEBUG) {
2020
app.use(pino({
21-
level: "warn",
22-
prettyPrint: true,
21+
level: "debug",
22+
prettyPrint: true
2323
}));
24-
app.use(helmet());
2524
} else {
2625
app.use(pino({
27-
level: "debug",
28-
prettyPrint: true
26+
level: "warn",
27+
prettyPrint: true,
2928
}));
29+
app.use(helmet());
3030
}
3131

3232
app.use(express.json());

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
"scripts": {
2727
"nodemon": "nodemon",
2828
"start": "DEBUG=cyberchef-server:server node -r esm ./bin/www",
29-
"test": "TEST_LOGGING=true mocha -r esm",
30-
"prod": "NODE_ENV=production node -r esm ./bin/www",
29+
"test": "mocha -r esm",
30+
"prod": "node -r esm ./bin/www",
3131
"lint": "./node_modules/.bin/eslint --fix .",
3232
"lint-test": "./node_modules/.bin/eslint ."
3333
},

routes/bake.js

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
import fs from "fs";
2+
import { promisify } from "util";
3+
14
import { Router } from "express";
25
import formidable from "formidable";
3-
const router = Router();
6+
47
import { bake, Dish } from "cyberchef/src/node/index.mjs";
8+
import { type } from "os";
9+
10+
const router = Router();
11+
const readFile = promisify(fs.readFile);
512

613
/**
714
* bakePost
@@ -16,54 +23,65 @@ router.post("/", async function bakePost(req, res, next) {
1623

1724
async function bakeMultipartForm(req, res, next) {
1825
const form = formidable();
26+
form.parse(req, async (err, fields, files) => {
27+
try {
1928

20-
try {
21-
22-
form.parse(req, async (err, fields, files) => {
23-
24-
// req.log.warn(`Recipe: ${fields.recipe}`);
25-
// req.log.warn(`Input: ${files.input}`);
26-
// req.log.warn(`Other input: ${fields.input}`);
2729
if (err) {
2830
throw err;
2931
}
3032

31-
if (!('recipe' in fields)) {
32-
throw new Error("Could not find required 'recipe' field in multipart form data");
33+
if (!('recipe' in files)) {
34+
throw new TypeError("Could not find required 'recipe' attachment in multipart form data");
35+
}
36+
37+
let recipe;
38+
try {
39+
const fileContents = await readFile(files.recipe.path);
40+
recipe = JSON.parse(fileContents);
41+
} catch (e) {
42+
throw new TypeError(`Could not parse recipe file: ${e}`);
3343
}
3444

3545
let dish;
3646

37-
// Case: data is in files.input
3847
if('input' in files) {
39-
// read the contents of the file and use it as an input
40-
res.json({ fields, files });
41-
} else if ('input' in fields) {
48+
const input = await readFile(files.input.path)
49+
dish = await bake(input, recipe);
4250

43-
dish = await bake(fields.input, fields.recipe);
51+
} else if ('input' in fields) {
52+
dish = await bake(fields.input, recipe);
4453

4554
} else {
46-
throw new Error("Could not find 'input' field in multipart form data.");
47-
}
48-
49-
// Attempt to translate to another type. Any translation errors
50-
// propagate through to the errorHandler.
51-
if ('outputType' in fields) {
52-
dish.get(req.body.outputType);
55+
throw new TypeError("Could not find 'input' field in multipart form data.");
5356
}
5457

5558
if (dish) {
59+
60+
if ('outputType' in fields) {
61+
// dish.get takes a typeEnum
62+
let typeEnum = parseInt(fields.outputType);
63+
if (isNaN(typeEnum)) {
64+
typeEnum = Dish.typeEnum(fields.outputType)
65+
}
66+
dish.get(typeEnum);
67+
68+
// Browser should handle files as a download
69+
if (typeEnum === Dish.FILE) {
70+
res.set("Content-Disposition", "attachment");
71+
}
72+
}
73+
5674
res.send({
5775
value: dish.value,
5876
type: Dish.enumLookup(dish.type),
5977
});
6078
}
6179

62-
});
80+
} catch (e) {
81+
next(e);
82+
}
6383

64-
} catch (e) {
65-
next(e);
66-
}
84+
});
6785
}
6886

6987
async function bakeBody(req, res, next) {

test/bake.js

Lines changed: 158 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import assert from "assert";
2+
13
import request from "supertest";
4+
25
import app from "../app";
36

7+
let testDataPath = "test/test_data"
8+
49

510
describe("GET /bake", function() {
611
it("doesnt exist", function(done) {
@@ -210,26 +215,64 @@ describe("POST /bake", function() {
210215
});
211216

212217
describe("POST /bake files", function () {
213-
it("should take input as a multipart file upload", (done) => {
218+
it("should complain if you do not send a recipe with your input", (done) => {
214219
request(app)
215220
.post("/bake")
216-
.field("recipe", "from hex")
217-
.attach("input", "test/test-hex-input.txt")
218-
.expect(200, done);
221+
.attach("input", `${testDataPath}/hex_input.txt`)
222+
.expect(400, done);
223+
});
224+
225+
it("should complain if it cannot find input field in form", (done) => {
226+
request(app)
227+
.post("/bake")
228+
.attach("recipe", `${testDataPath}/recipe_single_op.json`)
229+
.expect(400, done);
230+
});
231+
232+
it("should complain if it cannot find a matching operation - single op recipe - input field", (done) => {
233+
request(app)
234+
.post("/bake")
235+
.attach("recipe", `${testDataPath}/recipe_single_op_invalid.json`)
236+
.field("input", "testing - one, two, three")
237+
.expect(400, done);
238+
});
239+
240+
it("should complain if it cannot find a matching operation - multi-op recipe - input field", (done) => {
241+
request(app)
242+
.post("/bake")
243+
.attach("recipe", `${testDataPath}/recipe_multi_op_invalid.json`)
244+
.field("input", "testing - one, two, three")
245+
.expect(400, done);
246+
});
247+
248+
it("should complain if the recipe file is not valid JSON", (done) => {
249+
request(app)
250+
.post("/bake")
251+
.attach("recipe", `${testDataPath}/recipe_invalid_json.json`)
252+
.field("input", "testing - one, two, three")
253+
.expect(400, done);
219254
});
220255

221256
it("should take input as a multipart field", (done) => {
222257
request(app)
223258
.post("/bake")
224-
.field("recipe", "to morse code")
259+
.attach("recipe", `${testDataPath}/recipe_single_op.json`)
225260
.field("input", "The crowds stared around wildly")
226261
.expect(200, done);
227262
});
228263

229-
it("should perform a simple recipe with input as a form field", (done) => {
264+
it("should take input as a multipart file upload", (done) => {
265+
request(app)
266+
.post("/bake")
267+
.attach("recipe", `${testDataPath}/recipe_single_op.json`)
268+
.attach("input", `${testDataPath}/hex_input.txt`)
269+
.expect(200, done);
270+
});
271+
272+
it("should perform a simple recipe with input as a field", (done) => {
230273
request(app)
231274
.post("/bake")
232-
.field("recipe", "to morse code")
275+
.attach("recipe", `${testDataPath}/recipe_to_morse.json`)
233276
.field("input", "The crowds stared around wildly")
234277
.expect(200)
235278
.expect({
@@ -240,5 +283,112 @@ describe("POST /bake files", function () {
240283
.-- .. .-.. -.. .-.. -.--`,
241284
type: "string",
242285
}, done);
243-
})
286+
});
287+
288+
it("should perform a simple recipe with input as a file", (done) => {
289+
request(app)
290+
.post("/bake")
291+
.attach("recipe", `${testDataPath}/recipe_to_morse.json`)
292+
.attach("input", `${testDataPath}/text_input.txt`)
293+
.expect(200)
294+
.expect({
295+
value: `- .... .
296+
-.-. .-. --- .-- -.. ...
297+
... - .- .-. . -..
298+
.- .-. --- ..- -. -..
299+
.-- .. .-.. -.. .-.. -.--`,
300+
type: "string",
301+
}, done);
302+
});
303+
304+
it("should bake a multi-op recipe with arguments", (done) => {
305+
request(app)
306+
.post("/bake")
307+
.attach("recipe", `${testDataPath}/recipe_multi_op_with_args.json`)
308+
.field("input", "some input")
309+
.expect(200)
310+
.expect({
311+
value: "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something",
312+
type: "string",
313+
}, done);
314+
});
315+
316+
it("should handle image files as input", (done) => {
317+
request(app)
318+
.post("/bake")
319+
.attach("recipe", `${testDataPath}/recipe_detect_file_type.json`)
320+
.attach("input", `${testDataPath}/chef.png`)
321+
.expect(200)
322+
.expect({
323+
type: "string",
324+
value: `File type: Portable Network Graphics image
325+
Extension: png
326+
MIME type: image/png
327+
`
328+
}, done);
329+
});
330+
331+
it("should optionally transform the output to outputType", (done) => {
332+
request(app)
333+
.post("/bake")
334+
.attach("recipe", `${testDataPath}/recipe_take_bytes.json`)
335+
.attach("input", `${testDataPath}/text_input.txt`)
336+
.field("outputType", "string")
337+
.expect(200)
338+
.expect({
339+
type: "string",
340+
value: "The c"
341+
}, done);
342+
});
343+
344+
it("should optionally transform the output to outputType, when outputType is an enum", (done) => {
345+
request(app)
346+
.post("/bake")
347+
.attach("recipe", `${testDataPath}/recipe_take_bytes.json`)
348+
.attach("input", `${testDataPath}/text_input.txt`)
349+
.field("outputType", 1)
350+
.expect(200)
351+
.expect({
352+
type: "string",
353+
value: "The c"
354+
}, done);
355+
});
356+
357+
it("should set content-disposition header if File outputType is requested", (done) => {
358+
request(app)
359+
.post("/bake")
360+
.attach("recipe", `${testDataPath}/recipe_take_bytes.json`)
361+
.attach("input", `${testDataPath}/text_input.txt`)
362+
.field("outputType", "file")
363+
.expect(200)
364+
.expect("Content-Disposition", "attachment")
365+
.end((err, res) => {
366+
if (err) return done(err);
367+
assert.deepEqual(res.body.type, "File");
368+
assert.deepEqual(res.body.value.name, "unknown");
369+
assert.deepEqual(res.body.value.data.data, [84, 104, 101, 32, 99]);
370+
return done();
371+
});
372+
// /**
373+
// * Not sure this is how we want to present this. Need to validate with
374+
// * web page response.
375+
// */
376+
// type: "File",
377+
// value: {
378+
// data: {
379+
// data: [
380+
// 84,
381+
// 104,
382+
// 101,
383+
// 32,
384+
// 99,
385+
// ],
386+
// type: "Buffer"
387+
// },
388+
// lastModified: 1593784118511,
389+
// name: "unknown",
390+
// type: "application/unknown",
391+
// }
392+
});
393+
244394
});

test/test_data/chef.png

1.13 KB
Loading
File renamed without changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[
2+
"detect file type"
3+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ 1234567
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[
2+
"to hex",
3+
"not a valid operation"
4+
]
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"op": "To Morse Code",
4+
"args": [
5+
"Dash/Dot",
6+
"Backslash",
7+
"Comma"
8+
]
9+
},
10+
{
11+
"op": "Hex to PEM",
12+
"args": [
13+
"SOMETHING"
14+
]
15+
},
16+
{
17+
"op": "To Snake case",
18+
"args": [
19+
false
20+
]
21+
}
22+
]

0 commit comments

Comments
 (0)