Repository navigation
fix(security): define masked-copy keys and escape the request logger's error line - #5
Merged
Merged
Conversation
…s error line Masked copies (request bodies, request and response headers, maskMetaKeys metadata, Error fields, toJSON() output) were built by assigning each caller- or client-chosen key onto a fresh object. Assignment consults Object.prototype, so with Object.prototype frozen a key named after one of its members (toString, valueOf, ...) threw: a logged request body became "[UNSERIALIZABLE]", a maskMetaKeys entry degraded to its _unserializable / _redactionFailed / "[RedactionFailed]" form, and a mixed-case Constructor header dropped the whole request entry. - Rebuilds now define their keys (an object spread in redactEntries, Object.fromEntries elsewhere), so every key is logged as it is. __proto__, constructor and prototype are still left out, and header names are checked against them after lowercasing, so a __PROTO__ header can no longer silently disable a redactPaths header entry. Closes the CodeQL js/remote-property-injection alerts in src/redact.ts. - The error-path console.error line writes the method, URL and error message JSON-escaped, with Unicode control, format and separator characters as \u escapes, so none can split or forge the line. Closes the CodeQL js/log-injection alert. - Output is otherwise byte-identical, with no measurable slowdown.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the 7 open code-scanning alerts on
main(6 ×js/remote-property-injectioninsrc/redact.ts, 1 ×js/log-injectioninsrc/request-middleware.ts) by fixing the defect behind them.Why they appeared in 1.3.0. The new type fixtures
tests/fixtures/types/eopt-express.tsandeopt-node-http.tsmount the middleware, so CodeQL now models itsreqas a real HTTP request. The same CodeQL version gave 0 results on the 1.3.0 commit with those two fixtures removed. The fixtures stay: that is how applications mount the middleware.The defect. Every masked copy (request bodies, request and response headers,
maskMetaKeysmetadata,Errorfields,toJSON()output) was built by assigning each caller- or client-chosen key onto a fresh object. Assignment consultsObject.prototype, so withObject.prototypefrozen (a common prototype-pollution mitigation) a key such astoStringthrew: the logged request body became"[UNSERIALIZABLE]", amaskMetaKeysentry degraded to_unserializable/_redactionFailed/"[RedactionFailed]", and a mixed-caseConstructorheader dropped the entry.Changes
redactEntries(faster than the old loop),Object.fromEntrieselsewhere. Every key is logged as it is;__proto__/constructor/prototypeare still left out, now judged after lowercasing header names (a__PROTO__header could silently disable aredactPathsheader entry with header masking off).neutralizeCallerAccessorskeeps assigning on purpose: winston-transport's ownObject.assignre-clone follows it, so defining keys there would turn today's degraded line into an exception out oflogger.info(). A guard test pins this.console.errorline escapes the method, URL and error message (JSON.stringifywithout quotes, then Unicode control, format and separator characters as\uescapes). Lossless; an ordinary line prints as before.Object.prototypeis frozen (momentassigns while loading), and winston's own copies (format-carrying transports,info(msg, meta), child loggers,errors()) still assign top-level keys.Verification
c8f7ab9and reports 0 on this branch. The PR scan below only reports changed lines, so the alerts close whenmainis rescanned.npm run verifypasses; 1198 tests plus one Windows-only skip, 100% coverage; also run on Node 18.20.8 and 22.23.2.Type of change
Checklist
npm run buildpassesnpm testpasses (100% coverage threshold enforced)npm run lintpassesnpm run type-checkpassesnpm run format:checkpassesCHANGELOG.mdupdated under## [Unreleased]CLAUDE.md/README.mdupdated when public API, options, or conventions changedRelated issues
Resolves code-scanning alerts #5, #6, #7, #8, #9, #10 (
js/remote-property-injection) and #11 (js/log-injection).