Merge pull request #2598 from GSA/update_csp

updating csp and test
This commit is contained in:
ccostino
2025-05-22 15:20:56 -04:00
committed by GitHub
12 changed files with 48 additions and 34 deletions
+2 -2
View File
@@ -171,7 +171,7 @@
"filename": "app/config.py", "filename": "app/config.py",
"hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc", "hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc",
"is_verified": false, "is_verified": false,
"line_number": 122, "line_number": 123,
"is_secret": false "is_secret": false
} }
], ],
@@ -644,5 +644,5 @@
} }
] ]
}, },
"generated_at": "2025-05-19T21:41:23Z" "generated_at": "2025-05-22T19:03:11Z"
} }
+2
View File
@@ -64,6 +64,7 @@ jobs:
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-demo.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=STATE" LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-demo.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=STATE"
LOGIN_DOT_GOV_CERTS_URL: "https://secure.login.gov/api/openid_connect/certs" LOGIN_DOT_GOV_CERTS_URL: "https://secure.login.gov/api/openid_connect/certs"
API_PUBLIC_URL: ${{ secrets.API_PUBLIC_URL }} API_PUBLIC_URL: ${{ secrets.API_PUBLIC_URL }}
API_PUBLIC_WS_URL: ${{ secrets.API_PUBLIC_WS_URL }}
with: with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }} cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }} cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
@@ -89,6 +90,7 @@ jobs:
--var LOGIN_DOT_GOV_CERTS_URL="$LOGIN_DOT_GOV_CERTS_URL" --var LOGIN_DOT_GOV_CERTS_URL="$LOGIN_DOT_GOV_CERTS_URL"
--var LOGIN_PEM="$LOGIN_PEM" --var LOGIN_PEM="$LOGIN_PEM"
--var API_PUBLIC_URL="$API_PUBLIC_URL" --var API_PUBLIC_URL="$API_PUBLIC_URL"
--var API_PUBLIC_WS_URL="$API_PUBLIC_WS_URL"
--strategy rolling --strategy rolling
- name: Deploy egress proxy - name: Deploy egress proxy
+2
View File
@@ -64,6 +64,7 @@ jobs:
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://beta.notify.gov/sign-in&response_type=code&scope=openid+email&state=STATE" LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://beta.notify.gov/sign-in&response_type=code&scope=openid+email&state=STATE"
LOGIN_DOT_GOV_CERTS_URL: "https://secure.login.gov/api/openid_connect/certs" LOGIN_DOT_GOV_CERTS_URL: "https://secure.login.gov/api/openid_connect/certs"
API_PUBLIC_URL: ${{ secrets.API_PUBLIC_URL }} API_PUBLIC_URL: ${{ secrets.API_PUBLIC_URL }}
API_PUBLIC_WS_URL: ${{ secrets.API_PUBLIC_WS_URL }}
with: with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }} cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }} cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
@@ -89,6 +90,7 @@ jobs:
--var LOGIN_DOT_GOV_CERTS_URL="$LOGIN_DOT_GOV_CERTS_URL" --var LOGIN_DOT_GOV_CERTS_URL="$LOGIN_DOT_GOV_CERTS_URL"
--var LOGIN_PEM="$LOGIN_PEM" --var LOGIN_PEM="$LOGIN_PEM"
--var API_PUBLIC_URL="$API_PUBLIC_URL" --var API_PUBLIC_URL="$API_PUBLIC_URL"
--var API_PUBLIC_WS_URL="$API_PUBLIC_WS_URL"
--strategy rolling --strategy rolling
- name: Deploy egress proxy - name: Deploy egress proxy
+2
View File
@@ -70,6 +70,7 @@ jobs:
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=STATE" LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=STATE"
LOGIN_DOT_GOV_CERTS_URL: "https://secure.login.gov/api/openid_connect/certs" LOGIN_DOT_GOV_CERTS_URL: "https://secure.login.gov/api/openid_connect/certs"
API_PUBLIC_URL: ${{ secrets.API_PUBLIC_URL }} API_PUBLIC_URL: ${{ secrets.API_PUBLIC_URL }}
API_PUBLIC_WS_URL: ${{ secrets.API_PUBLIC_WS_URL }}
with: with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }} cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }} cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
@@ -95,6 +96,7 @@ jobs:
--var LOGIN_DOT_GOV_CERTS_URL="$LOGIN_DOT_GOV_CERTS_URL" --var LOGIN_DOT_GOV_CERTS_URL="$LOGIN_DOT_GOV_CERTS_URL"
--var LOGIN_PEM="$LOGIN_PEM" --var LOGIN_PEM="$LOGIN_PEM"
--var API_PUBLIC_URL="$API_PUBLIC_URL" --var API_PUBLIC_URL="$API_PUBLIC_URL"
--var API_PUBLIC_WS_URL="$API_PUBLIC_WS_URL"
--strategy rolling --strategy rolling
+4 -12
View File
@@ -142,8 +142,9 @@ def _csp(config):
asset_domain = config["ASSET_DOMAIN"] asset_domain = config["ASSET_DOMAIN"]
logo_domain = config["LOGO_CDN_DOMAIN"] logo_domain = config["LOGO_CDN_DOMAIN"]
api_public_url = config["API_PUBLIC_URL"] api_public_url = config["API_PUBLIC_URL"]
api_public_ws_url = config["API_PUBLIC_WS_URL"]
csp = { return {
"default-src": ["'self'", asset_domain], "default-src": ["'self'", asset_domain],
"frame-src": [ "frame-src": [
"https://www.youtube.com", "https://www.youtube.com",
@@ -167,22 +168,13 @@ def _csp(config):
"'self'", "'self'",
"https://gov-bam.nr-data.net", "https://gov-bam.nr-data.net",
"https://www.google-analytics.com", "https://www.google-analytics.com",
f"{api_public_url}",
f"{api_public_ws_url}",
], ],
"style-src": ["'self'", asset_domain], "style-src": ["'self'", asset_domain],
"img-src": ["'self'", asset_domain, logo_domain], "img-src": ["'self'", asset_domain, logo_domain],
} }
if api_public_url:
csp["connect-src"].append(api_public_url)
# this is for web socket
if api_public_url.startswith("http://"):
ws_url = api_public_url.replace("http://", "ws://")
csp["connect-src"].append(ws_url)
elif api_public_url.startswith("https://"):
ws_url = api_public_url.replace("https://", "wss://")
csp["connect-src"].append(ws_url)
return csp
def create_app(application): def create_app(application):
@application.after_request @application.after_request
+8
View File
@@ -20,6 +20,14 @@ document.addEventListener('DOMContentLoaded', function () {
if (featureEnabled) { if (featureEnabled) {
const socket = io(apiHost); const socket = io(apiHost);
socket.on('connect_error', (err) => {
console.error('Socket connect_error:', err);
});
socket.on('error', (err) => {
console.error('Socket error:', err);
});
socket.on('connect', () => { socket.on('connect', () => {
socket.emit('join', { room: `job-${jobId}` }); socket.emit('join', { room: `job-${jobId}` });
}); });
+2 -2
View File
@@ -13,6 +13,7 @@ class Config(object):
NOTIFY_ENVIRONMENT = getenv("NOTIFY_ENVIRONMENT", "development") NOTIFY_ENVIRONMENT = getenv("NOTIFY_ENVIRONMENT", "development")
API_HOST_NAME = getenv("API_HOST_NAME", "localhost") API_HOST_NAME = getenv("API_HOST_NAME", "localhost")
API_PUBLIC_URL = getenv("API_PUBLIC_URL", "localhost") API_PUBLIC_URL = getenv("API_PUBLIC_URL", "localhost")
API_PUBLIC_WS_URL = getenv("API_PUBLIC_WS_URL", "localhost")
ADMIN_BASE_URL = getenv("ADMIN_BASE_URL", "http://localhost:6012") ADMIN_BASE_URL = getenv("ADMIN_BASE_URL", "http://localhost:6012")
HEADER_COLOUR = ( HEADER_COLOUR = (
@@ -132,10 +133,9 @@ class Test(Development):
WTF_CSRF_ENABLED = False WTF_CSRF_ENABLED = False
ASSET_DOMAIN = "static.example.com" ASSET_DOMAIN = "static.example.com"
ASSET_PATH = "https://static.example.com/" ASSET_PATH = "https://static.example.com/"
API_HOST_NAME = "http://you-forgot-to-mock-an-api-call-to" API_HOST_NAME = "http://you-forgot-to-mock-an-api-call-to"
API_PUBLIC_URL = "http://you-forgot-to-mock-an-api-call-to" API_PUBLIC_URL = "http://you-forgot-to-mock-an-api-call-to"
API_PUBLIC_WS_URL = "ws://you-forgot-to-mock-an-api-call-to"
REDIS_URL = "redis://you-forgot-to-mock-a-redis-call-to" REDIS_URL = "redis://you-forgot-to-mock-a-redis-call-to"
LOGO_CDN_DOMAIN = "static-logos.test.com" LOGO_CDN_DOMAIN = "static-logos.test.com"
+1
View File
@@ -9,3 +9,4 @@ nr_agent_id: '1134302465'
nr_app_id: '1083160688' nr_app_id: '1083160688'
FEATURE_SOCKET_ENABLED: true FEATURE_SOCKET_ENABLED: true
API_PUBLIC_URL: https://notify-api-demo.app.cloud.gov API_PUBLIC_URL: https://notify-api-demo.app.cloud.gov
API_PUBLIC_WS_URL: wss://notify-api-demo.app.cloud.gov
+1
View File
@@ -9,3 +9,4 @@ nr_agent_id: '1050708682'
nr_app_id: '1050708682' nr_app_id: '1050708682'
FEATURE_SOCKET_ENABLED: false FEATURE_SOCKET_ENABLED: false
API_PUBLIC_URL: https://notify-api-production.app.cloud.gov API_PUBLIC_URL: https://notify-api-production.app.cloud.gov
API_PUBLIC_WS_URL: wss://notify-api-production.app.cloud.gov
+1
View File
@@ -9,3 +9,4 @@ nr_agent_id: '1134291385'
nr_app_id: '1031640326' nr_app_id: '1031640326'
FEATURE_SOCKET_ENABLED: false FEATURE_SOCKET_ENABLED: false
API_PUBLIC_URL: https://notify-api-staging.app.cloud.gov API_PUBLIC_URL: https://notify-api-staging.app.cloud.gov
API_PUBLIC_WS_URL: wss://notify-api-staging.app.cloud.gov
+1 -1
View File
@@ -62,6 +62,6 @@ applications:
LOGIN_DOT_GOV_CERTS_URL: ((LOGIN_DOT_GOV_CERTS_URL)) LOGIN_DOT_GOV_CERTS_URL: ((LOGIN_DOT_GOV_CERTS_URL))
API_PUBLIC_URL: ((API_PUBLIC_URL)) API_PUBLIC_URL: ((API_PUBLIC_URL))
API_PUBLIC_WS_URL: ((API_PUBLIC_WS_URL))
# feature flagging # feature flagging
FEATURE_SOCKET_ENABLED: ((FEATURE_SOCKET_ENABLED)) FEATURE_SOCKET_ENABLED: ((FEATURE_SOCKET_ENABLED))
+22 -17
View File
@@ -1,7 +1,5 @@
from re import search from re import search
from flask import current_app
def test_owasp_useful_headers_set( def test_owasp_useful_headers_set(
client_request, client_request,
@@ -27,21 +25,28 @@ def test_owasp_useful_headers_set(
csp, csp,
) )
assert search(r"'nonce-[^']+';", csp) assert search(r"'nonce-[^']+';", csp)
assert search( connect_src = next(
r"connect-src 'self' https:\/\/gov-bam\.nr-data\.net https:\/\/www\.google-analytics\.", (
csp, directive
for directive in csp.split(";")
if directive.strip().startswith("connect-src")
),
None,
) )
assert connect_src is not None, "connect-src directive is missing"
from flask import current_app
config = current_app.config
expected_sources = {
"'self'",
"https://gov-bam.nr-data.net",
"https://www.google-analytics.com",
config["API_PUBLIC_URL"],
config["API_PUBLIC_WS_URL"],
}
actual_sources = set(connect_src.strip().split()[1:])
assert (
expected_sources <= actual_sources
), f"Missing sources in connect-src: {expected_sources - actual_sources}"
assert search(r"style-src 'self' static\.example\.com 'nonce-.*';", 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) assert search(r"img-src 'self' static\.example\.com static-logos\.test\.com", csp)
api_public_url = current_app.config.get("API_PUBLIC_URL")
assert api_public_url is not None, f"API_PUBLIC_URL: {api_public_url} — is missing"
assert api_public_url in csp
if api_public_url.startswith("http://"):
assert api_public_url.replace("http://", "ws://") in csp
elif api_public_url.startswith("https://"):
assert api_public_url.replace("https://", "wss://") in csp
else:
raise AssertionError(
f"Unexpected API_PUBLIC_URL format: {api_public_url} — must start with 'http://' or 'https://'"
)