From ccd717826e32f0a88758f4666841e485d12022a9 Mon Sep 17 00:00:00 2001 From: David Perry Date: Fri, 29 May 2020 15:55:12 -0400 Subject: [PATCH 1/5] Add /operations endpoint to list available operations This is a straightforward change to add a `/operations` endpoint which can be used to get a list of the available operations on the CyberChef server. The returned object has an attribute for each operation name, with the argument description as its value. This change also documents this new endpoint in `README.md` and `swagger.yml`. --- README.md | 54 +++++++++++++++++++++++++++++++++++++++++++- app.js | 2 ++ routes/operations.js | 16 +++++++++++++ swagger.yml | 37 +++++++++++++++++++++++++++++- 4 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 routes/operations.js diff --git a/README.md b/README.md index bf85898..f8d1207 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,11 @@ A Docker image can be built, then run by doing the following: ## API overview > For full documentation of the API, you can find the swagger page hosted at the root url. See [Installing](#Installing) to run the application and browse the docs. -The server has two endpoints: `/bake` and `/magic`. +The server has these endpoints: + +* [`/bake`](#user-content-bake) +* [`/magic`](#user-content-magic) +* [`/operations`](#user-content-operations) ### `/bake` @@ -213,6 +217,54 @@ Response: ``` +### `/operations` + +This endpoint accepts a GET request and responds with a JSON object listing the available operations on this server. Each operation name is one attribute, and its value is the description of the arguments it can take. + +#### Example: operation list +Response: +```javascript +{ + "FromBase64" : { + "alphabet" : { + "type" : "editableOption", + "options" : [ + { + "name" : "Standard (RFC 4648): A-Za-z0-9+/=", + "value" : "A-Za-z0-9+/=" + }, + { + "name" : "URL safe (RFC 4648 §5): A-Za-z0-9-_", + "value" : "A-Za-z0-9-_" + }, + [...] + ] + }, + "removeNon-alphabetChars" : { + "type" : "boolean", + "value" : true + } + }, + "ToBase64" : { + "alphabet" : { + "type" : "editableOption", + "options" : [ + { + "name" : "Standard (RFC 4648): A-Za-z0-9+/=", + "value" : "A-Za-z0-9+/=" + }, + { + "name" : "URL safe (RFC 4648 §5): A-Za-z0-9-_", + "value" : "A-Za-z0-9-_" + }, + [...] + ] + } + }, + [...] +} +``` + ## Licencing CyberChef-server is released under the [Apache 2.0 Licence](https://www.apache.org/licenses/LICENSE-2.0) and is covered by [Crown Copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/copyright-and-re-use/crown-copyright/). diff --git a/app.js b/app.js index 5d155d5..a2f447d 100644 --- a/app.js +++ b/app.js @@ -11,6 +11,7 @@ import helmet from "helmet"; import bakeRouter from "./routes/bake"; import magicRouter from "./routes/magic"; +import operationsRouter from "./routes/operations"; const app = express(); app.disable("x-powered-by"); @@ -39,6 +40,7 @@ const swaggerFile = fs.readFileSync("./swagger.yml", "utf8"); // Routes app.use("/bake", bakeRouter); app.use("/magic", magicRouter); +app.use("/operations", operationsRouter); // Default route diff --git a/routes/operations.js b/routes/operations.js new file mode 100644 index 0000000..7064c7b --- /dev/null +++ b/routes/operations.js @@ -0,0 +1,16 @@ +import { Router } from "express"; +const router = Router(); +import { operations } from "cyberchef/src/node/index.mjs"; + +/** + * operationsGet + */ +router.get("/", async (req, res, next) => { + const ret = {}; + for (let op of operations) { + if (op.opName) ret[op.opName] = op.args; + } + res.send(ret); +}); + +export default router; diff --git a/swagger.yml b/swagger.yml index a790dae..50329b7 100644 --- a/swagger.yml +++ b/swagger.yml @@ -6,6 +6,42 @@ info: paths: + /operations: + get: + summary: List available operations + description: > + Retrieve a list of all available operations on this server. + The return value is an object whose attributes are operation names, + and each attribute value is the description of the arguments + the argument takes. + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + example: + FromBase64: + alphabet: + type: "editableOption" + options: + - name: "Standard (RFC 4648): A-Za-z0-9+/=" + value: "A-Za-z0-9+/=" + - name: "URL safe (RFC 4648 §5): A-Za-z0-9-_" + value: "A-Za-z0-9-_" + removeNon-alphabetChars: + type: boolean + value: true + ToBase64: + alphabet: + type: "editableOption" + options: + - name: "Standard (RFC 4648): A-Za-z0-9+/=" + value: "A-Za-z0-9+/=" + - name: "URL safe (RFC 4648 §5): A-Za-z0-9-_" + value: "A-Za-z0-9-_" + /bake: post: summary: Bakes a recipe @@ -189,4 +225,3 @@ components: items: type: Object useful: bool - \ No newline at end of file From d0d45ef36260b1582e4619b37f9ea386ef790845 Mon Sep 17 00:00:00 2001 From: David Perry Date: Fri, 29 May 2020 16:01:19 -0400 Subject: [PATCH 2/5] Ran eslint --- routes/operations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/operations.js b/routes/operations.js index 7064c7b..ff0fc1c 100644 --- a/routes/operations.js +++ b/routes/operations.js @@ -7,7 +7,7 @@ import { operations } from "cyberchef/src/node/index.mjs"; */ router.get("/", async (req, res, next) => { const ret = {}; - for (let op of operations) { + for (const op of operations) { if (op.opName) ret[op.opName] = op.args; } res.send(ret); From 2137819b9d979449b7795738b37958acde917aea Mon Sep 17 00:00:00 2001 From: David Perry Date: Fri, 26 Jun 2020 11:14:30 -0400 Subject: [PATCH 3/5] Use `function` instead of `->` --- routes/operations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/operations.js b/routes/operations.js index ff0fc1c..f18370e 100644 --- a/routes/operations.js +++ b/routes/operations.js @@ -5,7 +5,7 @@ import { operations } from "cyberchef/src/node/index.mjs"; /** * operationsGet */ -router.get("/", async (req, res, next) => { +router.get("/", async function (req, res, next) { const ret = {}; for (const op of operations) { if (op.opName) ret[op.opName] = op.args; From 3f1ea7f8c0d0419fdce74190f54b01b5f487bf85 Mon Sep 17 00:00:00 2001 From: David Perry Date: Fri, 26 Jun 2020 13:13:40 -0400 Subject: [PATCH 4/5] Some simple tests for `/operations` --- test/operations.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 test/operations.js diff --git a/test/operations.js b/test/operations.js new file mode 100644 index 0000000..356eb8d --- /dev/null +++ b/test/operations.js @@ -0,0 +1,23 @@ +import assert from "assert"; +import request from "supertest"; +import app from "../app"; + +describe("GET /operations", function() { + it("exists", (done) => { + request(app) + .get("/operations") + .expect(200, done); + }); + + it("contains some operations", (done) => { + request(app) + .get("/operations") + .expect(200) + .end(function(err, res) { + if(err) return done(err); + assert.notStrictEqual(res.body.ToBraille, undefined); + assert.notStrictEqual(res.body.ToBase, undefined); + done(); + }); + }); +}); From 2812b5c8a38cf4947fb394204dbe99e873f3078f Mon Sep 17 00:00:00 2001 From: David Perry Date: Fri, 26 Jun 2020 13:21:46 -0400 Subject: [PATCH 5/5] Fix lint error --- test/operations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/operations.js b/test/operations.js index 356eb8d..5ef67bd 100644 --- a/test/operations.js +++ b/test/operations.js @@ -14,7 +14,7 @@ describe("GET /operations", function() { .get("/operations") .expect(200) .end(function(err, res) { - if(err) return done(err); + if (err) return done(err); assert.notStrictEqual(res.body.ToBraille, undefined); assert.notStrictEqual(res.body.ToBase, undefined); done();