From b468620eadf75d1d9c62446db424c8431313c3a7 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Tue, 29 Apr 2025 11:59:06 -0700 Subject: [PATCH 1/6] appending api hosted urls to append to csp to allow for connections --- app/__init__.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 48e122bd8..f1ed64ab3 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -141,7 +141,9 @@ navigation = { def _csp(config): asset_domain = config["ASSET_DOMAIN"] logo_domain = config["LOGO_CDN_DOMAIN"] - return { + + api_host_name = os.getenv('API_HOST_NAME') + csp = { "default-src": ["'self'", asset_domain], "frame-src": [ "https://www.youtube.com", @@ -165,13 +167,21 @@ def _csp(config): "'self'", "https://gov-bam.nr-data.net", "https://www.google-analytics.com", - "http://localhost:6011", - "ws://localhost:6011", ], "style-src": ["'self'", asset_domain], "img-src": ["'self'", asset_domain, logo_domain], } + if api_host_name: + csp["connect-src"].append(api_host_name) + # this is for web socket + if api_host_name.startswith("http://"): + ws_url = api_host_name.replace("http://", "ws://") + csp["connect-src"].append(ws_url) + elif api_host_name.startswith("https://"): + ws_url = api_host_name.replace("https://", "wss://") + csp["connect-src"].append(ws_url) + return csp def create_app(application): @application.after_request From 69ae1ea0e19d860d4dca32a2672b8a3f48872e9a Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Tue, 29 Apr 2025 11:59:52 -0700 Subject: [PATCH 2/6] appending api hosted urls to append to csp to allow for connections --- app/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/__init__.py b/app/__init__.py index f1ed64ab3..4d5113f46 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -183,6 +183,7 @@ def _csp(config): csp["connect-src"].append(ws_url) return csp + def create_app(application): @application.after_request def add_csp_header(response): From e6b76486f4acb741c4def1c86d3581884627370b Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Tue, 29 Apr 2025 15:41:06 -0700 Subject: [PATCH 3/6] testing fixed --- pytest.ini | 2 +- tests/app/main/views/test_headers.py | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/pytest.ini b/pytest.ini index 5e9b75af8..2266104c6 100644 --- a/pytest.ini +++ b/pytest.ini @@ -5,7 +5,7 @@ log_level = 999 env = NOTIFY_ENVIRONMENT=test ADMIN_CLIENT_SECRET=dev-notify-secret-key - API_HOST_NAME=test + API_HOST_NAME=http://you-forgot-to-mock-an-api-call-to DANGEROUS_SALT=dev-notify-salt SECRET_KEY=dev-notify-secret-key ZENDESK_API_KEY=test diff --git a/tests/app/main/views/test_headers.py b/tests/app/main/views/test_headers.py index c84fdc094..63ddcec7a 100644 --- a/tests/app/main/views/test_headers.py +++ b/tests/app/main/views/test_headers.py @@ -1,5 +1,5 @@ from re import search - +from flask import current_app def test_owasp_useful_headers_set( client_request, @@ -26,9 +26,20 @@ def test_owasp_useful_headers_set( ) assert search(r"'nonce-[^']+';", csp) assert search( - r"connect-src 'self' https:\/\/gov-bam\.nr-data\.net https:\/\/www\.google-analytics\." - r"com http:\/\/localhost:6011 ws:\/\/localhost:6011;", + r"connect-src 'self' https:\/\/gov-bam\.nr-data\.net https:\/\/www\.google-analytics\.", csp, ) assert search(r"style-src 'self' static\.example\.com 'nonce-.*';", csp) assert search(r"img-src 'self' static\.example\.com static-logos\.test\.com", csp) + api_host_name = current_app.config.get("API_HOST_NAME") + assert api_host_name is not None, f"API_HOST_NAME: {api_host_name} — is missing" + + assert api_host_name in csp + if api_host_name.startswith("http://"): + assert api_host_name.replace("http://", "ws://") in csp + elif api_host_name.startswith("https://"): + assert api_host_name.replace("https://", "wss://") in csp + else: + raise AssertionError( + f"Unexpected API_HOST_NAME format: {api_host_name} — must start with 'http://' or 'https://'" + ) From c825878c45e3db513834670cab925efb8c2f83f7 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Tue, 29 Apr 2025 15:55:14 -0700 Subject: [PATCH 4/6] isort --- tests/app/main/views/test_headers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/app/main/views/test_headers.py b/tests/app/main/views/test_headers.py index 63ddcec7a..ae5b3f448 100644 --- a/tests/app/main/views/test_headers.py +++ b/tests/app/main/views/test_headers.py @@ -1,6 +1,8 @@ from re import search + from flask import current_app + def test_owasp_useful_headers_set( client_request, mocker, From 0917ddaa23fbfe9bb52c66dc5311abfc2a747c92 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 30 Apr 2025 10:03:17 -0700 Subject: [PATCH 5/6] replaced os.env with config --- app/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/__init__.py b/app/__init__.py index 4d5113f46..8b219b917 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -142,7 +142,7 @@ def _csp(config): asset_domain = config["ASSET_DOMAIN"] logo_domain = config["LOGO_CDN_DOMAIN"] - api_host_name = os.getenv('API_HOST_NAME') + api_host_name = config["API_HOST_NAME"] csp = { "default-src": ["'self'", asset_domain], "frame-src": [ From ccdfd0c10e9cac09359ca12b3a85bd4a442438f6 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 30 Apr 2025 10:04:30 -0700 Subject: [PATCH 6/6] formating changes --- app/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/__init__.py b/app/__init__.py index 8b219b917..441ff5dd4 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -141,8 +141,8 @@ navigation = { def _csp(config): asset_domain = config["ASSET_DOMAIN"] logo_domain = config["LOGO_CDN_DOMAIN"] - api_host_name = config["API_HOST_NAME"] + csp = { "default-src": ["'self'", asset_domain], "frame-src": [