fix(h2): keep a request header named __proto__ - #5669
Conversation
buildRequestHeaders probed the accumulator with headers[key], which returns Object.prototype for __proto__ instead of undefined. The header took the already-present branch, its value became '[object Object], pwned', and the assignment then hit the Object.prototype setter, which refuses a string, so nothing reached the wire. The probe uses Object.hasOwn and the writes go through a setHeader helper using Object.defineProperty, the guard parseHeaders already uses.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5669 +/- ##
==========================================
+ Coverage 93.43% 93.45% +0.02%
==========================================
Files 110 110
Lines 38733 38795 +62
==========================================
+ Hits 36190 36256 +66
+ Misses 2543 2539 -4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
The two failing Node 24 jobs stop at I do not have permission to rerun workflows in this repository. Could a maintainer please rerun the failed jobs to confirm whether this is the same intermittent failure? |
|
Consolidated into #5688, so the guard is one util in Closing this one to keep your queue clear. Happy to reopen if you would rather look at the sites separately. |
This relates to...
The h2 half of what I flagged in a comment on #5667.
test/prototype-headers.jsalready covers this concern forclient.requestover HTTP/1.Rationale
buildRequestHeadersinlib/dispatcher/client-h2.jsprobes the accumulator by index:For
__proto__that probe returnsObject.prototype, which is truthy, so the header takes the "already present" branch and the value becomes"[object Object], pwned". That string is then assigned through theObject.prototypesetter, which refuses a string, so nothing is stored and the header never reaches the wire.Reproduced over a real HTTP/2 connection, before the change:
__proto__is a valid field name, and the flat array form is exactly what this function receives from the dispatcher.There is no
Object.prototypepollution: the setter refuses the string, so the global prototype is untouched.Changes
The probe uses
Object.hasOwnso an inherited name is not mistaken for an existing header, and all four writes go through asetHeaderhelper usingObject.defineProperty, the guardparseHeadersalready uses inlib/core/util.js.Test added to
test/prototype-headers.js, alongside the HTTP/1 cases: an h2 request carrying__proto__reaches the server with that header, with a control header that already worked.test/+(http2|h2)*.js104 passing with 1 skipped as before, lint clean.Features
N/A
Bug Fixes
An HTTP/2 request no longer drops a header named
__proto__, and no longer corrupts its value with[object Object],first.Breaking Changes and Deprecations
None.
Status