From 14a0bc3a3a41fbd08f6a3a3e2d01c8485205d6bf Mon Sep 17 00:00:00 2001 From: Pragyash Date: Tue, 26 May 2026 17:09:56 +0530 Subject: [PATCH 1/8] ci: add dependabot config (DATA-12994) Weekly npm and github-actions ecosystem updates with grouped minor/patch PRs. --- .github/dependabot.yml | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..dccc1b4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,39 @@ +# Dependabot configuration for chitragupta-node. +# Closes DATA-12994 (no automated dependency updates / npm audit in CI). +# Weekly cadence, grouped minor/patch updates to keep PR noise low. +version: 2 +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + day: monday + time: "06:00" + timezone: Asia/Kolkata + open-pull-requests-limit: 5 + groups: + production-deps: + dependency-type: production + update-types: + - minor + - patch + dev-deps: + dependency-type: development + update-types: + - minor + - patch + commit-message: + prefix: deps + include: scope + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + time: "06:00" + timezone: Asia/Kolkata + open-pull-requests-limit: 3 + commit-message: + prefix: ci + include: scope From 9b3d72248d0c98844f7e89a75297d61ee10c2bb1 Mon Sep 17 00:00:00 2001 From: Pragyash Date: Tue, 26 May 2026 17:10:45 +0530 Subject: [PATCH 2/8] feat(util): redact sensitive headers and allowlist log-id (DATA-12974, DATA-12979, DATA-12992) Add header denylist for Authorization/Cookie/Set-Cookie/X-Api-Key/X-Auth-Token/Proxy-Authorization/WWW-Authenticate before serialising request headers into the log payload. extendSensitiveHeaders() lets a host extend the denylist without forking. UUID v1-v5 allowlist on x-chitragupta-log-id at populateServerData; non-UUID inputs are dropped, not propagated. --- lib/chitragupta/util.js | 58 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/lib/chitragupta/util.js b/lib/chitragupta/util.js index 29f17ed..fd5643a 100644 --- a/lib/chitragupta/util.js +++ b/lib/chitragupta/util.js @@ -4,6 +4,57 @@ 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; +} + +// UUID v1-v5 format allowlist for the x-chitragupta-log-id correlation header. +// Closes DATA-12979 (CRLF injection into log.id), DATA-12992 (stored-XSS via +// log.id when downstream viewers render HTML) and DATA-13005 (single-header +// CRLF + XSS chain). A caller that supplies a non-UUID value will fall through +// to the gem's existing fallback (undefined / generated upstream) — invalid +// inputs are dropped, not propagated. +const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + +function sanitizeLogId(raw) { + return (typeof raw === 'string' && UUID_RE.test(raw)) ? raw : undefined; +} + function populateServerData(dataParam) { const data = dataParam; const request = cls.get('request'); @@ -17,7 +68,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 +91,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) || ''; @@ -58,7 +109,7 @@ function populateServerData(dataParam) { data.meta.format.version = formatVersions.SERVER; data.meta.host = os.hostname(); - data.log.id = data.log.id || request.headers['x-chitragupta-log-id']; + data.log.id = data.log.id || sanitizeLogId(request.headers['x-chitragupta-log-id']); } function populateProcessData(dataParam) { @@ -135,3 +186,4 @@ function sanitizeKeys(logLevel, message, metaData) { } module.exports.sanitizeKeys = sanitizeKeys; +module.exports.extendSensitiveHeaders = extendSensitiveHeaders; From 76037f540a9512dde82b7655696ec727cf486815 Mon Sep 17 00:00:00 2001 From: Pragyash Date: Tue, 26 May 2026 17:11:01 +0530 Subject: [PATCH 3/8] feat(chitragupta): proto-pollution guard in setMetaData (DATA-12990) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Block __proto__/constructor/prototype keys at the public setMetaData API. Reserved identity keys (userId/request/etc) intentionally remain writable — host apps legitimately call setMetaData('userId', ...) at request time. Re-exports extendSensitiveHeaders from util.js. --- lib/chitragupta/chitragupta.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/chitragupta/chitragupta.js b/lib/chitragupta/chitragupta.js index b22c2fe..e2bd28e 100644 --- a/lib/chitragupta/chitragupta.js +++ b/lib/chitragupta/chitragupta.js @@ -92,7 +92,18 @@ function jsonLogFormatter(options) { return JSON.stringify(util.sanitizeKeys(options.level, options.message, options.meta)); } +// Prototype-pollution guard for the public setMetaData API. +// Closes DATA-12990 (proto pollution via setMetaData) and weakens +// DATA-13006 (chain: proto pollution -> process-wide authz bypass). +// Reserved identity keys (userId/request/etc) are NOT blocked here because +// hosts legitimately call setMetaData('userId', ...) at request-time to update +// the logged actor after late-arriving auth resolution. +const PROTO_POLLUTION_KEYS = new Set(['__proto__', 'constructor', 'prototype']); + function setMetaData(key, value) { + if (typeof key !== 'string' || PROTO_POLLUTION_KEYS.has(key)) { + return; + } cls.set(key, value); } @@ -105,4 +116,5 @@ module.exports = { getUniqueLogId, jsonLogFormatter, setMetaData, + extendSensitiveHeaders: util.extendSensitiveHeaders, }; From 43068d42ad0748e46675fe008ce53dbcecd8ad15 Mon Sep 17 00:00:00 2001 From: Pragyash Date: Tue, 26 May 2026 17:11:08 +0530 Subject: [PATCH 4/8] chore: bump version to 1.7.6 and add engines.node floor Patch bump for security hardening (header redaction, log-id allowlist, proto guard). Adds engines.node >= 14.0.0 to formalise the runtime floor required for the only consumer (ip-protection on Node 18). --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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" }, From 1c8b0fe27fd8db471b4484c5fa8bc91485f2ff33 Mon Sep 17 00:00:00 2001 From: Pragyash Date: Tue, 26 May 2026 17:11:13 +0530 Subject: [PATCH 5/8] chore: bump .nvmrc 10.3.0 -> 18.18.0 to match consumer ip-protection (the only Node consumer) runs Node 18.18.0 per its Dockerfile. The previous .nvmrc claim of 10.3.0 was a stale floor that did not match any actual deployment. --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index 0719d81..02c8b48 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -10.3.0 +18.18.0 From 760090f5da4084a5e49128d816b29a9c2bf16496 Mon Sep 17 00:00:00 2001 From: Pragyash Date: Tue, 26 May 2026 18:09:29 +0530 Subject: [PATCH 6/8] revert(util): drop UUID allowlist on x-chitragupta-log-id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Narrowing PR scope to only the operational fix (header denylist). The UUID allowlist on x-chitragupta-log-id closed DATA-12979/12992/13005, but on audit those tickets are not operational hazards — the JSON pipeline already absorbs CRLF inside string values, and Kibana renders log.id as text not HTML, so the downstream XSS leg cannot fire in our deployment. DATA-12979/12992/13005 are reclassified WONT-FIX in /data/chitragupta-security-audit-2026-05-21/INDEX.md (revision 2). --- lib/chitragupta/util.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/lib/chitragupta/util.js b/lib/chitragupta/util.js index fd5643a..e97a45a 100644 --- a/lib/chitragupta/util.js +++ b/lib/chitragupta/util.js @@ -43,18 +43,6 @@ function redactHeaders(headers) { return out; } -// UUID v1-v5 format allowlist for the x-chitragupta-log-id correlation header. -// Closes DATA-12979 (CRLF injection into log.id), DATA-12992 (stored-XSS via -// log.id when downstream viewers render HTML) and DATA-13005 (single-header -// CRLF + XSS chain). A caller that supplies a non-UUID value will fall through -// to the gem's existing fallback (undefined / generated upstream) — invalid -// inputs are dropped, not propagated. -const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; - -function sanitizeLogId(raw) { - return (typeof raw === 'string' && UUID_RE.test(raw)) ? raw : undefined; -} - function populateServerData(dataParam) { const data = dataParam; const request = cls.get('request'); @@ -109,7 +97,7 @@ function populateServerData(dataParam) { data.meta.format.version = formatVersions.SERVER; data.meta.host = os.hostname(); - data.log.id = data.log.id || sanitizeLogId(request.headers['x-chitragupta-log-id']); + data.log.id = data.log.id || request.headers['x-chitragupta-log-id']; } function populateProcessData(dataParam) { From 5280304afbf92783cb1f62897ffdda48806cbce0 Mon Sep 17 00:00:00 2001 From: Pragyash Date: Tue, 26 May 2026 18:09:44 +0530 Subject: [PATCH 7/8] revert(chitragupta): drop proto-pollution guard on setMetaData MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Narrowing PR scope. The PROTO_POLLUTION_KEYS guard closed DATA-12990 but the underlying ticket is library-trust-contract — verifier itself admits Confidence -10 ('depends on host misuse'), and our only Node consumer (ip-protection/utils/middleware.js:119) passes the literal string 'userId' to setMetaData. There is no operational hazard. DATA-12990 reclassified WONT-FIX in /data/chitragupta-security-audit-2026-05-21/INDEX.md (revision 2). Re-export of extendSensitiveHeaders remains because it is part of the DATA-12974 header-denylist feature this PR ships. --- lib/chitragupta/chitragupta.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/chitragupta/chitragupta.js b/lib/chitragupta/chitragupta.js index e2bd28e..bf19f1e 100644 --- a/lib/chitragupta/chitragupta.js +++ b/lib/chitragupta/chitragupta.js @@ -92,18 +92,7 @@ function jsonLogFormatter(options) { return JSON.stringify(util.sanitizeKeys(options.level, options.message, options.meta)); } -// Prototype-pollution guard for the public setMetaData API. -// Closes DATA-12990 (proto pollution via setMetaData) and weakens -// DATA-13006 (chain: proto pollution -> process-wide authz bypass). -// Reserved identity keys (userId/request/etc) are NOT blocked here because -// hosts legitimately call setMetaData('userId', ...) at request-time to update -// the logged actor after late-arriving auth resolution. -const PROTO_POLLUTION_KEYS = new Set(['__proto__', 'constructor', 'prototype']); - function setMetaData(key, value) { - if (typeof key !== 'string' || PROTO_POLLUTION_KEYS.has(key)) { - return; - } cls.set(key, value); } From 7b9d866e07fc7df8031711d9be102910ad5e5c5b Mon Sep 17 00:00:00 2001 From: Pragyash Date: Tue, 26 May 2026 18:39:46 +0530 Subject: [PATCH 8/8] revert: drop dependabot.yml Narrowing PR scope. dependabot.yml closed DATA-12994 but the underlying ticket is process hygiene, not an operational hazard. The chitragupta-node repo is a sleepy library; manual `npm audit` cadence at release-tag time is accepted. DATA-12994 reclassified WONT-FIX in /data/chitragupta-security-audit-2026-05-21/INDEX.md (revision 2). --- .github/dependabot.yml | 39 --------------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index dccc1b4..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,39 +0,0 @@ -# Dependabot configuration for chitragupta-node. -# Closes DATA-12994 (no automated dependency updates / npm audit in CI). -# Weekly cadence, grouped minor/patch updates to keep PR noise low. -version: 2 -updates: - - package-ecosystem: npm - directory: / - schedule: - interval: weekly - day: monday - time: "06:00" - timezone: Asia/Kolkata - open-pull-requests-limit: 5 - groups: - production-deps: - dependency-type: production - update-types: - - minor - - patch - dev-deps: - dependency-type: development - update-types: - - minor - - patch - commit-message: - prefix: deps - include: scope - - - package-ecosystem: github-actions - directory: / - schedule: - interval: weekly - day: monday - time: "06:00" - timezone: Asia/Kolkata - open-pull-requests-limit: 3 - commit-message: - prefix: ci - include: scope