Skip to content

safe-url's SSRF IPv4 guard is missing the RFC 6598 CGNAT range (100.64.0.0/10) #7253

Description

@JSONbored

Context

packages/loopover-engine/src/review/safe-url.ts is a self-contained SSRF-safe URL guard, ported from reviewbot's core/source-url.ts. ipv4IsPrivateOrLocal (line 45-52) rejects a set of private/reserved IPv4 ranges by decoding the address to an integer and checking each documented block:

function ipv4IsPrivateOrLocal(host: string): boolean {
  const n = ipv4ToInt(host);
  if (n === null) return false;
  const a = (n >>> 24) & 0xff;
  const b = (n >>> 16) & 0xff;
  if (a === 0 || a === 10 || a === 127) return true; // 0.0.0.0/8, 10/8, loopback
  if (a === 169 && b === 254) return true; // link-local (incl. cloud metadata 169.254.169.254)
  if (a === 192 && b === 168) return true;
  if (a === 172 && b >= 16 && b <= 31) return true;
  return false;
}

This covers 0.0.0.0/8, 10.0.0.0/8, 127.0.0.0/8 (loopback), 169.254.0.0/16 (link-local, explicitly called out for covering the cloud-metadata endpoint), 192.168.0.0/16, and 172.16.0.0/12. It does not check 100.64.0.0/10 (RFC 6598, "Shared Address Space" / Carrier-Grade NAT) — a range reserved specifically for service-provider address sharing, which is exactly the kind of internal, non-publicly-routable range this guard exists to block, in the same category as the five ranges already checked. Real cloud/hosting environments assign 100.64.0.0/10 addresses to internal service endpoints (it's used by several major cloud providers for exactly this purpose), so a URL resolving to a 100.64.x.x100.127.x.x host currently passes this guard's IPv4 check as if it were a normal public address.

This module is explicitly a "SELF-CONTAINED NATIVE PORT" with "no imports, no I/O" (file header) — the fix is a pure, additive range check following the identical pattern already used for every other private range in the same function; it does not touch the guard's overall design, its call sites, or any other part of the SSRF protection surface (hostname checks, IPv6 handling, encoded-IP decoding are all unaffected).

Requirements

  • Add a check for 100.64.0.0/10 (a === 100 && b >= 64 && b <= 127) to ipv4IsPrivateOrLocal, following the exact same inline style and range-comment convention already used for the five existing checks in this function.
  • Do not change any other range check, the function's signature, or any other function in this file.
  • Since this is a security-relevant guard (SSRF protection), the PR must not touch anything beyond this one additive range check — no refactoring of the surrounding decode/parse logic in the same change.

Deliverables

  • ipv4IsPrivateOrLocal in packages/loopover-engine/src/review/safe-url.ts rejects 100.64.0.0/10 (RFC 6598 CGNAT/Shared Address Space) the same way it already rejects the other five private ranges
  • Regression tests covering the new range's boundaries: 100.63.255.255 (just below the range, must NOT be flagged private), 100.64.0.0 and 100.127.255.255 (the range's inclusive bounds, must be flagged private), 100.128.0.0 (just above the range, must NOT be flagged private) — matching the boundary-testing style already used for the file's other range checks in its existing test suite

Test Coverage Requirements

This repo's Codecov patch gate is 99%+ hard (branch-counted) on every changed line/branch in src/**/packages/**. The four boundary-value regression tests above must all be present and passing; this is a security-relevant module so boundary precision matters — an off-by-one here would either fail to close the gap or wrongly reject legitimate public addresses adjacent to the range.

Expected Outcome

A URL resolving to a 100.64.0.0/10 host is now correctly rejected by ipv4IsPrivateOrLocal, closing an SSRF gap in the same guard that already protects against the other standard private/reserved IPv4 ranges.

Links & Resources

packages/loopover-engine/src/review/safe-url.ts:45-52 (ipv4IsPrivateOrLocal, the function to extend). RFC 6598 defines 100.64.0.0/10 as Shared Address Space / Carrier-Grade NAT range.

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions