GitHub Advisoryadvisory · npm
GHSA-4xrf-jv44-h6hh ip-address: a CIDR suffix on the parsed address suppresses special-use classification and can bypass SSRF and trust-boundary checks
MEDIUM
Official page ↗
- Aliases
- CVE-2026-69198
- Ecosystem
- npm
- Packages
- ip-address
- Severity
- Moderate
- CVSS vector
- CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:H/SI:N/SA:N
- Weaknesses
- CWE-20, CWE-918
- Reviewed
- 2026-08-03
Affected packages
• npm ip-address: >= 10.1.1, < 10.2.2 → fixed in 10.2.2
Description
Summary
Every special-use classification method is built on `isInSubnet`, which short-circuits to `false` whenever the address's own subnet mask is *shorter* than the reference range's mask. That mask comes verbatim from the CIDR suffix on the parsed input, so appending a suffix such as `/0` suppresses classification entirely: `isLoopback()`, `isPrivate()`, `isLinkLocal()`, `isCGNAT()`, `isMulticast()`, `isUnspecified()`, `isBroadcast()`, `isULA()`, and `getType()` all report an internal address as unremarkable, while `correctForm()` and `address` still return the real internal target.
An application that builds a network trust-boundary decision on these checks (for example a filter intended to block Server-Side Request Forgery, or SSRF) may therefore treat an internal target as external and allow the request. SSRF is an attack in which a user-supplied address coaxes the server into making a request to an internal destination the user could not otherwise reach, such as a loopback service or a cloud metadata endpoint.
Details
`isInSubnet` in `src/common.ts` opens with a guard that compares the two prefix lengths:
```js
export function isInSubnet(this, address) {
if (this.subnetMask = 10.1.1, block as internal, false => allow outbound
function isBlocked(host) {
try {
const a = new Address4(host);
return a.isPrivate() || a.isLoopback() || a.isLinkLocal() || a.isCGNAT()
|| a.isMulticast() || a.isUnspecified() || a.isBroadcast();
} catch {}
try {
const a = new Address6(host);
return a.isPrivate() || a.isLoopback() || a.isLinkLocal() || a.isULA()
|| a.isMulticast() || a.isUnspecified();
} catch {}
return false;
}
for (const h of ['127.0.0.1', '10.0.0.1', '::1',
'127.0.0.1/0', '10.0.0.5/7', '169.254.169.254/0',
'::1/0', '::ffff:127.0.0.1/0', '64:ff9b::7f00:1/0']) {
console.log(isBlocked(h) ? 'BLOCK ' : 'ALLOW ', h, '->', new (h.includes(':') ? Address6 : Address4)(h).correctForm());
}
```
On affected versions every suffixed internal target is allowed, and `correctForm()` shows the request would reach the real internal address:
```
BLOCK 127.0.0.1 -> 127.0.0.1
BLOCK 10.0.0.1 -> 10.0.0.1
BLOCK ::1 -> ::1
ALLOW 127.0.0.1/0 -> 127.0.0.1
ALLOW 10.0.0.5/7 -> 10.0.0.5
ALLOW 169.254.169.254/0 -> 169.254.169.254
ALLOW ::1/0 -> ::1
ALLOW ::ffff:127.0.0.1/0 -> ::ffff:7f00:1
ALLOW 64:ff9b::7f00:1/0 -> 64:ff9b::7f00:1
```
The first three lines are blocked as expected; the same destinations with a CIDR suffix are allowed through.
Remediation
Upgrade to the patched release. In the fix, classification no longer consults the address's own prefix: a new `isHostInSubnet()` compares the address's host bits against the reference range only, and every classifier (`isLoopback`, `isPrivate`, `isLinkLocal`, `isCGNAT`, `isMulticast`, `isUnspecified`, `isBroadcast`, `isULA`, `isMapped4`, `isTeredo`, `is6to4`, `isDocumentation`, `getType`, and the IPv4-mapped/NAT64 normalization behind `embeddedIPv4`) uses it. `isInSubnet` keeps its subnet-containment semantics unchanged, including the guard that a wider network is not contained in a narrower one. After upgrading, `new Address4('127.0.0.1/0').isLoopback()` returns `true`.
If you cannot upgrade immediately, strip the suffix before classifying by re-parsing `addressMinusSuffix`:
```js
const parsed = new Address4(userInput);
const host = new Address4(parsed.addressMinusSuffix); // classify this one
```
A note on SSRF defense
These methods are address classifiers, not a complete SSRF defense. Regardless of this fix, a robust SSRF guard must resolve the hostname and validate the *resolved* IP against the socket it connects to, and account for DNS rebinding and redirects. Treat these checks as one layer, not the only one.
Credit
Reported by @hi-im-glitchless.
Related
Alias · CVE (1)
- CVE-2026-69198 ip-address is a library for parsing and manipulating IPv4 and IPv6 addresses in JavaScript. vulnerability
References
- https://github.com/beaugunderson/ip-address/security/advisories/GHSA-4xrf-jv44-h6hh web
- https://github.com/beaugunderson/ip-address/commit/488fe9bc7c35363b4b090494fc38c266d217740d web
- https://github.com/beaugunderson/ip-address package
- https://github.com/beaugunderson/ip-address/releases/tag/v10.2.2 web