Fix production site issues with a more permissive CSP policy
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
colin 2025-07-03 18:15:49 -04:00
parent 767df7dc44
commit 7845079307
1 changed files with 22 additions and 28 deletions

View File

@ -54,19 +54,20 @@ if CSP_CSS_HASH:
if CSP_CUSTOM_CSS_HASH:
logger.info(f"Using pre-calculated CSP hash for custom CSS: sha256-{CSP_CUSTOM_CSS_HASH}")
# Define the base CSP for UI routes
ui_csp = {
'default-src': "'none'", # Deny by default
'script-src': ["'self'"] + ([f"'sha256-{CSP_JS_HASH}'"] if CSP_JS_HASH else []),
'style-src': ["'self'"] +
# Define a more permissive CSP that works for both UI and API routes
# This is less secure but ensures compatibility with all clients
csp = {
'default-src': ["'self'", "'unsafe-inline'", "'unsafe-eval'", "data:", "blob:"],
'script-src': ["'self'", "'unsafe-inline'", "'unsafe-eval'"] + ([f"'sha256-{CSP_JS_HASH}'"] if CSP_JS_HASH else []),
'style-src': ["'self'", "'unsafe-inline'"] +
([f"'sha256-{CSP_CSS_HASH}'"] if CSP_CSS_HASH else []) +
([f"'sha256-{CSP_CUSTOM_CSS_HASH}'"] if CSP_CUSTOM_CSS_HASH else []),
'img-src': ["'self'", "data:"],
'font-src': ["'self'"],
'connect-src': "'self'",
'img-src': ["'self'", "data:", "blob:"],
'font-src': ["'self'", "data:"],
'connect-src': ["'self'", "*"],
'manifest-src': "'self'",
'object-src': "'none'", # Explicitly disallow objects
'frame-ancestors': "'none'", # Prevent framing
'object-src': "'none'", # Still explicitly disallow objects
'frame-ancestors': "'none'", # Still prevent framing
'base-uri': "'self'",
'form-action': "'self'"
}
@ -75,18 +76,16 @@ ui_csp = {
if APP_DOMAIN:
logger.info(f"Configuring CSP for domain: {APP_DOMAIN}")
# Add domain to connect-src if needed
if APP_DOMAIN not in ui_csp['connect-src']:
if isinstance(ui_csp['connect-src'], list):
ui_csp['connect-src'].append(APP_DOMAIN)
else:
ui_csp['connect-src'] = [ui_csp['connect-src'], APP_DOMAIN]
if APP_DOMAIN not in csp['connect-src']:
if isinstance(csp['connect-src'], list):
csp['connect-src'].append(APP_DOMAIN)
# Update form-action to include the domain
if isinstance(ui_csp['form-action'], list):
if APP_DOMAIN not in ui_csp['form-action']:
ui_csp['form-action'].append(APP_DOMAIN)
if isinstance(csp['form-action'], list):
if APP_DOMAIN not in csp['form-action']:
csp['form-action'].append(APP_DOMAIN)
else:
ui_csp['form-action'] = [ui_csp['form-action'], APP_DOMAIN]
csp['form-action'] = [csp['form-action'], APP_DOMAIN]
# Configure Permissions-Policy (formerly Feature-Policy)
# Deny access to all browser features that we don't need
@ -132,11 +131,10 @@ additional_headers = {
'Cross-Origin-Opener-Policy': 'same-origin'
}
# Initialize Talisman with the static CSP configuration
# We'll handle API routes separately in the after_request handler
# Initialize Talisman with the permissive CSP configuration
talisman = Talisman(
app,
content_security_policy=ui_csp,
content_security_policy=csp,
content_security_policy_nonce_in=['script-src'],
feature_policy=permissions_policy,
force_https=force_https,
@ -151,7 +149,7 @@ talisman = Talisman(
session_cookie_http_only=True
)
# Add CORS headers for API routes and handle CSP
# Add CORS headers for API routes
@app.after_request
def add_api_headers(response):
if request.path.startswith('/api/'):
@ -160,10 +158,6 @@ def add_api_headers(response):
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
response.headers['Cross-Origin-Resource-Policy'] = 'cross-origin'
# Remove CSP for API routes to ensure compatibility with clients
if 'Content-Security-Policy' in response.headers:
del response.headers['Content-Security-Policy']
else:
# For UI routes, add additional security headers
for header, value in additional_headers.items():