A vulnerability in the beaugunderson ip-address library before version 10.7.4 allows denial of service via inefficient regular expression complexity. Specifically, input containing many '%' characters can cause excessive CPU consumption and application stalls when processed by the Address6 constructor.
The ip-address JavaScript library prior to version 10.5.1 contains an incorrect comparison vulnerability in its IPv6 link-local address detection. The Address6 isLinkLocal method only recognizes the fe80::/64 range instead of the full fe80::/10 range, causing inconsistent classification of certain link-local addresses. This inconsistency can allow an attacker-controlled address within the broader fe80::/10 range to bypass trust-boundary checks relying on isLinkLocal, potentially reaching on-link hosts outside intended boundaries. The issue is fixed in version 10.5.1.
The ip-address JavaScript library versions prior to 10.7.1 contain a vulnerability in the isInSubnet and isHostInSubnet methods. These methods perform subnet containment checks by comparing masked binary strings without verifying that both addresses belong to the same IP family (IPv4 vs IPv6). This flaw can cause cross-family containment checks to incorrectly classify an IP address as within a subnet when it is not, potentially leading to incorrect allowlist or denylist decisions. The issue is fixed in version 10.7.1.
CVE-2026-101911 is a medium severity vulnerability in the JavaScript ip-address library before version 10.7.1. It involves uncontrolled resource consumption due to unbounded string input in the Address6 constructor, Address6.isValid, and related parsing code. Large attacker-controlled inputs can cause synchronous stalls, high memory usage, invalid string length exceptions, and process termination. The issue is fixed in version 10.7.1.
A Server-Side Request Forgery (SSRF) vulnerability exists in the ip-address JavaScript library versions 10.2.0 through 10.5.0. The issue arises because the Address6 isPrivate classifier does not recognize the NAT64 local-use range 64:ff9b:1::/48, potentially allowing internal IPv4 destinations encoded through this range to be treated as external. This can lead to bypassing network trust boundaries in applications that rely on isPrivate, isLoopback, and isLinkLocal for trust decisions. The vulnerability is fixed in version 10.5.1.
ip-address is a library for parsing and manipulating IPv4 and IPv6 addresses in JavaScript. From 10.1.1 until 10.2.2, 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. The underlying bit comparison is correct, and mask(n) already returns the first n bits of the full parsed address independently of subnetMask; the defect is solely that the containment guard sits in the classification path. This issue is fixed in version 10.2.2.
The ip-address JavaScript library prior to version 10.3.1 improperly validates input when parsing IPv4 addresses with octets that have leading zeros. This causes a mismatch between the library's interpretation and standard network parsers, potentially leading to incorrect classification of internal versus external IP addresses. This can affect security decisions such as Server-Side Request Forgery (SSRF) protections. The issue is fixed in version 10.3.1.
### Summary
`Address6`'s special-property checks misclassify IPv4-mapped (`::ffff:0:0/96`) and NAT64 well-known (`64:ff9b::/96`) IPv6 addresses. These checks classify an address by its IPv6 wrapper rather than by the IPv4 address it embeds, so `isLoopback()`, `isLinkLocal()`, `isMulticast()`, and `isUnspecified()` all return `false` for literals such as `::ffff:127.0.0.1` or `::ffff:169.254.169.254` that actually route to loopback, RFC 1918, or link-local (cloud-metadata) destinations. `Address6` also had no `isPrivate()` method, so a mapped RFC 1918 address could not be detected at all.
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
`Address6.getType()` classifies an address by matching it against a table of known IPv6 special-use prefixes, returning `Global unicast` when nothing matches. That table had no entry for the IPv4-mapped range (`::ffff:0:0/96`), so every mapped address fell through to `Global unicast`; NAT64 addresses matched their own `NAT64 …` labels. The boolean checks `isLoopback`, `isUnspecified`, and `isMulticast` compared `getType()` against a fixed label and so returned `false`, while `isLinkLocal` and `isULA` checked only the native IPv6 ranges.
The library already exposed `isMapped4()` and `to4()`, but did not apply them inside these checks, so a mapped or NAT64 address was never normalized to its embedded IPv4 address before classification. The underlying CIDR matching is correct; the defect is that the special-use table omitted the IPv4-mapped range and the checks performed no embedded-IPv4 normalization.
### Affected versions
`>= 10.1.1, <= 10.2.0`. The `is*` classification API was introduced for `Address4` in 10.1.1 and extended to `Address6` in 10.2.0. Releases before 10.1.1 do not expose this API and are not affected through this vector.
### Impact
The misclassification covers the entire `::ffff:0:0/96` range, in both dotted and hex notation and case-insensitively, plus the `64:ff9b::/96` NAT64 well-known prefix:
| Address | Reported as | Actually points at |
|---|---|---|
| `::ffff:127.0.0.1` / `::ffff:7f00:1` | Global unicast | loopback (`127.0.0.0/8`) |
| `::ffff:10.0.0.1` | Global unicast | RFC 1918 `10/8` |
| `::ffff:172.16.5.5` | Global unicast | RFC 1918 `172.16/12` |
| `::ffff:192.168.1.1` | Global unicast | RFC 1918 `192.168/16` |
| `::ffff:169.254.169.254` / `::ffff:a9fe:a9fe` | Global unicast | link-local / cloud metadata (IMDS) |
| `::ffff:100.64.0.1` | Global unicast | CGNAT `100.64/10` |
| `::ffff:0.0.0.0` / `::ffff:255.255.255.255` | Global unicast | unspecified / broadcast |
| `64:ff9b::7f00:1` / `64:ff9b::a9fe:a9fe` | NAT64 (well-known) | loopback / IMDS via NAT64 |
For IPv4-mapped addresses the host OS routes to the IPv4 stack, so the misclassification is reachable on any dual-stack host. For NAT64, the classification bypass is unconditional but end-to-end reachability additionally requires a NAT64/DNS64 gateway in the deployment network.
### Proof of concept
A guard assembled from these checks lets internal hosts through:
```js
const { Address4, Address6 } = require('ip-address');
// true => 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.isLoopback() || a.isLinkLocal() || a.isULA()
|| a.isMulticast() || a.isUnspecified();
} catch {}
return false;
}
for (const h of ['127.0.0.1', '::1', '10.0.0.1', '8.8.8.8',
'::ffff:127.0.0.1', '::ffff:10.0.0.1',
'::ffff:169.254.169.254', '64:ff9b::7f00:1']) {
console.log(isBlocked(h) ? 'BLOCK ' : 'ALLOW ', h);
}
```
On affected versions this prints (note that every `::ffff:…` and `64:ff9b::…` internal target is allowed):
```
BLOCK 127.0.0.1
BLOCK ::1
BLOCK 10.0.0.1
ALLOW 8.8.8.8
ALLOW ::ffff:127.0.0.1
ALLOW ::ffff:10.0.0.1
ALLOW ::ffff:169.254.169.254
ALLOW 64:ff9b::7f00:1
```
The first three lines (native loopback, native IPv6 loopback, and a literal RFC 1918 address) are blocked as expected; the IPv4-mapped and NAT64 forms of the same internal destinations are allowed through.
### Remediation
Upgrade to the patched release. In the fix, `Address6` normalizes IPv4-mapped and NAT64 well-known addresses to their embedded IPv4 address before classifying, via a new `embeddedIPv4()` helper that `isLoopback`, `isLinkLocal`, `isMulticast`, and `isUnspecified` consult first. `Address6` als
ip-address is a library for parsing and manipulating IPv4 and IPv6 addresses in JavaScript. Prior to 10.1.1, Address6.group() and Address6.link() do not HTML-escape attacker-controlled content before embedding it in the HTML strings they return, and AddressError.parseMessage (emitted by the Address6 constructor for invalid input) can contain unescaped attacker-controlled content in one branch. An application that (1) passes untrusted input to Address6 and (2) renders the output of these methods, or the thrown error's parseMessage, as HTML (e.g. via innerHTML) is vulnerable to cross-site scripting. This vulnerability is fixed in 10.1.1.