Repository navigation
Close out review findings: path traversal, redirects, secrets, timeouts, DB reconnect, and doc accuracy - #73
Merged
Conversation
…ts, DB reconnect, and doc accuracy P1 security fixes: - Reject `.`/`..` path segments before catalog validation so a traversal path cannot normalize to a different upstream path than the one authorized (#48). - Disable automatic redirects and add connect/read timeouts on the shared HTTP client, so a redirect can't escape catalog validation and a slow upstream can't hold a request open indefinitely (#49, #51). - Reject empty required config values (SERVER_SECRET, DATABASE_URL, etc.) instead of accepting them, and treat an empty SESSION_SECRET as unset rather than hashing it into a reproducible key (#50). - Stream and bound the upstream response body incrementally instead of buffering the whole thing before checking the size limit (#51). P2 fixes: - Add a bounded OpenAPI request validator (required query parameters, enum values, request body presence/content-type) wired into the proxy forwarding path (#52). - Give `Security` a self-healing PostgreSQL connection: a dropped connection is retried with backoff and the shared client is swapped in automatically, with a new `/healthz` readiness endpoint (#53). - Opportunistically clean up expired `oauth_states` and `connection_codes` rows (matching the existing pattern for other tables), plus supporting indexes (#54). - Forward `ETag`, `X-Total-Count`, and `X-Next-Page` response headers to match what CORS already exposes (#55). - Add end-to-end coverage of `forward()`: credential refresh, upstream call, header forwarding, and connection-code rotation (#56). - Add regression coverage of the OAuth profiles catalog against the application's actual default `CATALOG_PATH` pin, not just a separately identified revision (#57). - Correct README/SECURITY.md/.env.example inaccuracies: the no-database framing, the removed "token storage unimplemented" claim, browser handoff vs. legacy proxy credentials, the nonexistent GitHub pagination rewrite, .env auto-loading, missing DATABASE_URL/ENCRYPTION_KEY example values, the Spotify client auth method, and the stale five-minute proxy credential expiry (#58, #60, #61, #62, #63, #64, #65). - Replace hardcoded "Atomic Data Hub" consent wording with generic destination wording, and drop the obsolete two-provider description now that OAuth is fully catalog-driven (#66). Also fixes 3 pre-existing clippy findings (identity.rs, providers.rs) surfaced by the current toolchain, needed to keep `-D warnings` green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UCidwbyQwtBvFwHyreivf
This was referenced Sep 16, 2026
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.
Summary
Works through the 20 open review-finding issues (#48–#66, #72). All P1 and P2 issues with a real code or doc fix are addressed below; #59 and #72 are closed as already resolved on
main(no code change needed — see notes).P1 (security)
./..path segments before catalog validation, so a request like/repositories/../issuescan't pass template matching and then normalize to a different upstream path than the one authorized.reqwest::redirect::Policy::none()), matching the pattern already used for the identity client, so a redirect can't send a request to a destination the catalog never validated.SERVER_SECRET,DATABASE_URL,ENCRYPTION_KEY, theAPP_AUTH_*values) instead of silently accepting them; an emptySESSION_SECRETis now treated as unset (triggering random key generation) instead of being hashed into a reproducible, guessable key.Response::chunk()), rejecting as soon as the 10 MiB limit is crossed instead of buffering the whole body first.P2 (code)
Catalog::validate_request: a bounded, explicit check that a declared required query parameter is present, an enum-constrained parameter's value is one of the declared values, and a request body's presence/content-type match the operation's declaredrequestBody. Documented as an explicit bounded subset inSECURITY.md(not full JSON Schema validation).Securitynow holds its PostgreSQL client behindArc<RwLock<Arc<Client>>>with a background supervisor that reconnects with exponential backoff and swaps the client in automatically. AddedSecurity::is_ready()and a/healthzendpoint. Covered by an ignored test that kills the backend viapg_terminate_backendand confirms the connection self-heals.oauth_statesandconnection_codesnow get the same opportunisticDELETE ... WHERE expires_at <= NOW()cleanup already used forused_challenges/connection_handoffs, plus supporting indexes.ETag,X-Total-Count, andX-Next-Pagefrom the upstream response, matching what CORS already exposes to browser clients.forward()through the real router: an expired credential is refreshed at a mocked token endpoint, the refreshed token is used against a mocked provider API, the response (with its ETag/Link headers) is forwarded, and the one-time connection code is rotated and verified.CATALOG_PATHpin (now a sharedconfig::DEFAULT_CATALOG_PATHconstant) and asserts the OAuth profiles it supplies; the existing test against a separately-pinned revision is now clearly labeled as such.P2 (docs)
main; the "token storage/forwarding unimplemented" claim is gone. Closing with no code change.connection_codeflow from the browser bootstrap flow's handoff-then-redeem, and removed the false claim that GitHub pagination links get rewritten (no such code exists;Linkis forwarded verbatim)..envisn't auto-loaded; added an explicitsource/set -astep.DATABASE_URL/ENCRYPTION_KEY(with a generation hint) to.env.example.OAUTH_SPOTIFY_CLIENT_AUTH_METHOD=noneto.env.exampleand the README's Spotify section.SECURITY.md's stale "five-minute" proxy-credential expiry to ten minutes (matchingstore_connection_code), keeping the handoff's separate five-minute lifetime explicit.default_catalog_pin_...test ([P2] Test OAuth profiles against the application default catalog revision #57 above) further confirmsoauth_provider("notion")resolves against the live default. Nointegration-proxycode change is required per the issue's own hand-off framing. Closing with no code change.Also fixes 3 pre-existing
clippy::nonminimal_boolfindings (identity.rs,providers.rs) surfaced by the current toolchain — needed to keepcargo clippy --all-targets --all-features -- -D warningsgreen for this PR's CI run.Test plan
cargo fmt --all -- --checkcargo clippy --all-targets --all-features -- -D warningscargo test(91 passed)cargo test -- --include-ignoredagainst a local PostgreSQL instance and live network (111 passed), including the new reconnect, e2e forwarding, and default-catalog-pin testsFixes #48, #49, #50, #51, #52, #53, #54, #55, #56, #57, #58, #59, #60, #61, #62, #63, #64, #65, #66, #72
🤖 Generated with Claude Code
https://claude.ai/code/session_013UCidwbyQwtBvFwHyreivf
Generated by Claude Code