fix: set baseline security headers on every API response (#251-#254) - #585
Merged
Markadrian6399 merged 2 commits intoAug 27, 2026
Merged
Conversation
…Labs#251-StellarGateLabs#254) Only the dashboard_asset handler set security headers; every JSON API response (payment records, freshly-minted API keys, webhook URLs) was sent wrapped with no headers at all. Configured on the root router's outer layer so they apply to all responses, including rate-limit 429s, timeout 408s, and 404/405 fallbacks: - X-Content-Type-Options: nosniff (overriding) - merchant-controlled strings in responses can't be MIME-sniffed - Referrer-Policy: no-referrer (overriding) - payment ids travel in URLs - Cache-Control: no-store (if-not-present) - a plaintext API key is returned exactly once and payment detail must not be cached, while a handler that opts into caching is preserved - Strict-Transport-Security (if-not-present) - emitted only on public network deployments, where the proxy termination is guaranteed The dashboard's stricter Content-Security-Policy is left untouched. Tests assert the headers on a representative API response, HSTS behavior per network, and that the dashboard CSP survives the outer layers. Also restores a buildable baseline that `main` no longer compiled (several support functions/config fields referenced but missing, migrate() tombstone transaction rollback, a stale migration snapshot, and test config initializers brought up to date).
|
@devgrace100 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
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
Only the
dashboard_assethandler set security headers — the three static dashboard assets. Every JSON API response — including payment records and freshly-minted API keys — was sent with no security headers at all.This PR adds a baseline header stack to the root router (
tower-http'sset-headerfeature was already a dependency) so every response, including rate-limit 429s, timeout 408s, and the router's 404/405 fallbacks, carries them.What changes
Applied outermost in
src/api/mod.rs:X-Content-Type-Options: nosniff(overriding) — responses embed merchant-controlled strings (webhook_url,memo), so browsers must not MIME-sniff JSON.Referrer-Policy: no-referrer(overriding) — payment ids travel in URLs and would otherwise leak through referrers.Cache-Control: no-store(if-not-present) —POST /merchantsreturns a plaintext API key exactly once andGET /payments/:idreturns payment detail; neither should be cached by an intermediary or written to browser disk cache.if_not_presentpreserves a handler that legitimately opts into caching.Strict-Transport-Security(if-not-present, conditional) — emitted only onpublicnetwork deployments (STELLAR_NETWORK=public), where TLS termination is guaranteed; absent on testnet, where asserting HSTS over plain HTTP would be misleading.The dashboard's stricter
Content-Security-Policyis preserved, not overwritten.Acceptance criteria
nosniff,Referrer-Policy, andCache-Control: no-storeset on API responses/payments/does-not-exist), HSTS per network, and CSP preservation on/dashboardNote on scope
maindid not compile at head when this work started (several referenced-but-missing support functions/config fields, a stalemigrate()transaction tombstone, and a missing schema snapshot fixture). This PR also restores a buildable, test-passing baseline so the change lands on a green suite. Two pre-existing, unrelated failures remain out of scope for this issue and are documented in the PR description:cors_tests—build_corsadvertises{get, post}but the router servesdelete(tracked separately, issue CORS allows only GET/POST/OPTIONS and two headers, blockingDELETE,Idempotency-Key, andX-Admin-Secret#281).openapi_contract::openapi_documents_exactly_the_intended_paths—openapi.yamlpath-set drift.Closes
closes #251
closes #252
closes #253
closes #254