feat(trusted_endpoints): support {id} and {path:path} placeholders#15
Closed
aural-psynapse wants to merge 1 commit into
Closed
feat(trusted_endpoints): support {id} and {path:path} placeholders#15aural-psynapse wants to merge 1 commit into
aural-psynapse wants to merge 1 commit into
Conversation
… registered URLs
A registered URL may now contain FastAPI/Express-style path placeholders so a
single entry covers a family of concrete URLs:
{name} - matches exactly one path segment (no '/').
e.g. https://api.example.com/customers/{id} matches
/customers/42 but NOT /customers/42/orders.
{name:path} - matches any subtree, including '/' separators.
e.g. https://api.example.com/customers/{rest:path} matches
both /customers/42 and /customers/42/orders.
Closes #14.
Why: customer-support-sdk-demo had to enumerate ~70 concrete URLs at startup
for templated routes (/customers/{id}). Runtime-generated ids (e.g. POST
/tickets returning a fresh id) couldn't be trusted until manually registered.
A single placeholder entry replaces the enumeration.
Implementation:
- Plain URLs without '{' keep exact-match semantics. No schema change. No
migration needed for existing rows. Existing exact-match tests unchanged.
- Pattern matching is auto-detected from URL content. Pattern compilation is
LRU-cached so repeated lookups don't recompile the regex.
- is_trusted_endpoint uses a two-phase lookup: exact match first (single
indexed query, fast path), then a pattern-only scan (LIKE '%{%' filter)
for rows containing placeholders. Plain registries see no perf regression.
- The snapshot tamper-check inside check_claim_endpoints_are_trusted honors
the same syntax — a payload built against a pattern entry verifies cleanly
on the receiver side.
Tests: 12 new (94 total). Ruff clean.
Contributor
Author
|
Bundled into #17 with the API key onboarding docs and the cluster_b cleanup, per request. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #14.
What
A registered URL in
trusted_endpointsmay now contain FastAPI/Express-style path placeholders so one entry covers a family of concrete URLs:{name}/)https://api.example.com/customers/{id}matches…/customers/42but not…/customers/42/orders{name:path}/separatorshttps://api.example.com/customers/{rest:path}matches both…/customers/42and…/customers/42/ordersThe placeholder name (
id,rest, …) is descriptive; matching is content-driven (any URL containing{is treated as a pattern). Plain URLs without{keep exact-match semantics — zero migration, no schema change, all existing rows behave exactly as before.Why (issue summary)
customer-support-sdk-demo had to enumerate ~70 concrete URLs at startup (5 customers × 5 services × multiple per-id resources). Runtime-generated ids (e.g.
POST /ticketsreturning a fresh ticket id) could not be trusted at all until manually re-registered.One pattern entry replaces the whole enumeration:
Implementation
_compile_pattern(registered)and_matches_registered(claim_url, registered)insrc/provably/trusted_endpoints.py. Compilation is LRU-cached so repeated lookups don't recompile.is_trusted_endpointis now two-phase:(org_id, normalized_url)row (unchanged from before).WHERE normalized_url LIKE '%{%'to fetch pattern rows for the org and test each in Python.check_claim_endpoints_are_trustedruns the same matching logic, so a payload built with pattern entries also verifies cleanly on the receiver side.{) skip the slow path entirely — registries that don't use patterns see no perf regression.Why FastAPI/Express syntax (vs path-prefix or glob)
Issue #14 listed three candidates: pattern with
{id}, host-only, path-prefix. Path-prefix is easier to implement but broader —/customerswould also trust/customers/anything/under/it/forever, which is a security regression for users who only meant to allow per-id routes. The{id}syntax is the same shape every popular Python web framework already uses (FastAPI, Starlette, Flask via converters), and{rest:path}is FastAPI's native escape hatch when you genuinely want subtree-wide trust. Versatile, industry-standard, hard to misuse.Tests
12 new tests, 94 total. Coverage:
_compile_patternreturnsNonefor plain URLs (no perf cost when patterns aren't used).{id}matches one segment, refuses to swallow extra path components.{rest:path}matches subtree-wide.is_trusted_endpointtwo-phase lookup: exact-match wins on first query, pattern-match wins on second query, no match returnsFalse.check_claim_endpoints_are_trustedhonors patterns the same way — accepts a claim URL covered by a pattern in the registry, rejects one outside the pattern.Migration
None. Existing exact-match entries are unchanged. The new behavior is fully opt-in per row.
Branch
Off
main.