feat(egress-proxy): retarget remediation flow (PRD 0017 chunk 3) #30

Merged
didericis merged 18 commits from egress-proxy-block-remediation into main 2026-05-25 20:34:24 -04:00
2 changed files with 18 additions and 9 deletions
Showing only changes of commit 6177c0518e - Show all commits
+12 -7
View File
@@ -174,10 +174,15 @@ def match_route(
Match precedence: Match precedence:
1. Exact (case-insensitive) match on the literal hostname. 1. Exact (case-insensitive) match on the literal hostname.
2. Wildcard match: a route whose host starts with `*.` is a 2. Wildcard match: a route whose host starts with `*.` is a
suffix pattern. `*.example.com` matches any host that suffix pattern that covers the apex AND every subdomain.
ends with `.example.com` (so `foo.example.com` and `*.example.com` matches `example.com`, `foo.example.com`,
`a.b.example.com`, but NOT the apex `example.com` or and `a.b.example.com`, but NOT `barexample.com` (the
`barexample.com`). label boundary `.` is required when matching a
subdomain). This is intentionally more permissive than
RFC 6125 TLS-wildcard semantics — an allowlist's natural
reading of `*.example.com` is "all of example.com",
apex included, and matches what the pipelock mirror does
(strips `*.example.com` → `example.com`).
Exact match wins over wildcard so an operator can declare a Exact match wins over wildcard so an operator can declare a
specific route on top of a broader wildcard (e.g. a specific route on top of a broader wildcard (e.g. a
@@ -189,12 +194,12 @@ def match_route(
host = r.host.lower() host = r.host.lower()
if not host.startswith("*.") and host == target: if not host.startswith("*.") and host == target:
return r return r
# Pass 2: wildcard suffix match (`*.foo.com` → `.foo.com`). # Pass 2: wildcard match — apex + every subdomain.
for r in routes: for r in routes:
host = r.host.lower() host = r.host.lower()
if host.startswith("*."): if host.startswith("*."):
suffix = host[1:] # keeps the leading `.` suffix = host[2:] # strip the `*.`
if target.endswith(suffix) and target != suffix[1:]: if target == suffix or target.endswith("." + suffix):
return r return r
return None return None
+6 -2
View File
@@ -162,9 +162,13 @@ class TestMatchRouteWildcards(unittest.TestCase):
routes = (Route(host="*.example.com"),) routes = (Route(host="*.example.com"),)
self.assertIsNotNone(match_route(routes, "a.b.example.com")) self.assertIsNotNone(match_route(routes, "a.b.example.com"))
def test_wildcard_does_not_match_apex(self): def test_wildcard_matches_apex(self):
# Allowlist semantics: `*.example.com` covers
# `example.com` itself + every subdomain. Matches what
# the pipelock mirror does (strips `*.example.com` →
# `example.com`) so the two layers agree.
routes = (Route(host="*.example.com"),) routes = (Route(host="*.example.com"),)
self.assertIsNone(match_route(routes, "example.com")) self.assertIsNotNone(match_route(routes, "example.com"))
def test_wildcard_does_not_match_overlapping_suffix(self): def test_wildcard_does_not_match_overlapping_suffix(self):
# `*.example.com` shouldn't match `barexample.com` — the # `*.example.com` shouldn't match `barexample.com` — the