Repository navigation
feat: add security headers and Cache-Control: no-store to API responses - #254
Conversation
aditeyabaral
left a comment
There was a problem hiding this comment.
Thanks for picking this up! The middleware does what #191 asks: I checked 200/400/401/404/405/307/308, HEAD /health, /openapi.json and the playground with TestClient, and the headers are where they should be.
One change before merge (inline on app/app.py): the README says every response carries the security headers, but an unhandled 500 currently carries none, including Cache-Control: no-store on /authenticate. Please set them on that response too, so the README holds.
The rest are smaller and inline. Two points that aren't tied to a line:
- Commit message: the commit has no body. The contribution guide asks for Conventional Commits with a body that explains why, so please add one when you push the fixes.
- PR template: "🧰 Dependency update" is ticked, but the only
pyproject.toml/uv.lockchange is the version bump. Please untick it.
If another PR merges into dev before this one, you'll need to bump the version again from the new one.
…he-Control - Attach security headers and Cache-Control: no-store directly in unhandled_exception_handler so 500 responses from ServerErrorMiddleware carry them. - Document Cache-Control: no-store in authenticate OpenAPI schema. - Add integration tests for unhandled 500 responses on general routes and /authenticate. - Relocate Response headers section in README above endpoint descriptions.
aditeyabaral
left a comment
There was a problem hiding this comment.
Thanks, everything from the last round is addressed, and I've resolved those threads. I checked the new behaviour with TestClient: an unhandled 500 on /authenticate now carries all four headers, the OpenAPI schema documents Cache-Control on every /authenticate response, and the full suite passes with live credentials at 100% coverage.
One small thing is left, inline: the docstring on _security_headers. Once that's in, this is good to go.
Co-authored-by: Aditeya Baral <aditeya.baral@gmail.com>
📌 Description
What is the purpose of this PR?
Adds standard HTTP security headers across all API responses and applies
Cache-Control: no-storeto the/authenticateendpoint.What problem does it solve, or what feature does it add?
The
/authenticateendpoint returns sensitive student profile data without instructions preventing intermediate caches, proxies, or browsers from storing it. The API also previously lacked baseline security headers recommended by the OWASP REST Security Cheat Sheet. This PR adds asecurity_headers_middlewareto:X-Content-Type-Options: nosniff,Referrer-Policy: no-referrer, andX-Frame-Options: DENYto every response.Cache-Control: no-storespecifically to/authenticateresponses (both success and error outcomes).Any relevant motivation, background, or context?
Offline integration tests have been added with monkeypatching to maintain 100% test coverage without requiring live credentials.
🧱 Type of Change
pyproject.toml,uv.lock🧪 How Has This Been Tested?
tests/unit/)tests/functional/)tests/integration/)✅ Checklist
scripts/run_tests.py)pre-commit run --all-files).envvars updated (if applicable)scripts/benchmark/benchmark_requests.py)🛠️ Affected API Behaviour
app/app.py– Modified/authenticateroute logicapp/pesu.py– Updated PESU Academy login or profile handlingapp/exceptions/– Error types, their status codes or messagesapp/metrics/– Metrics collection, exposition or the/metricstokenapp/docs/– OpenAPI (Swagger) examples and descriptions🧩 Models
app/models/request.py– Input validation or request schema changesapp/models/response.py– Authentication response formattingapp/models/profile.py– Profile response schemaapp/models/upstream.py– Parsing of PESU Academy's login and profile responses🐳 DevOps & Config
Dockerfile– Changes to base image or build process.github/workflows/*.yml– CI/CD pipeline or deployment updatespyproject.toml/uv.lock– Dependency version changes.pre-commit-config.yaml– Linting or formatting hook changes.github/scripts/check_version_bump.py– The CI version check📊 Benchmarks & Analysis
scripts/benchmark/benchmark_requests.py– Performance or latency measurement changesscripts/benchmark/analyze_benchmark.py– Benchmark result analysis changesscripts/run_tests.py– Custom test runner logic or behavior updates🧠 Additional Notes (if applicable)
Closes #191.
Live PESU credential tests were skipped locally due to missing local secrets; offline integration tests were added to verify the headers while preserving 100% test coverage.
Handled exceptions (4xx, 502) pass through the middleware and carry headers, while unhandled 500 exceptions are rendered above Starlette HTTP middleware as per framework design.
What Changed:
Fixes: #191underFixes / Related Issuesand explicitly addedCloses #191underAdditional Notes.- [x]and- [ ]) and standard markdown blockquotes (>) without broken raw HTML tags or missing line breaks.