diff --git a/packages/controller/src/Controller.ts b/packages/controller/src/Controller.ts index 75a0e084..4d94f3af 100644 --- a/packages/controller/src/Controller.ts +++ b/packages/controller/src/Controller.ts @@ -409,7 +409,7 @@ export default class Controller { method: req.method, url: req.originalUrl, httpVersion: req.httpVersion, - headers: req.headers, + headers: Controller.loggableHeaders(req.headers), query: req.query, }, }); @@ -897,7 +897,7 @@ export default class Controller { statusCode: res.statusCode, responseTime: durationMs, httpVersion: req.httpVersion, - headers: req.headers, + headers: Controller.loggableHeaders(req.headers), query: req.query, }, }); @@ -1226,6 +1226,22 @@ export default class Controller { return url; } + /** + * Copy of request headers with credentials masked for logging + * + * @param headers - headers of the incoming request. + * @returns headers safe to write to the log. + */ + static loggableHeaders(headers: http.IncomingHttpHeaders) { + const result = { ...headers }; + for (const name of ["authorization", "cookie", "proxy-authorization", "x-access-token"]) { + if (result[name] !== undefined) { + result[name] = "[redacted]"; + } + } + return result; + } + /** * Serves the web interface with the root path set appropriately * diff --git a/test/controller/Controller.js b/test/controller/Controller.js index efcc69ee..26b80ba1 100644 --- a/test/controller/Controller.js +++ b/test/controller/Controller.js @@ -86,6 +86,24 @@ describe("controller/src/Controller", function() { await assert.rejects(Controller.loadJsonObject(path.join(dir, "invalid.json")), SyntaxError); }); }); + describe(".loggableHeaders()", function() { + it("should redact credentials and keep other headers", function() { + const headers = { + "authorization": "Bearer secret", + "x-access-token": "secret", + "cookie": "a=secret", + "user-agent": "test", + }; + assert.deepEqual(Controller.loggableHeaders(headers), { + "authorization": "[redacted]", + "x-access-token": "[redacted]", + "cookie": "[redacted]", + "user-agent": "test", + }); + assert.equal(headers["x-access-token"], "secret"); + }); + }); + describe(".serveWeb()", function() { let server, port; before(async function() {