From 3036eb1ece9b220a3a407779f6e1f3e37ca8a80c Mon Sep 17 00:00:00 2001 From: Andrew Shu Date: Sun, 24 Aug 2025 12:10:08 -0700 Subject: [PATCH] Fix CSP errors on inline elements in Swagger UI * Move inline CSS to a separate file * Inject a CSP nonce using Pyramid tween --- ansible/roles/swagger_ui/files/index.css | 17 +++++++++++ .../roles/swagger_ui/files}/index.html | 20 +------------ ansible/roles/swagger_ui/tasks/main.yml | 15 ++++++++++ tildes/tildes/tweens.py | 28 +++++++++++++++++++ 4 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 ansible/roles/swagger_ui/files/index.css rename {tildes/static/swagger-ui-tildes => ansible/roles/swagger_ui/files}/index.html (77%) diff --git a/ansible/roles/swagger_ui/files/index.css b/ansible/roles/swagger_ui/files/index.css new file mode 100644 index 0000000..d99b570 --- /dev/null +++ b/ansible/roles/swagger_ui/files/index.css @@ -0,0 +1,17 @@ +html +{ + box-sizing: border-box; + overflow: -moz-scrollbars-vertical; + overflow-y: scroll; +} +*, +*:before, +*:after +{ + box-sizing: inherit; +} + +body { + margin:0; + background: #fafafa; +} \ No newline at end of file diff --git a/tildes/static/swagger-ui-tildes/index.html b/ansible/roles/swagger_ui/files/index.html similarity index 77% rename from tildes/static/swagger-ui-tildes/index.html rename to ansible/roles/swagger_ui/files/index.html index fe2954f..1cd1014 100644 --- a/tildes/static/swagger-ui-tildes/index.html +++ b/ansible/roles/swagger_ui/files/index.html @@ -4,25 +4,7 @@ Swagger UI - + diff --git a/ansible/roles/swagger_ui/tasks/main.yml b/ansible/roles/swagger_ui/tasks/main.yml index ccc25be..f1b03b6 100644 --- a/ansible/roles/swagger_ui/tasks/main.yml +++ b/ansible/roles/swagger_ui/tasks/main.yml @@ -7,3 +7,18 @@ group: "{{ app_username }}" mode: 0755 remote_src: true + +- name: Copy our own custom swagger-ui web assets + block: + - copy: + src: "index.html" + dest: "{{ app_dir }}/static/swagger-ui/index.html" + owner: "{{ app_username }}" + group: "{{ app_username }}" + mode: 0644 + - copy: + src: "index.css" + dest: "{{ app_dir }}/static/swagger-ui/index.css" + owner: "{{ app_username }}" + group: "{{ app_username }}" + mode: 0644 diff --git a/tildes/tildes/tweens.py b/tildes/tildes/tweens.py index 9031c1f..a4c3879 100644 --- a/tildes/tildes/tweens.py +++ b/tildes/tildes/tweens.py @@ -3,6 +3,7 @@ """Contains Pyramid "tweens", used to insert additional logic into request-handling.""" +import secrets from collections.abc import Callable from time import time @@ -106,8 +107,35 @@ def theme_cookie_tween_factory(handler: Callable, registry: Registry) -> Callabl return theme_cookie_tween +def inject_csp_header_tween_factory(handler: Callable, registry: Registry) -> Callable: + # pylint: disable=unused-argument + """Return a tween function that sets a CSP nonce (for Swagger UI).""" + + def inject_csp_header_tween(request: Request) -> Response: + """Generate a CSP nonce and add it to the request and response. + + Only apply to specific routes defined here, to minimize performance overhead. + """ + nonce = None + route_name = request.matched_route.name if request.matched_route else None + if route_name == "pyramid_openapi3.explorer": + nonce = secrets.token_urlsafe(16) + request.csp_nonce = nonce + + response = handler(request) + + if nonce: + response.headers["Content-Security-Policy"] = ( + f"script-src 'self' 'nonce-{nonce}'" + ) + return response + + return inject_csp_header_tween + + def includeme(config: Configurator) -> None: """Attach Tildes tweens to the Pyramid config.""" config.add_tween("tildes.tweens.http_method_tween_factory") config.add_tween("tildes.tweens.metrics_tween_factory") config.add_tween("tildes.tweens.theme_cookie_tween_factory") + config.add_tween("tildes.tweens.inject_csp_header_tween_factory")