| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
Reject octal-ambiguous IPv4 octets and stacked subnet suffixes (GHSA-mwp4-54f8-5fhr) Address4 accepted an octet written with a leading zero and decoded it as decimal, while the WHATWG URL host parser, inet_aton and getaddrinfo decode a leading zero as octal. The library and the network stack therefore disagreed about which host a string named: '012.0.0.1' reported correctForm '12.0.0.1' and isPrivate false, while fetching it reached 10.0.0.1. A guard built on these checks classified an RFC 1918 destination as public and allowed it. The disagreement ran both ways, with '010.0.0.1' reported private while resolving to the public 8.0.0.1. RE_ADDRESS now admits each octet only without a leading zero, and parse() checks for the notation first so the error names it. Address6 already applied this rule on its v4-in-v6 path; the detection there now matches the dotted quad permissively so that path keeps reporting the offending octet with its own highlighted message rather than falling through as an unknown group. Separately, RE_SUBNET_STRING anchors on the end of the address, so a stacked suffix left the earlier one in place: '::/0/1' stripped only '/1', parsed '::/0' as an address, and produced a NaN group with a correctForm of '::NaN' while isValid returned true. The leftover-slash reject now runs whether or not a suffix matched, so those inputs throw. Address4 already rejected the equivalent '10.0.0.1/8/16'. This one carries no security impact. Rejects input earlier releases accepted: zero-padded forms such as '127.000.000.001' and '001.002.003.004' are no longer valid, and the test that asserted zero-padded notations parse to the same address now asserts they are rejected. Callers feeding zero-padded addresses must strip the padding first. | 1 个月前 | |
Merge commit from fork Address6's special-property checks were built on getType(), which has no entry for IPv4-mapped (::ffff:0:0/96) addresses and labels them 'Global unicast', while NAT64 (64:ff9b::/96) carries its own label. As a result isLoopback/isLinkLocal/isMulticast/isUnspecified all returned false for mapped/NAT64 literals that actually point at loopback, RFC1918, or link-local (cloud-metadata) addresses, and Address6 had no isPrivate() at all. A guard built from these checks would let ::ffff:127.0.0.1, ::ffff:10.0.0.1, ::ffff:169.254.169.254, 64:ff9b::7f00:1, etc. through. Add Address6.embeddedIPv4(), which returns the embedded Address4 for mapped and NAT64-well-known addresses, and route isLoopback, isLinkLocal, isMulticast, and isUnspecified through it so they classify by what the address actually reaches. Add isPrivate(), isCGNAT(), and isBroadcast() to Address6 for parity with Address4 (the missing private-check gap the advisory names). Add the missing ::ffff:0:0/96 -> 'IPv4-mapped' entry to the TYPES table so getType() is honest. isULA() is intentionally left as fc00::/7 only; isPrivate() is the umbrella that also catches mapped RFC1918. | 1 个月前 | |
improve test coverage by removing useless check | 2 年前 | |
Validate the byte arrays Address6 is given (#217) Address6.fromByteArray and fromUnsignedByteArray fold whatever they are given into a BigInt and bounds-check only the aggregate, so wrong-length, out-of-range and non-integer input produces a plausible wrong address instead of an error: three bytes give ::1:203, seventeen bytes whose first is zero give ::1, a low byte of 300 gives ::12c, and a byte of 1.5 gives 100::. A 17-byte array shows why the aggregate bound cannot cover this, since a leading zero keeps the magnitude under 2**128. Address4 rejects all four. This reaches consumers. socks calls Address6.fromByteArray at three sites with Array.from(buff.readBuffer(16)), and in parseUDPFrame that buffer is an unvalidated datagram while smart-buffer's readBuffer clamps a short read with Math.min rather than throwing. A SOCKS5 UDP frame carrying four address bytes therefore reports a remote host of 0000:0000:0000:0000:0000:0000:dead:beef. Both methods now require exactly 16 bytes. fromByteArray keeps folding signed bytes to unsigned, so an Int8Array or a Java byte[] still works and nothing that parses today stops parsing; its floor is -128, below which folding would silently produce a value the caller did not mean. fromUnsignedByteArray takes 0 to 255. The length and range checks live in one place that Address4 shares, replacing its copy of the same loop, with its messages unchanged. Address6.fromByteArray accepting signed bytes while Address4.fromByteArray rejects them, and toUnsignedByteArray returning what toByteArray already returns, are differences to settle in a major version rather than here, where the point is to reach the versions socks resolves. A test asserts the major version is below 11 and names what to delete when it is not. The byte array parameters are typed number[] rather than any[], matching Address4. Co-authored-by: gaoflow <gaoflow@users.noreply.github.com> | 1 个月前 | |
use re-export syntax | 2 年前 | |
Validate the byte arrays Address6 is given (#217) Address6.fromByteArray and fromUnsignedByteArray fold whatever they are given into a BigInt and bounds-check only the aggregate, so wrong-length, out-of-range and non-integer input produces a plausible wrong address instead of an error: three bytes give ::1:203, seventeen bytes whose first is zero give ::1, a low byte of 300 gives ::12c, and a byte of 1.5 gives 100::. A 17-byte array shows why the aggregate bound cannot cover this, since a leading zero keeps the magnitude under 2**128. Address4 rejects all four. This reaches consumers. socks calls Address6.fromByteArray at three sites with Array.from(buff.readBuffer(16)), and in parseUDPFrame that buffer is an unvalidated datagram while smart-buffer's readBuffer clamps a short read with Math.min rather than throwing. A SOCKS5 UDP frame carrying four address bytes therefore reports a remote host of 0000:0000:0000:0000:0000:0000:dead:beef. Both methods now require exactly 16 bytes. fromByteArray keeps folding signed bytes to unsigned, so an Int8Array or a Java byte[] still works and nothing that parses today stops parsing; its floor is -128, below which folding would silently produce a value the caller did not mean. fromUnsignedByteArray takes 0 to 255. The length and range checks live in one place that Address4 shares, replacing its copy of the same loop, with its messages unchanged. Address6.fromByteArray accepting signed bytes while Address4.fromByteArray rejects them, and toUnsignedByteArray returning what toByteArray already returns, are differences to settle in a major version rather than here, where the point is to reach the versions socks resolves. A test asserts the major version is below 11 and names what to delete when it is not. The byte array parameters are typed number[] rather than any[], matching Address4. Co-authored-by: gaoflow <gaoflow@users.noreply.github.com> | 1 个月前 | |
Honor the fromURL graceful-failure contract for non-IPv6 hosts (#218) Address6.fromURL() is documented and typed to return `{ error, address: null, port: null }` when a URL can't be parsed, but that contract was only implemented on the regex-miss path. The URL host character class `[0-9a-f:.]` is a superset of valid IPv6, so any host made only of hex digits, colons and dots matched the regex and was handed straight to `new Address6(host)`, whose AddressError propagated out uncaught. IPv4 literals are the visible case — `http://127.0.0.1/`, `http://169.254.169.254/`, `http://0.0.0.0/` all threw — but so did `[:::]`, `[1:2:3:4:5:6:7:8:9]` and `[::ffff:999.1.1.1]`. A plain hostname like `example.com` avoided the throw only because letters outside a-f break the regex match first. Catch the constructor and return the same error object the regex-miss path returns, so the function never throws for any input. Reported by @zikk090 via GHSA-mxvh-v779-f36j. Declining that as a security advisory: an uncaught synchronous throw from a parser on the caller's own stack is a correctness bug, not a denial of service. | 27 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 1 个月前 | ||
| 1 个月前 | ||
| 2 年前 | ||
| 1 个月前 | ||
| 2 年前 | ||
| 1 个月前 | ||
| 27 天前 |