"""Production nginx security contract for Issues #2264 and #2265.""" from pathlib import Path ROOT = Path(__file__).resolve().parents[3] NGINX = (ROOT / "web" / "nginx.conf").read_text() def test_spa_document_and_asset_locations_emit_security_headers(): required = ( "Content-Security-Policy", "frame-ancestors 'none'", "Strict-Transport-Security", "X-Content-Type-Options", "X-Frame-Options", "Referrer-Policy", "Permissions-Policy", ) for header in required: # server default + explicit repeats in asset and index locations, # whose Cache-Control add_header would otherwise suppress inheritance. assert NGINX.count(header) >= 3, header def test_csp_keeps_google_identity_login_operational(): assert NGINX.count("script-src 'self' https://accounts.google.com/gsi/client") >= 3 assert NGINX.count("frame-src https://accounts.google.com/gsi/") >= 3 def test_global_api_no_longer_inherits_upload_sized_body_limit(): generic_api = NGINX.split("location /api/ {", 1)[1].split( "# Hashed assets:", 1 )[0] assert "client_max_body_size 2m;" in generic_api assert "client_max_body_size 60m;" not in generic_api def test_upload_headroom_is_restricted_to_enumerated_routes(): assert "approval/asset/uploads" in NGINX assert "assets/(?:intake/bulk|bulk-update)" in NGINX assert "finance-flux/import" in NGINX assert "client_max_body_size 61m;" in NGINX def test_upload_routes_bound_idle_client_body_time(): upload = NGINX.split( "location ~ ^/api/v1/(?:approval/asset/uploads", 1 )[1].split("}", 1)[0] assert "client_body_timeout 60s;" in upload def test_oauth_has_a_smaller_proxy_limit_than_generic_api(): oauth = NGINX.split("location /api/v1/auth/oauth/ {", 1)[1].split("}", 1)[0] assert "client_max_body_size 320k;" in oauth assert "error_page 413 = @oauth_payload_too_large;" in oauth assert '"error":"invalid_request"' in NGINX def test_nginx_level_api_413_is_also_a_helix_json_envelope(): assert "error_page 413 = @api_payload_too_large;" in NGINX assert '"code":"PAYLOAD_TOO_LARGE"' in NGINX