From 784c386ad48efcd7403eb82c8878357927ece4d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 24 Jan 2026 18:11:33 +0000 Subject: [PATCH 1/3] Initial plan From 989bac7ac3e574343b6e4b4299028368216f40a2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 24 Jan 2026 18:18:08 +0000 Subject: [PATCH 2/3] Add HSTS header to prevent man-in-the-middle attacks Co-authored-by: gkorland <753206+gkorland@users.noreply.github.com> --- api/app_factory.py | 8 ++++++++ tests/test_hsts_header.py | 43 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/test_hsts_header.py diff --git a/api/app_factory.py b/api/app_factory.py index c3920487..e26509a8 100644 --- a/api/app_factory.py +++ b/api/app_factory.py @@ -43,6 +43,14 @@ async def dispatch(self, request: Request, call_next): return JSONResponse(status_code=403, content={"detail": "Forbidden"}) response = await call_next(request) + + # Add HSTS header to prevent man-in-the-middle attacks + # max-age=31536000: 1 year in seconds + # includeSubDomains: apply to all subdomains + # preload: eligible for browser HSTS preload lists + hsts_value = "max-age=31536000; includeSubDomains; preload" + response.headers["Strict-Transport-Security"] = hsts_value + return response diff --git a/tests/test_hsts_header.py b/tests/test_hsts_header.py new file mode 100644 index 00000000..3a4de19c --- /dev/null +++ b/tests/test_hsts_header.py @@ -0,0 +1,43 @@ +""" +Test for HSTS header presence in responses. +""" +import pytest +from fastapi.testclient import TestClient +from api.index import app + + +class TestHSTSHeader: + """Test HSTS security header.""" + + @pytest.fixture + def client(self): + """Create a test client.""" + return TestClient(app) + + def test_hsts_header_present(self, client): + """Test that the HSTS header is present in responses.""" + # Make a request to the root endpoint + response = client.get("/") + + # Verify HSTS header is present + assert "strict-transport-security" in response.headers + + # Verify header value contains required directives + hsts_header = response.headers["strict-transport-security"] + assert "max-age=31536000" in hsts_header + assert "includeSubDomains" in hsts_header + assert "preload" in hsts_header + + def test_hsts_header_on_api_endpoints(self, client): + """Test that the HSTS header is present on API endpoints.""" + # Test on graphs endpoint + response = client.get("/api/graphs") + + # Verify HSTS header is present + assert "strict-transport-security" in response.headers + + # Verify header value contains required directives + hsts_header = response.headers["strict-transport-security"] + assert "max-age=31536000" in hsts_header + assert "includeSubDomains" in hsts_header + assert "preload" in hsts_header From 93df5c906294026512dd053900ec46a6a615ea20 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 24 Jan 2026 18:19:09 +0000 Subject: [PATCH 3/3] Fix test endpoint path from /api/graphs to /graphs Co-authored-by: gkorland <753206+gkorland@users.noreply.github.com> --- tests/test_hsts_header.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_hsts_header.py b/tests/test_hsts_header.py index 3a4de19c..51a5d0f0 100644 --- a/tests/test_hsts_header.py +++ b/tests/test_hsts_header.py @@ -31,7 +31,7 @@ def test_hsts_header_present(self, client): def test_hsts_header_on_api_endpoints(self, client): """Test that the HSTS header is present on API endpoints.""" # Test on graphs endpoint - response = client.get("/api/graphs") + response = client.get("/graphs") # Verify HSTS header is present assert "strict-transport-security" in response.headers