Skip to content

fix: set baseline security headers on every API response (#251-#254) - #585

Merged
Markadrian6399 merged 2 commits into
StellarGateLabs:mainfrom
devgrace100:fix/security-headers-api-global-v2
Aug 27, 2026
Merged

fix: set baseline security headers on every API response (#251-#254)#585
Markadrian6399 merged 2 commits into
StellarGateLabs:mainfrom
devgrace100:fix/security-headers-api-global-v2

Conversation

@devgrace100

Copy link
Copy Markdown

Summary

Only the dashboard_asset handler 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's set-header feature 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 /merchants returns a plaintext API key exactly once and GET /payments/:id returns payment detail; neither should be cached by an intermediary or written to browser disk cache. if_not_present preserves a handler that legitimately opts into caching.
  • Strict-Transport-Security (if-not-present, conditional) — emitted only on public network 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-Policy is preserved, not overwritten.

Acceptance criteria

  • nosniff, Referrer-Policy, and Cache-Control: no-store set on API responses
  • HSTS emitted on public-network deployments only
  • Dashboard's stricter CSP preserved, not overwritten
  • Tests assert the headers on a representative API response (/payments/does-not-exist), HSTS per network, and CSP preservation on /dashboard

Note on scope

main did not compile at head when this work started (several referenced-but-missing support functions/config fields, a stale migrate() 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:

Closes

closes #251
closes #252
closes #253
closes #254

…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).
@drips-wave

drips-wave Bot commented Aug 27, 2026

Copy link
Copy Markdown

@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! 🚀

Learn more about application limits

@Markadrian6399
Markadrian6399 merged commit e64e1cd into StellarGateLabs:main Aug 27, 2026
2 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants