From 6d829b69f3731155b01a588558d1c73477931532 Mon Sep 17 00:00:00 2001 From: sidakram Date: Sat, 8 Mar 2025 18:07:52 +0530 Subject: [PATCH 1/2] feat: added header key redaction for gdpr compliance --- lib/req.js | 18 +++++++++++++++++- lib/res.js | 15 ++++++++++++++- package.json | 3 +++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/req.js b/lib/req.js index 4a58034..c471fe3 100644 --- a/lib/req.js +++ b/lib/req.js @@ -5,6 +5,22 @@ 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"]' + ] +}) + const rawSymbol = Symbol('pino-raw-req-ref') const pinoReqProto = Object.create({}, { id: { @@ -85,7 +101,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..41aa1cb 100644 --- a/lib/res.js +++ b/lib/res.js @@ -5,6 +5,16 @@ module.exports = { resSerializer } +const fastRedact = require('fast-redact') + +const headerRedact = fastRedact({ + paths: [ + 'location', + '["x-real-ip"]', + '["x-forwarded-for"]' + ] +}) + const rawSymbol = Symbol('pino-raw-res-ref') const pinoResProto = Object.create({}, { statusCode: { @@ -35,7 +45,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" } } From 90329103c0588108fadf4df673b79c06abad84e2 Mon Sep 17 00:00:00 2001 From: sidakram Date: Sun, 9 Mar 2025 01:55:59 +0530 Subject: [PATCH 2/2] feat: added test and serializer --- lib/req.js | 5 ++++- lib/res.js | 5 ++++- test/req.test.js | 28 ++++++++++++++++++++++++++++ test/res.test.js | 24 ++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/lib/req.js b/lib/req.js index c471fe3..e865ea4 100644 --- a/lib/req.js +++ b/lib/req.js @@ -18,7 +18,10 @@ const headerRedact = fastRedact({ '["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') diff --git a/lib/res.js b/lib/res.js index 41aa1cb..1f40672 100644 --- a/lib/res.js +++ b/lib/res.js @@ -12,7 +12,10 @@ const headerRedact = fastRedact({ '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') 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 +})