diff --git a/.nvmrc b/.nvmrc index 0719d81..02c8b48 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -10.3.0 +18.18.0 diff --git a/lib/chitragupta/chitragupta.js b/lib/chitragupta/chitragupta.js index b22c2fe..bf19f1e 100644 --- a/lib/chitragupta/chitragupta.js +++ b/lib/chitragupta/chitragupta.js @@ -105,4 +105,5 @@ module.exports = { getUniqueLogId, jsonLogFormatter, setMetaData, + extendSensitiveHeaders: util.extendSensitiveHeaders, }; diff --git a/lib/chitragupta/util.js b/lib/chitragupta/util.js index 29f17ed..e97a45a 100644 --- a/lib/chitragupta/util.js +++ b/lib/chitragupta/util.js @@ -4,6 +4,45 @@ const formatVersions = require('./format_versions'); const fieldLimits = require('./field_limits'); const os = require('os'); +// Sensitive headers that must never reach the log sink as cleartext values. +// Closes DATA-12974 (Authorization/Cookie/etc serialised verbatim) and weakens +// DATA-13003/13004 chains by removing the secret material at the source. +// Names are matched case-insensitively against the request header keys. +const DEFAULT_SENSITIVE_HEADERS = new Set([ + 'authorization', + 'cookie', + 'set-cookie', + 'x-api-key', + 'x-auth-token', + 'proxy-authorization', + 'www-authenticate', +]); + +const customSensitiveHeaders = new Set(); + +function extendSensitiveHeaders(headerNames) { + if (!Array.isArray(headerNames)) return; + headerNames.forEach((h) => { + if (typeof h === 'string' && h.length) { + customSensitiveHeaders.add(h.toLowerCase()); + } + }); +} + +function isSensitiveHeader(name) { + const lower = String(name).toLowerCase(); + return DEFAULT_SENSITIVE_HEADERS.has(lower) || customSensitiveHeaders.has(lower); +} + +function redactHeaders(headers) { + if (!headers || typeof headers !== 'object') return headers; + const out = {}; + Object.keys(headers).forEach((key) => { + out[key] = isSensitiveHeader(key) ? '[REDACTED]' : headers[key]; + }); + return out; +} + function populateServerData(dataParam) { const data = dataParam; const request = cls.get('request'); @@ -17,7 +56,7 @@ function populateServerData(dataParam) { const indexOfQuestionMark = url.indexOf('?'); let endpoint = ''; let params = ''; - let headers = JSON.stringify(request.headers); + let headers = JSON.stringify(redactHeaders(request.headers)); if (indexOfQuestionMark > 0) { endpoint = url.slice(0, indexOfQuestionMark); params = url.slice(indexOfQuestionMark + 1); @@ -40,7 +79,7 @@ function populateServerData(dataParam) { data.data.request.params = JSON.stringify(data.data.request.params); } if (data.data.request.headers && typeof data.data.request.headers === 'object') { - data.data.request.headers = JSON.stringify(data.data.request.headers); + data.data.request.headers = JSON.stringify(redactHeaders(data.data.request.headers)); } data.data.request.params = data.data.request.params.substring(0, fieldLimits.PARAMS) || ''; @@ -135,3 +174,4 @@ function sanitizeKeys(logLevel, message, metaData) { } module.exports.sanitizeKeys = sanitizeKeys; +module.exports.extendSensitiveHeaders = extendSensitiveHeaders; diff --git a/package.json b/package.json index a5eeff7..68bc499 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,11 @@ { "name": "chitragupta", - "version": "1.7.5", + "version": "1.7.6", "description": "An easy to install node module to convert unstructured logs into informative structured logs", "main": "lib/index.js", + "engines": { + "node": ">=14.0.0" + }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" },