diff --git a/lib/req.js b/lib/req.js index 4a58034..e865ea4 100644 --- a/lib/req.js +++ b/lib/req.js @@ -5,6 +5,25 @@ module.exports = { reqSerializer } +const fastRedact = require('fast-redact') + +const headerRedact = fastRedact({ + paths: [ + 'cookie', + 'referer', + 'location', + 'geolocation', + 'authorization', + '["x-real-ip"]', + '["user-agent"]', + '["x-forwarded-for"]', + '["proxy-authorization"]' + ], + serialize: (val) => typeof structuredClone === 'function' + ? structuredClone(val) + : JSON.parse(JSON.stringify(val)) +}) + const rawSymbol = Symbol('pino-raw-req-ref') const pinoReqProto = Object.create({}, { id: { @@ -85,7 +104,7 @@ function reqSerializer (req) { _req.params = req.params } - _req.headers = req.headers + _req.headers = req.headers && typeof req.headers === 'object' ? headerRedact(req.headers) : req.headers _req.remoteAddress = connection && connection.remoteAddress _req.remotePort = connection && connection.remotePort // req.raw is for hapi compat/equivalence diff --git a/lib/res.js b/lib/res.js index e48004b..1f40672 100644 --- a/lib/res.js +++ b/lib/res.js @@ -5,6 +5,19 @@ module.exports = { resSerializer } +const fastRedact = require('fast-redact') + +const headerRedact = fastRedact({ + paths: [ + 'location', + '["x-real-ip"]', + '["x-forwarded-for"]' + ], + serialize: (val) => typeof structuredClone === 'function' + ? structuredClone(val) + : JSON.parse(JSON.stringify(val)) +}) + const rawSymbol = Symbol('pino-raw-res-ref') const pinoResProto = Object.create({}, { statusCode: { @@ -35,7 +48,10 @@ Object.defineProperty(pinoResProto, rawSymbol, { function resSerializer (res) { const _res = Object.create(pinoResProto) _res.statusCode = res.headersSent ? res.statusCode : null - _res.headers = res.getHeaders ? res.getHeaders() : res._headers + + const _headers = res.getHeaders ? res.getHeaders() : res._headers + _res.headers = _headers && typeof _headers === 'object' ? headerRedact(_headers) : _headers + _res.raw = res return _res } diff --git a/package.json b/package.json index ec1d860..6e4b34d 100644 --- a/package.json +++ b/package.json @@ -43,5 +43,8 @@ }, "tsd": { "directory": "test/types" + }, + "dependencies": { + "fast-redact": "^3.5.0" } } diff --git a/test/req.test.js b/test/req.test.js index d8a6486..110d9f3 100644 --- a/test/req.test.js +++ b/test/req.test.js @@ -475,3 +475,31 @@ test('req.params is available', async (t) => { await p.completed }) + +test('req.headers\' certain keys are redacted', async (t) => { + const p = tspl(t, { plan: 3 }) + + const server = http.createServer(handler) + server.unref() + server.listen(0, () => { + http.get({ + ...server.address(), + headers: { + custom: 'y', + 'x-forwarded-for': 'y' + } + }, () => {}) + }) + + t.after(() => server.close()) + + function handler (req, res) { + const serialized = serializers.reqSerializer(req) + p.strictEqual(serialized.headers.custom, 'y') + p.strictEqual(serialized.headers['x-forwarded-for'], '[REDACTED]') + p.strictEqual(req.headers['x-forwarded-for'], 'y') + res.end() + } + + await p.completed +}) diff --git a/test/res.test.js b/test/res.test.js index 638afaf..974a51d 100644 --- a/test/res.test.js +++ b/test/res.test.js @@ -118,3 +118,27 @@ test('req.url will be obtained from input request url when input request url is await p.completed }) + +test('res.headers\' certain keys are redacted', async (t) => { + const p = tspl(t, { plan: 3 }) + + const server = http.createServer(handler) + server.unref() + server.listen(0, () => { + http.get(server.address(), () => {}) + }) + + t.after(() => server.close()) + + function handler (_req, res) { + res.setHeader('custom', 'y') + res.setHeader('x-forwarded-for', 'y') + const serialized = serializers.resSerializer(res) + p.strictEqual(serialized.headers.custom, 'y') + p.strictEqual(serialized.headers['x-forwarded-for'], '[REDACTED]') + p.strictEqual(res.getHeaders()['x-forwarded-for'], 'y') + res.end() + } + + await p.completed +})