Repository navigation
Conversation
|
Review requested:
|
There was a problem hiding this comment.
This would make the port issue much simpler with this additional validation. If the string ends in /:(\d+)$/, then it can just be sliced off, and if the rest of the string gets parsed as a valid hostname by URLParse then the port can be obtained from the digit string with NumberParseInt.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #64832 +/- ##
==========================================
+ Coverage 90.58% 90.66% +0.08%
==========================================
Files 797 798 +1
Lines 278297 278869 +572
Branches 53515 53645 +130
==========================================
+ Hits 252096 252846 +750
+ Misses 16561 16360 -201
- Partials 9640 9663 +23
🚀 New features to boost your workflow:
|
|
@Renegade334 maybe we should include |
|
I'm not yet convinced this is the best way to approach this (with the regex). Won't block for now but there's probably a more performant / reliable way. |
971d5b4 to
9d21a93
Compare
@jasnell I implemented a new strict socket address parser replacing Ada, it fixes #62906 and is faster. Benchmark results compared to main: |
9d21a93 to
75ae3d0
Compare
|
Benchmark GHA (net / net-socketaddress-parse): https://github.com/nodejs/node/actions/runs/33605572788 Results
Benchmark results:
|
f702a10 to
45c3368
Compare
Signed-off-by: Guilherme Araújo <arauujogui@gmail.com>
SocketAddress.parse() ran its input through the WHATWG URL host parser, which accepts far more than an address with an optional port. It took the legacy IPv4 notations behind CVE-2021-29922 and CVE-2021-29923, including octal, hexadecimal, integer and shorthand forms, and it dropped a port that matched the default for the assumed scheme, so '1.2.3.4:80' parsed as port 0. Replace it with a small parser that matches the documented grammar and nothing else. It only frames the input; the host is handed to uv_inet_pton, which rejects the legacy forms itself. IPv6 zone ids are deliberately outside the grammar. sin6_scope_id is not exposed on the JS object, so a scoped address could not round trip through its own JSON, and an interface name cannot be resolved without consulting the local interface table, which would make the result depend on the host it was parsed on. '%' stays in the IPv6 delimiter set so that it can never reach uv_ip6_addr(), which would otherwise resolve it. Assisted-by: Claude Opus 5 Signed-off-by: Guilherme Araújo <arauujogui@gmail.com>
The parse benchmark only measured inputs that parse successfully, so the rejection paths were unmeasured. Add a group covering the four ways the parser bails: a non-address host and a legacy hex form that only uv_inet_pton rejects, an out of range port, and an unterminated IPv6 address that never reaches uv_ip6_addr. Assisted-by: Claude Opus 5 Signed-off-by: Guilherme Araújo <arauujogui@gmail.com>
45c3368 to
a936229
Compare
InternalSocketAddress does not need to move above SocketAddress: parse() is a static method and runs at call time, after the module has finished loading, so the class is already defined at its original position. Keep it there to limit the diff to the parse() rewrite. Assisted-by: Claude Signed-off-by: Guilherme Araújo <arauujogui@gmail.com>
SocketAddress.parse()built ahttp://${input}URL and read the host back out. The WHATWG parser is lenient, so it accepted input that is not a socket address. Legacy IPv4 like0177.0.0.1,0x7f.0.0.1,2130706433and127.1, URL syntax likeuser@1.2.3.4:80, and rewritten input such as tabs and IDNA digits all passed. It also disagreed with the constructor, which rejects them.Now it splits host and port in C++ and validates with
uv_inet_pton(), same as the constructor.Those legacy forms are the differential behind CVE-2021-29923, CVE-2021-29922 and the TLS CVEs this cycle, CVE-2026-106069 and CVE-2026-106072, where the parser and the resolver read them differently.
parse()isn't a security boundary, so this is hardening rather than a CVE fix.Also fixes #62906.
parse('1.2.3.4:80')returned port 0 because the URL parser drops a scheme's default port. It now returns 80.Grammar is documented in
doc/api/net.md. IPv6 zone ids are left out, sincesin6_scope_idisn't on the JS object and a scoped address can't round-trip its own JSON.