Skip to content

Report the whole fe80::/10 range from Address6.isLinkLocal() - #223

Closed
spokodev wants to merge 2 commits into
beaugunderson:mainfrom
spokodev:fix-islinklocal-fe80-range
Closed

Report the whole fe80::/10 range from Address6.isLinkLocal()#223
spokodev wants to merge 2 commits into
beaugunderson:mainfrom
spokodev:fix-islinklocal-fe80-range

Conversation

@spokodev

Copy link
Copy Markdown
Contributor

Address6.isLinkLocal() under-reports the IANA link-local range fe80::/10. It returns true only when the interface-prefix bits (10–63) are all zero, i.e. fe80::/64, so every other address in the range reads as non-link-local:

new Address6('fe80::1').isLinkLocal();     // true
new Address6('fe80:1::1').isLinkLocal();   // false  <-- fe80::/10, but reported false
new Address6('fe90::1').isLinkLocal();     // false
new Address6('fea0::1').isLinkLocal();     // false
new Address6('febf::1').isLinkLocal();     // false

This contradicts the library's own getType(), which already classifies all of those as Link-local unicast:

new Address6('fe80:1::1').getType();       // 'Link-local unicast'
new Address6('fe80:1::1').isLinkLocal();   // false — disagrees

getType() reaches that verdict through isHostInSubnet('fe80::/10'), so the inline comment on isLinkLocal() — "we can't check isHostInSubnet with 'fe80::/10'" — is not accurate.

The fix

Replace the hard-coded 64-bit prefix compare with isHostInSubnet(LINK_LOCAL_UNICAST_SUBNET), mirroring getType() and the sibling predicates (isULA() uses isHostInSubnet(ULA_SUBNET)). The fe80::/10 subnet is added to the existing block of module-level subnet constants. The IPv4-mapped delegation at the top of the method is preserved.

The change is a strict superset of the previous behaviour: every input that returned true still returns true, fec0::1 (site-local) and global addresses stay false.

History

getScope()/getType() were broadened to the full fe80::/10 in #200 (closing #122), and the boolean predicates were added in #197, but isLinkLocal() kept its narrower fe80::/64 check, so the two have disagreed since. This aligns them.

Tests

Added a case in the existing isLinkLocal block asserting the whole fe80::/10 range (including a zone-id form) reports true and matches getType(), plus a negative fec0::1. Fails before the change, passes after; full suite 3595 passing.

isLinkLocal() only returned true for fe80::/64 with an all-zero
interface prefix, so fe80:1::1, fe90::1, fea0::1 and febf::1 all read
as non-link-local even though getType() classifies the same addresses
as 'Link-local unicast' via isHostInSubnet('fe80::/10'). The stale
comment claiming isHostInSubnet can't be used for fe80::/10 was
disproven by getType() already doing exactly that.

Match getType() by testing membership in fe80::/10. fec0::1 and global
addresses are unaffected, and the change is a strict superset of the
previous result.
@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (5e3ceb7) to head (b0aedb1).
⚠️ Report is 12 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #223   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            6         6           
  Lines          370       373    +3     
  Branches       216       218    +2     
=========================================
+ Hits           370       373    +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@beaugunderson

Copy link
Copy Markdown
Owner

Thanks, and sorry for the overlap: the same fix shipped in 10.5.1 through GHSA-rpw4-54j3-4h4q, which was filed a few days after you opened this. Closing as superseded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants