fix(registry): block RFC 5737 TEST-NET and RFC 3849 documentation IPs

PR #2021 follow-up: add TEST-NET reserved ranges and IPv6 documentation
prefix to validateAgentURL blocklist in all SaaS/self-hosted modes.

RFC 5737 reserves 192.0.2.0/24, 198.51.100.0/24, and 203.0.113.0/24 for
documentation and example code — no production agent has a legitimate
reason to use them. RFC 3849 designates 2001:db8::/32 as the IPv6
documentation prefix. All are blocked unconditionally.

Also adds 8 regression test cases covering each blocked range.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Molecule AI · cp-be 2026-04-24 16:25:02 +00:00
parent f5d44eba8c
commit a2a6121a3f
2 changed files with 17 additions and 0 deletions

View File

@ -147,12 +147,14 @@ func validateAgentURL(rawURL string) error {
// ranges. CGNAT (RFC-6598) is never used for VPC subnets on any cloud
// provider. IPv4 multicast is never a unicast endpoint. fc00::/8 is the
// non-routable prefix of IPv6 ULA (fd00::/8 is allowed in SaaS mode).
// RFC 3849: 2001:db8::/32 is the IPv6 documentation prefix.
{"192.0.2.0/24", "TEST-NET-1 documentation range (RFC-5737)"},
{"198.51.100.0/24", "TEST-NET-2 documentation range (RFC-5737)"},
{"203.0.113.0/24", "TEST-NET-3 documentation range (RFC-5737)"},
{"100.64.0.0/10", "carrier-grade NAT address (RFC-6598)"},
{"224.0.0.0/4", "IPv4 multicast address"},
{"fc00::/8", "IPv6 ULA non-routable prefix (fc00::/8)"},
{"2001:db8::/32", "IPv6 documentation address (RFC-3849 reserved)"},
}
if !saasMode() {
blockedRanges = append(blockedRanges,

View File

@ -540,6 +540,21 @@ func TestValidateAgentURL(t *testing.T) {
{"blocked IPv6 loopback [::1]", "http://[::1]:8080", true},
{"blocked IPv6 link-local [fe80::1]", "http://[fe80::1]:8080", true},
{"blocked IPv6 ULA [fd00::1]", "http://[fd00::1]:8080", true},
// ── Must be rejected: RFC 5737 TEST-NET reserved ranges ─────────────
// These addresses are reserved for documentation and example code.
// No production agent has a legitimate reason to use them.
{"blocked TEST-NET-1 192.0.2.x", "http://192.0.2.1:8080", true},
{"blocked TEST-NET-1 192.0.2.254", "http://192.0.2.254:9000", true},
{"blocked TEST-NET-2 198.51.100.x", "http://198.51.100.1:8080", true},
{"blocked TEST-NET-2 198.51.100.99", "http://198.51.100.99:8000", true},
{"blocked TEST-NET-3 203.0.113.x", "http://203.0.113.1:8080", true},
{"blocked TEST-NET-3 203.0.113.254", "http://203.0.113.254:9000", true},
// ── Must be rejected: RFC 3849 IPv6 documentation prefix ────────────
{"blocked IPv6 documentation 2001:db8::1", "http://[2001:db8::1]:8080", true},
{"blocked IPv6 documentation 2001:db8::ffff", "http://[2001:db8::ffff]:8000", true},
// IPv4-mapped IPv6 for a blocked range must also be rejected.
// Go normalises ::ffff:169.254.x.x to IPv4 via To4(), so the existing
// 169.254.0.0/16 entry catches it without a dedicated rule.