CVE-2026-101913: CWE-697: Incorrect Comparison in beaugunderson ip-address
### Summary `Address6.isLinkLocal()` recognizes `fe80::/64` rather than `fe80::/10`. Link-local unicast is the whole `/10` under RFC 4291 §2.4 and the IANA IPv6 Special-Purpose Address Registry, so the method returns `false` for every link-local address outside the one `/64` that stateless address autoconfiguration happens to use. `new Address6('fe81::1').isLinkLocal()` is `false`. The library contradicts itself on the same object: for `fe81::1`, `getType()` returns `'Link-local unicast'`, `getScope()` returns `'Link local'`, and `isHostInSubnet(new Address6('fe80::/10'))` returns `true`, while `isLinkLocal()` returns `false`. 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) will classify a link-local target as unremarkable 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. ### Details `isLinkLocal()` in `src/ipv6.ts` compares the first 64 bits of the address against a literal string: ```ts // Zeroes are required, i.e. we can't check isHostInSubnet with 'fe80::/10' if ( this.getBitsBase2(0, 64) === '1111111010000000000000000000000000000000000000000000000000000000' ) { return true; } ``` The comparison requires the first 64 bits to be exactly `fe80:0000:0000:0000`, so it accepts 2⁶⁴ of the 2¹¹⁸ addresses in `fe80::/10`. The comment states a premise the library disproves: `getType()` classifies the same range with `isHostInSubnet` against the `'fe80::/10': 'Link-local unicast'` entry in `src/v6/constants.ts`, and `Address4.isLinkLocal()` is a plain `isHostInSubnet` test against `169.254.0.0/16`. RFC 4291 §2.5.6 constrains the format of an autoconfigured link-local address; it does not define the range, and reading it as the definition is the likeliest origin of the `/64` comparison. The IPv4-mapped and NAT64 well-known paths of `isLinkLocal()` are unaffected: `::ffff:169.254.169.254` and `64:ff9b::a9fe:a9fe` are classified by their embedded IPv4 address and report `true`. ### Affected versions `<= 10.5.0`. The comparison has had this shape since `Address6.isLinkLocal()` was introduced, so every release exposing the method is affected. ### Impact Every well-formed address in `fe80::/10` outside `fe80::/64` is parsed successfully, `isValid()` is `true`, and the classifier reports something untrue about it. No other classifier catches these addresses: `isPrivate()` covers ULA (`fc00::/7`), not link-local. | Address | `isLinkLocal()` | `getType()` | `getScope()` | |---|---|---|---| | `fe80::1` | `true` | Link-local unicast | Link local | | `fe81::1` | `false` | Link-local unicast | Link local | | `fe8f::1` | `false` | Link-local unicast | Link local | | `febf::1` | `false` | Link-local unicast | Link local | | `fe80:0:0:1::1` | `false` | Link-local unicast | Link local | | `fe80::1:0:0:0:1` | `false` | Link-local unicast | Link local | Python's `ipaddress` module, the `IN6_IS_ADDR_LINKLOCAL` macro in `netinet6/in6.h`, and Linux's `ipv6_addr_type()` all apply a ten-bit prefix test and classify every row above as link-local. A request admitted through a guard built on `isLinkLocal()` reaches a link-local host on the server's own segment: a neighboring machine or the on-link router. An IPv6 link-local destination generally needs a zone index and a neighbor on the same link, so the reach is the server's own segment rather than the internet or a universal metadata endpoint, and the severity reflects that. ### Proof of concept `npm i [email protected]`, then: ```js const { Address6 } = require('ip-address'); // A guard of the shape the library documents. function isBlocked(host) { const a = new Address6(host); return a.isLoopback() || a.isLinkLocal() || a.isPrivate() || a.isMulticast() || a.isUnspecified(); } for (const h of ['fe80::1', 'fe81::1', 'febf::1', 'fe80:0:0:1::1']) { const a = new Address6(h); console.log(isBlocked(h) ? 'BLOCK' : 'ALLOW', h, '-> getType()', a.getType()); } ``` On affected versions: ``` BLOCK fe80::1 -> getType() Link-local unicast ALLOW fe81::1 -> getType() Link-local unicast ALLOW febf::1 -> getType() Link-local unicast ALLOW fe80:0:0:1::1 -> getType() Link-local unicast ``` ### Remediation Upgrade to the patched release. In the fix, `isLinkLocal()` tests the address against `fe80::/10` with the same `isHostInSubnet` predicate `getType()` and `Address4.isLinkLocal()` use. The same release adds `2001::/32` to the type table so `getType()` reports `'Teredo'` for the addresses `isTeredo()` returns `true` for; that is a consistency correction with no security effect. If you cannot upgrade immediately, test the range directly: ```js const LINK_LOCAL = new Address6('fe80::/10'); const linkLocal = new Address6(host).isHostInSubnet(LINK_LOCAL); ``` ### A note on SSRF defense These methods are address classifiers, not a complete S
AI Analysis
Technical Summary
CVE-2026-101913 describes a vulnerability in the ip-address JavaScript library's IPv6 address handling. Specifically, the Address6 isLinkLocal method incorrectly limits link-local address recognition to the fe80::/64 subnet, whereas the full IPv6 link-local range is fe80::/10. This causes inconsistent classification between isLinkLocal and other methods like getType and getScope. An attacker can exploit this inconsistency to bypass trust-boundary checks that rely on isLinkLocal, potentially accessing on-link hosts that should be protected. The vulnerability is resolved in version 10.5.1.
Potential Impact
The vulnerability allows an attacker to bypass trust-boundary checks that depend on the isLinkLocal method, potentially enabling access to on-link hosts outside the intended trust boundary. This could lead to unauthorized network access or communication with hosts that should be restricted. The CVSS 4.0 base score is 6.3 (medium severity), reflecting network attack vector, low complexity, partial attack complexity, no privileges required, no user interaction, and limited confidentiality impact.
Mitigation Recommendations
Upgrade the ip-address library to version 10.5.1 or later, where the isLinkLocal method correctly recognizes the entire fe80::/10 IPv6 link-local range. This official fix resolves the inconsistent address classification and prevents trust-boundary bypass. No other mitigation is indicated by the vendor advisory.
CVE-2026-101913: CWE-697: Incorrect Comparison in beaugunderson ip-address
Description
### Summary `Address6.isLinkLocal()` recognizes `fe80::/64` rather than `fe80::/10`. Link-local unicast is the whole `/10` under RFC 4291 §2.4 and the IANA IPv6 Special-Purpose Address Registry, so the method returns `false` for every link-local address outside the one `/64` that stateless address autoconfiguration happens to use. `new Address6('fe81::1').isLinkLocal()` is `false`. The library contradicts itself on the same object: for `fe81::1`, `getType()` returns `'Link-local unicast'`, `getScope()` returns `'Link local'`, and `isHostInSubnet(new Address6('fe80::/10'))` returns `true`, while `isLinkLocal()` returns `false`. 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) will classify a link-local target as unremarkable 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. ### Details `isLinkLocal()` in `src/ipv6.ts` compares the first 64 bits of the address against a literal string: ```ts // Zeroes are required, i.e. we can't check isHostInSubnet with 'fe80::/10' if ( this.getBitsBase2(0, 64) === '1111111010000000000000000000000000000000000000000000000000000000' ) { return true; } ``` The comparison requires the first 64 bits to be exactly `fe80:0000:0000:0000`, so it accepts 2⁶⁴ of the 2¹¹⁸ addresses in `fe80::/10`. The comment states a premise the library disproves: `getType()` classifies the same range with `isHostInSubnet` against the `'fe80::/10': 'Link-local unicast'` entry in `src/v6/constants.ts`, and `Address4.isLinkLocal()` is a plain `isHostInSubnet` test against `169.254.0.0/16`. RFC 4291 §2.5.6 constrains the format of an autoconfigured link-local address; it does not define the range, and reading it as the definition is the likeliest origin of the `/64` comparison. The IPv4-mapped and NAT64 well-known paths of `isLinkLocal()` are unaffected: `::ffff:169.254.169.254` and `64:ff9b::a9fe:a9fe` are classified by their embedded IPv4 address and report `true`. ### Affected versions `<= 10.5.0`. The comparison has had this shape since `Address6.isLinkLocal()` was introduced, so every release exposing the method is affected. ### Impact Every well-formed address in `fe80::/10` outside `fe80::/64` is parsed successfully, `isValid()` is `true`, and the classifier reports something untrue about it. No other classifier catches these addresses: `isPrivate()` covers ULA (`fc00::/7`), not link-local. | Address | `isLinkLocal()` | `getType()` | `getScope()` | |---|---|---|---| | `fe80::1` | `true` | Link-local unicast | Link local | | `fe81::1` | `false` | Link-local unicast | Link local | | `fe8f::1` | `false` | Link-local unicast | Link local | | `febf::1` | `false` | Link-local unicast | Link local | | `fe80:0:0:1::1` | `false` | Link-local unicast | Link local | | `fe80::1:0:0:0:1` | `false` | Link-local unicast | Link local | Python's `ipaddress` module, the `IN6_IS_ADDR_LINKLOCAL` macro in `netinet6/in6.h`, and Linux's `ipv6_addr_type()` all apply a ten-bit prefix test and classify every row above as link-local. A request admitted through a guard built on `isLinkLocal()` reaches a link-local host on the server's own segment: a neighboring machine or the on-link router. An IPv6 link-local destination generally needs a zone index and a neighbor on the same link, so the reach is the server's own segment rather than the internet or a universal metadata endpoint, and the severity reflects that. ### Proof of concept `npm i [email protected]`, then: ```js const { Address6 } = require('ip-address'); // A guard of the shape the library documents. function isBlocked(host) { const a = new Address6(host); return a.isLoopback() || a.isLinkLocal() || a.isPrivate() || a.isMulticast() || a.isUnspecified(); } for (const h of ['fe80::1', 'fe81::1', 'febf::1', 'fe80:0:0:1::1']) { const a = new Address6(h); console.log(isBlocked(h) ? 'BLOCK' : 'ALLOW', h, '-> getType()', a.getType()); } ``` On affected versions: ``` BLOCK fe80::1 -> getType() Link-local unicast ALLOW fe81::1 -> getType() Link-local unicast ALLOW febf::1 -> getType() Link-local unicast ALLOW fe80:0:0:1::1 -> getType() Link-local unicast ``` ### Remediation Upgrade to the patched release. In the fix, `isLinkLocal()` tests the address against `fe80::/10` with the same `isHostInSubnet` predicate `getType()` and `Address4.isLinkLocal()` use. The same release adds `2001::/32` to the type table so `getType()` reports `'Teredo'` for the addresses `isTeredo()` returns `true` for; that is a consistency correction with no security effect. If you cannot upgrade immediately, test the range directly: ```js const LINK_LOCAL = new Address6('fe80::/10'); const linkLocal = new Address6(host).isHostInSubnet(LINK_LOCAL); ``` ### A note on SSRF defense These methods are address classifiers, not a complete S
CVSS v4.0
Score 6.3medium
Affected software
beaugunderson
ip-address
Run on your own infrastructure? Check whether these packages are installed with threat-finder — our free open-source scanner.
AI-Powered Analysis
Machine-generated threat intelligence
Technical Analysis
CVE-2026-101913 describes a vulnerability in the ip-address JavaScript library's IPv6 address handling. Specifically, the Address6 isLinkLocal method incorrectly limits link-local address recognition to the fe80::/64 subnet, whereas the full IPv6 link-local range is fe80::/10. This causes inconsistent classification between isLinkLocal and other methods like getType and getScope. An attacker can exploit this inconsistency to bypass trust-boundary checks that rely on isLinkLocal, potentially accessing on-link hosts that should be protected. The vulnerability is resolved in version 10.5.1.
Potential Impact
The vulnerability allows an attacker to bypass trust-boundary checks that depend on the isLinkLocal method, potentially enabling access to on-link hosts outside the intended trust boundary. This could lead to unauthorized network access or communication with hosts that should be restricted. The CVSS 4.0 base score is 6.3 (medium severity), reflecting network attack vector, low complexity, partial attack complexity, no privileges required, no user interaction, and limited confidentiality impact.
Mitigation Recommendations
Upgrade the ip-address library to version 10.5.1 or later, where the isLinkLocal method correctly recognizes the entire fe80::/10 IPv6 link-local range. This official fix resolves the inconsistent address classification and prevents trust-boundary bypass. No other mitigation is indicated by the vendor advisory.
Technical Details
- Data Version
- 5.2
- Assigner Short Name
- GitHub_M
- Date Reserved
- 2026-09-28T15:55:37.907Z
- Cvss Version
- 4.0
- State
- PUBLISHED
Threat ID: 6abaabe3f7a7c54106053ebc
Added to database: 09/28/2026, 18:03:15 UTC
Last enriched: 09/28/2026, 18:17:52 UTC
Last updated: 09/29/2026, 04:41:54 UTC
Views: 12
Community Reviews
0 reviewsCrowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.
Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.
Actions
Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.
Need more coverage?
Upgrade to Pro Console for AI refresh and higher limits.
For incident response and remediation, OffSeq services can help resolve threats faster.
Latest Threats
Check if your credentials are on the dark web
Instant breach scanning across billions of leaked records. Free tier available.