fastapi-csp-docs 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/PKG-INFO +28 -76
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/README.md +28 -76
- fastapi_csp_docs-0.1.1/RELEASE_NOTES.md +24 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/fastapi_csp_docs/__init__.py +1 -1
- fastapi_csp_docs-0.1.0/RELEASE_NOTES.md +0 -32
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/dependabot.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/labeler.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/create-draft-release.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/detect-conflicts.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/guard-dependencies.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/labeler.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/latest-changes.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/pre-commit.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/prepare-release.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/publish.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/test-redistribute.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/test.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/zizmor.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.gitignore +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.pre-commit-config.yaml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.python-version +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/LICENSE +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/__init__.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/_csp.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/assisted_app.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/mounted_subapp_app.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/oauth2_redirect_app.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/redoc_disable_search_app.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/root_path_app.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/self_hosted_app.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/favicon.png +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-cyrillic-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-cyrillic-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-cyrillic-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-cyrillic-ext-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-cyrillic-ext-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-cyrillic-ext-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-latin-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-latin-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-latin-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-latin-ext-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-latin-ext-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-latin-ext-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-vietnamese-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-vietnamese-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-vietnamese-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-cyrillic-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-cyrillic-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-cyrillic-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-cyrillic-ext-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-cyrillic-ext-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-cyrillic-ext-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-greek-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-greek-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-greek-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-greek-ext-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-greek-ext-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-greek-ext-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-latin-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-latin-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-latin-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-latin-ext-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-latin-ext-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-latin-ext-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-math-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-math-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-math-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-symbols-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-symbols-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-symbols-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-vietnamese-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-vietnamese-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-vietnamese-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts.css +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/redoc.standalone.js +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/swagger-ui-bundle.js +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/swagger-ui.css +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/fastapi_csp_docs/docs.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/fastapi_csp_docs/py.typed +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/fastapi_csp_docs/setup.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/pyproject.toml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/scripts/format.sh +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/scripts/lint.sh +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/scripts/prepare_release.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/scripts/test-cov-html.sh +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/scripts/test-cov.sh +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/scripts/test.sh +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/tests/__init__.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/tests/test_docs.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/tests/test_prepare_release.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/tests/test_setup.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-csp-docs
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: CSP-safe replacements for FastAPI's built-in /docs, /redoc, and OAuth2 redirect endpoints, with no inline script or style
|
|
5
5
|
Project-URL: Homepage, https://github.com/mat81black/fastapi-csp-docs
|
|
6
6
|
Project-URL: Repository, https://github.com/mat81black/fastapi-csp-docs
|
|
@@ -31,35 +31,21 @@ Description-Content-Type: text/markdown
|
|
|
31
31
|
|
|
32
32
|
# FastAPI CSP Docs
|
|
33
33
|
|
|
34
|
-
[](https://pypi.org/project/fastapi-csp-docs/)
|
|
37
|
-
[](https://github.com/mat81black/fastapi-csp-docs/actions)
|
|
35
|
+
[](https://pypi.org/project/fastapi-csp-docs/)
|
|
36
|
+
[](https://pypi.org/project/fastapi-csp-docs/)
|
|
37
|
+
[](LICENSE)
|
|
38
38
|
|
|
39
|
-
FastAPI's built-in `/docs`, `/redoc`, and OAuth2 redirect pages embed inline `<script>`/`<style>`
|
|
40
|
-
tags, so they break under a Content-Security-Policy without `'unsafe-inline'`. Following FastAPI's
|
|
41
|
-
own ["Custom Docs UI Static Assets"](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)
|
|
42
|
-
recipe only swaps the CDN URLs for local ones — it doesn't remove any inline content:
|
|
43
|
-
`get_swagger_ui_html()` still embeds the Swagger UI bootstrap script inline, `get_redoc_html()`
|
|
44
|
-
still embeds a `<style>` reset inline, and `get_swagger_ui_oauth2_redirect_html()` still embeds
|
|
45
|
-
the OAuth2 redirect logic inline. `fastapi-csp-docs` replaces all three pages with versions that
|
|
46
|
-
load every script and stylesheet from a separate endpoint, with no inline content anywhere.
|
|
39
|
+
FastAPI's built-in `/docs`, `/redoc`, and OAuth2 redirect pages embed inline `<script>`/`<style>` tags, so they break under a Content-Security-Policy without `'unsafe-inline'`. Following FastAPI's own ["Custom Docs UI Static Assets"](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/) recipe only swaps the CDN URLs for local ones; it doesn't remove any inline content: `get_swagger_ui_html()` still embeds the Swagger UI bootstrap script inline, `get_redoc_html()` still embeds a `<style>` reset inline, and `get_swagger_ui_oauth2_redirect_html()` still embeds the OAuth2 redirect logic inline. `fastapi-csp-docs` replaces all three pages with versions that load every script and stylesheet from a separate endpoint, with no inline content anywhere.
|
|
47
40
|
|
|
48
41
|
## Features
|
|
49
42
|
|
|
50
|
-
- **Drop-in `setup()`**: one call replaces FastAPI's built-in `/docs`, `/redoc`, and OAuth2
|
|
51
|
-
|
|
52
|
-
- **
|
|
53
|
-
|
|
54
|
-
- **
|
|
55
|
-
|
|
56
|
-
(`script-src 'self'`, zero external origins).
|
|
57
|
-
- **Reverse-proxy / sub-app aware**: doc URLs include the ASGI `root_path` computed per request,
|
|
58
|
-
so mounting under a prefix (`app.mount("/api", sub_app)`) works out of the box.
|
|
59
|
-
- **Independently configurable mount paths**: `docs_url`/`redoc_url` are explicit `setup()`
|
|
60
|
-
parameters, each can be disabled on its own by passing `None`.
|
|
61
|
-
- **Content generators exported for manual wiring**: the same building blocks `setup()` uses
|
|
62
|
-
internally are public, for a fully self-hosted setup with no CDN at all.
|
|
43
|
+
- **Drop-in `setup()`**: one call replaces FastAPI's built-in `/docs`, `/redoc`, and OAuth2 redirect wiring, with no inline script or style left anywhere.
|
|
44
|
+
- **Fails fast on misconfiguration**: raises `RuntimeError` if the app's built-in docs aren't disabled first, instead of silently letting them shadow the CSP-safe routes.
|
|
45
|
+
- **CDN or fully self-hosted**: works with the default jsdelivr CDN (`script-src 'self' <cdn-host>`) or entirely offline with your own static assets (`script-src 'self'`, zero external origins).
|
|
46
|
+
- **Reverse-proxy / sub-app aware**: doc URLs include the ASGI `root_path` computed per request, so mounting under a prefix (`app.mount("/api", sub_app)`) works out of the box.
|
|
47
|
+
- **Independently configurable mount paths**: `docs_url`/`redoc_url` are explicit `setup()` parameters, each can be disabled on its own by passing `None`.
|
|
48
|
+
- **Content generators exported for manual wiring**: the same building blocks `setup()` uses internally are public, for a fully self-hosted setup with no CDN at all.
|
|
63
49
|
|
|
64
50
|
## Requirements
|
|
65
51
|
|
|
@@ -101,20 +87,13 @@ async def add_csp_header(request: Request, call_next) -> Response:
|
|
|
101
87
|
fastapi_csp_docs.setup(app)
|
|
102
88
|
```
|
|
103
89
|
|
|
104
|
-
`docs_url`/`redoc_url` must already be `None` on the app for whichever mode you're enabling
|
|
105
|
-
`setup()` raises `RuntimeError` otherwise, so FastAPI's built-in inline-script docs can't
|
|
106
|
-
silently shadow the routes it registers.
|
|
90
|
+
`docs_url`/`redoc_url` must already be `None` on the app for whichever mode you're enabling. `setup()` raises `RuntimeError` otherwise, so FastAPI's built-in inline-script docs can't silently shadow the routes it registers.
|
|
107
91
|
|
|
108
|
-
Full runnable version:
|
|
109
|
-
[`examples/assisted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/assisted_app.py).
|
|
92
|
+
Full runnable version: [`examples/assisted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/assisted_app.py).
|
|
110
93
|
|
|
111
94
|
### Fully self-hosted mode (no CDN at all)
|
|
112
95
|
|
|
113
|
-
For a CSP with zero external origins (`script-src 'self'`), skip `setup()` and wire the
|
|
114
|
-
endpoints directly — same mechanics as FastAPI's own
|
|
115
|
-
["Custom Docs UI Static Assets"](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)
|
|
116
|
-
recipe, but built from this package's content generators instead of `fastapi.openapi.docs`, so
|
|
117
|
-
the OAuth2 redirect page stays script-free too:
|
|
96
|
+
For a CSP with zero external origins (`script-src 'self'`), skip `setup()` and wire the endpoints directly, using the same mechanics as FastAPI's own ["Custom Docs UI Static Assets"](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/) recipe, but built from this package's content generators instead of `fastapi.openapi.docs`, so the OAuth2 redirect page stays script-free too:
|
|
118
97
|
|
|
119
98
|
```python
|
|
120
99
|
from fastapi import FastAPI
|
|
@@ -136,46 +115,19 @@ async def swagger_ui_html():
|
|
|
136
115
|
)
|
|
137
116
|
```
|
|
138
117
|
|
|
139
|
-
Full runnable version, including the `swagger-initializer.js`/`redoc.css`/OAuth2 redirect
|
|
140
|
-
endpoints and the vendor assets:
|
|
141
|
-
[`examples/self_hosted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/self_hosted_app.py).
|
|
118
|
+
Full runnable version, including the `swagger-initializer.js`/`redoc.css`/OAuth2 redirect endpoints and the vendor assets: [`examples/self_hosted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/self_hosted_app.py).
|
|
142
119
|
|
|
143
120
|
## ReDoc and Content-Security-Policy
|
|
144
121
|
|
|
145
|
-
Beyond the inline `<script>`/`<style>` tags this package removes from FastAPI's own generated
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
without `'unsafe-inline'`: open DevTools, note the exact `'sha256-...'` value(s) the browser
|
|
155
|
-
reports as blocked, and add them to `style-src`. These hashes are tied to the exact ReDoc
|
|
156
|
-
build, so they need to be recaptured whenever `redoc.standalone.js` is upgraded.
|
|
157
|
-
|
|
158
|
-
**Web Worker (`worker-src`)** — ReDoc runs its search indexing in a Web Worker, instantiated
|
|
159
|
-
from a `blob:` URL rather than a separate `.js` file: the worker's entire source is inlined as
|
|
160
|
-
a string inside `redoc.standalone.js`, so there's no real file URL to point `worker-src` at
|
|
161
|
-
instead. Worker instantiation is governed by `worker-src`, not `script-src` — without it set
|
|
162
|
-
explicitly, browsers fall back to `script-src`, which never matches a `blob:` URL, so search
|
|
163
|
-
breaks the first time it's used (`Creating a worker from 'blob:...' violates ... worker-src`).
|
|
164
|
-
Two ways to fix it:
|
|
165
|
-
- Allow it explicitly: `worker-src 'self' blob:` — what `assisted_app.py` and the other
|
|
166
|
-
`setup()`-based examples do, since `setup()` deliberately has no CSP-specific parameters (it
|
|
167
|
-
mirrors FastAPI's own `docs_url`/`redoc_url` surface, nothing more).
|
|
168
|
-
- Or skip the worker (and the search box) entirely by not using `setup()` for the ReDoc route:
|
|
169
|
-
pass `redoc_url=None` to `setup()` and wire `/redoc` yourself with
|
|
170
|
-
`get_redoc_html(disable_search=True)` instead — no `blob:` needed in `worker-src` then. See
|
|
171
|
-
[`examples/redoc_disable_search_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/redoc_disable_search_app.py).
|
|
172
|
-
|
|
173
|
-
**Logo image (`img-src`)** — ReDoc's "API docs by Redocly" attribution logo hardcodes an
|
|
174
|
-
absolute `cdn.redoc.ly` URL, with no supported way to override it or point it at a local file.
|
|
175
|
-
There's no CSP hash mechanism for images (hash-source only applies to `script-src`/`style-src`), so the only way to allow it is
|
|
176
|
-
whitelisting that origin in `img-src` — both examples do this, since it's a small, non-code
|
|
177
|
-
image request and doesn't weaken `script-src`/`style-src` at all. It's the one external origin
|
|
178
|
-
left in `self_hosted_app.py`'s otherwise fully self-hosted CSP.
|
|
122
|
+
Beyond the inline `<script>`/`<style>` tags this package removes from FastAPI's own generated HTML, ReDoc's *own* runtime does three more things that need explicit CSP entries. Both examples above already handle these (`examples/_csp.py` for the CDN-based ones, `examples/self_hosted_app.py` for the fully self-hosted one); this section explains why.
|
|
123
|
+
|
|
124
|
+
**Inline styles (`style-src`)**: ReDoc is built with `styled-components`, which injects `<style>` tags into the page at runtime instead of shipping one static stylesheet. This is unrelated to the inline content `fastapi-csp-docs` removes: it's ReDoc's own rendering mechanism, and this package can't eliminate it. A CSP hash allowlist is used to allow them without `'unsafe-inline'`: open DevTools, note the exact `'sha256-...'` value(s) the browser reports as blocked, and add them to `style-src`. These hashes are tied to the exact ReDoc build, so they need to be recaptured whenever `redoc.standalone.js` is upgraded.
|
|
125
|
+
|
|
126
|
+
**Web Worker (`worker-src`)**: ReDoc runs its search indexing in a Web Worker, instantiated from a `blob:` URL rather than a separate `.js` file: the worker's entire source is inlined as a string inside `redoc.standalone.js`, so there's no real file URL to point `worker-src` at instead. Worker instantiation is governed by `worker-src`, not `script-src`; without it set explicitly, browsers fall back to `script-src`, which never matches a `blob:` URL, so search breaks the first time it's used (`Creating a worker from 'blob:...' violates ... worker-src`). Two ways to fix it:
|
|
127
|
+
- Allow it explicitly: `worker-src 'self' blob:`, which is what `assisted_app.py` and the other `setup()`-based examples do, since `setup()` deliberately has no CSP-specific parameters (it mirrors FastAPI's own `docs_url`/`redoc_url` surface, nothing more).
|
|
128
|
+
- Or skip the worker (and the search box) entirely by not using `setup()` for the ReDoc route: pass `redoc_url=None` to `setup()` and wire `/redoc` yourself with `get_redoc_html(disable_search=True)` instead, so no `blob:` is needed in `worker-src`. See [`examples/redoc_disable_search_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/redoc_disable_search_app.py).
|
|
129
|
+
|
|
130
|
+
**Logo image (`img-src`)**: ReDoc's "API docs by Redocly" attribution logo hardcodes an absolute `cdn.redoc.ly` URL, with no supported way to override it or point it at a local file. There's no CSP hash mechanism for images (hash-source only applies to `script-src`/`style-src`), so the only way to allow it is whitelisting that origin in `img-src`; both examples do this, since it's a small, non-code image request and doesn't weaken `script-src`/`style-src` at all. It's the one external origin left in `self_hosted_app.py`'s otherwise fully self-hosted CSP.
|
|
179
131
|
|
|
180
132
|
## Reference
|
|
181
133
|
|
|
@@ -206,8 +158,8 @@ Raises `RuntimeError` if `app.docs_url`/`app.redoc_url` is still set for the mod
|
|
|
206
158
|
|-----------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
|
|
207
159
|
| [`examples/assisted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/assisted_app.py) | `setup()` + CDN-hosted Swagger UI/ReDoc bundles, CSP header set via middleware. |
|
|
208
160
|
| [`examples/self_hosted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/self_hosted_app.py) | Fully self-hosted docs (no CDN), manual wiring, vendor assets served from `examples/static/`. |
|
|
209
|
-
| [`examples/mounted_subapp_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/mounted_subapp_app.py) | `setup()` on a sub-app mounted under a prefix (`app.mount("/api", sub_app)`)
|
|
210
|
-
| [`examples/root_path_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/root_path_app.py) | `setup()` on an app created with `FastAPI(root_path="/api")
|
|
161
|
+
| [`examples/mounted_subapp_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/mounted_subapp_app.py) | `setup()` on a sub-app mounted under a prefix (`app.mount("/api", sub_app)`), so doc URLs resolve correctly via the ASGI `root_path`. |
|
|
162
|
+
| [`examples/root_path_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/root_path_app.py) | `setup()` on an app created with `FastAPI(root_path="/api")`, the reverse-proxy way of setting `root_path`, as opposed to mounting a sub-app. |
|
|
211
163
|
| [`examples/oauth2_redirect_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/oauth2_redirect_app.py) | OAuth2 "Authorize" flow with a real security scheme, exercising the `/docs/oauth2-redirect` endpoint `setup()` registers. |
|
|
212
164
|
| [`examples/redoc_disable_search_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/redoc_disable_search_app.py) | `setup()` for Swagger UI + manual ReDoc wiring with `disable_search=True`, avoiding `worker-src 'self' blob:` entirely. |
|
|
213
165
|
|
|
@@ -217,4 +169,4 @@ See [RELEASE_NOTES.md](https://github.com/mat81black/fastapi-csp-docs/blob/main/
|
|
|
217
169
|
|
|
218
170
|
## License
|
|
219
171
|
|
|
220
|
-
MIT
|
|
172
|
+
MIT. See [LICENSE](https://github.com/mat81black/fastapi-csp-docs/blob/main/LICENSE).
|
|
@@ -1,34 +1,20 @@
|
|
|
1
1
|
# FastAPI CSP Docs
|
|
2
2
|
|
|
3
|
-
[](https://pypi.org/project/fastapi-csp-docs/)
|
|
6
|
-
[
|
|
11
|
-
recipe only swaps the CDN URLs for local ones — it doesn't remove any inline content:
|
|
12
|
-
`get_swagger_ui_html()` still embeds the Swagger UI bootstrap script inline, `get_redoc_html()`
|
|
13
|
-
still embeds a `<style>` reset inline, and `get_swagger_ui_oauth2_redirect_html()` still embeds
|
|
14
|
-
the OAuth2 redirect logic inline. `fastapi-csp-docs` replaces all three pages with versions that
|
|
15
|
-
load every script and stylesheet from a separate endpoint, with no inline content anywhere.
|
|
3
|
+
[](https://github.com/mat81black/fastapi-csp-docs/actions)
|
|
4
|
+
[](https://pypi.org/project/fastapi-csp-docs/)
|
|
5
|
+
[](https://pypi.org/project/fastapi-csp-docs/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
FastAPI's built-in `/docs`, `/redoc`, and OAuth2 redirect pages embed inline `<script>`/`<style>` tags, so they break under a Content-Security-Policy without `'unsafe-inline'`. Following FastAPI's own ["Custom Docs UI Static Assets"](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/) recipe only swaps the CDN URLs for local ones; it doesn't remove any inline content: `get_swagger_ui_html()` still embeds the Swagger UI bootstrap script inline, `get_redoc_html()` still embeds a `<style>` reset inline, and `get_swagger_ui_oauth2_redirect_html()` still embeds the OAuth2 redirect logic inline. `fastapi-csp-docs` replaces all three pages with versions that load every script and stylesheet from a separate endpoint, with no inline content anywhere.
|
|
16
9
|
|
|
17
10
|
## Features
|
|
18
11
|
|
|
19
|
-
- **Drop-in `setup()`**: one call replaces FastAPI's built-in `/docs`, `/redoc`, and OAuth2
|
|
20
|
-
|
|
21
|
-
- **
|
|
22
|
-
|
|
23
|
-
- **
|
|
24
|
-
|
|
25
|
-
(`script-src 'self'`, zero external origins).
|
|
26
|
-
- **Reverse-proxy / sub-app aware**: doc URLs include the ASGI `root_path` computed per request,
|
|
27
|
-
so mounting under a prefix (`app.mount("/api", sub_app)`) works out of the box.
|
|
28
|
-
- **Independently configurable mount paths**: `docs_url`/`redoc_url` are explicit `setup()`
|
|
29
|
-
parameters, each can be disabled on its own by passing `None`.
|
|
30
|
-
- **Content generators exported for manual wiring**: the same building blocks `setup()` uses
|
|
31
|
-
internally are public, for a fully self-hosted setup with no CDN at all.
|
|
12
|
+
- **Drop-in `setup()`**: one call replaces FastAPI's built-in `/docs`, `/redoc`, and OAuth2 redirect wiring, with no inline script or style left anywhere.
|
|
13
|
+
- **Fails fast on misconfiguration**: raises `RuntimeError` if the app's built-in docs aren't disabled first, instead of silently letting them shadow the CSP-safe routes.
|
|
14
|
+
- **CDN or fully self-hosted**: works with the default jsdelivr CDN (`script-src 'self' <cdn-host>`) or entirely offline with your own static assets (`script-src 'self'`, zero external origins).
|
|
15
|
+
- **Reverse-proxy / sub-app aware**: doc URLs include the ASGI `root_path` computed per request, so mounting under a prefix (`app.mount("/api", sub_app)`) works out of the box.
|
|
16
|
+
- **Independently configurable mount paths**: `docs_url`/`redoc_url` are explicit `setup()` parameters, each can be disabled on its own by passing `None`.
|
|
17
|
+
- **Content generators exported for manual wiring**: the same building blocks `setup()` uses internally are public, for a fully self-hosted setup with no CDN at all.
|
|
32
18
|
|
|
33
19
|
## Requirements
|
|
34
20
|
|
|
@@ -70,20 +56,13 @@ async def add_csp_header(request: Request, call_next) -> Response:
|
|
|
70
56
|
fastapi_csp_docs.setup(app)
|
|
71
57
|
```
|
|
72
58
|
|
|
73
|
-
`docs_url`/`redoc_url` must already be `None` on the app for whichever mode you're enabling
|
|
74
|
-
`setup()` raises `RuntimeError` otherwise, so FastAPI's built-in inline-script docs can't
|
|
75
|
-
silently shadow the routes it registers.
|
|
59
|
+
`docs_url`/`redoc_url` must already be `None` on the app for whichever mode you're enabling. `setup()` raises `RuntimeError` otherwise, so FastAPI's built-in inline-script docs can't silently shadow the routes it registers.
|
|
76
60
|
|
|
77
|
-
Full runnable version:
|
|
78
|
-
[`examples/assisted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/assisted_app.py).
|
|
61
|
+
Full runnable version: [`examples/assisted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/assisted_app.py).
|
|
79
62
|
|
|
80
63
|
### Fully self-hosted mode (no CDN at all)
|
|
81
64
|
|
|
82
|
-
For a CSP with zero external origins (`script-src 'self'`), skip `setup()` and wire the
|
|
83
|
-
endpoints directly — same mechanics as FastAPI's own
|
|
84
|
-
["Custom Docs UI Static Assets"](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)
|
|
85
|
-
recipe, but built from this package's content generators instead of `fastapi.openapi.docs`, so
|
|
86
|
-
the OAuth2 redirect page stays script-free too:
|
|
65
|
+
For a CSP with zero external origins (`script-src 'self'`), skip `setup()` and wire the endpoints directly, using the same mechanics as FastAPI's own ["Custom Docs UI Static Assets"](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/) recipe, but built from this package's content generators instead of `fastapi.openapi.docs`, so the OAuth2 redirect page stays script-free too:
|
|
87
66
|
|
|
88
67
|
```python
|
|
89
68
|
from fastapi import FastAPI
|
|
@@ -105,46 +84,19 @@ async def swagger_ui_html():
|
|
|
105
84
|
)
|
|
106
85
|
```
|
|
107
86
|
|
|
108
|
-
Full runnable version, including the `swagger-initializer.js`/`redoc.css`/OAuth2 redirect
|
|
109
|
-
endpoints and the vendor assets:
|
|
110
|
-
[`examples/self_hosted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/self_hosted_app.py).
|
|
87
|
+
Full runnable version, including the `swagger-initializer.js`/`redoc.css`/OAuth2 redirect endpoints and the vendor assets: [`examples/self_hosted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/self_hosted_app.py).
|
|
111
88
|
|
|
112
89
|
## ReDoc and Content-Security-Policy
|
|
113
90
|
|
|
114
|
-
Beyond the inline `<script>`/`<style>` tags this package removes from FastAPI's own generated
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
without `'unsafe-inline'`: open DevTools, note the exact `'sha256-...'` value(s) the browser
|
|
124
|
-
reports as blocked, and add them to `style-src`. These hashes are tied to the exact ReDoc
|
|
125
|
-
build, so they need to be recaptured whenever `redoc.standalone.js` is upgraded.
|
|
126
|
-
|
|
127
|
-
**Web Worker (`worker-src`)** — ReDoc runs its search indexing in a Web Worker, instantiated
|
|
128
|
-
from a `blob:` URL rather than a separate `.js` file: the worker's entire source is inlined as
|
|
129
|
-
a string inside `redoc.standalone.js`, so there's no real file URL to point `worker-src` at
|
|
130
|
-
instead. Worker instantiation is governed by `worker-src`, not `script-src` — without it set
|
|
131
|
-
explicitly, browsers fall back to `script-src`, which never matches a `blob:` URL, so search
|
|
132
|
-
breaks the first time it's used (`Creating a worker from 'blob:...' violates ... worker-src`).
|
|
133
|
-
Two ways to fix it:
|
|
134
|
-
- Allow it explicitly: `worker-src 'self' blob:` — what `assisted_app.py` and the other
|
|
135
|
-
`setup()`-based examples do, since `setup()` deliberately has no CSP-specific parameters (it
|
|
136
|
-
mirrors FastAPI's own `docs_url`/`redoc_url` surface, nothing more).
|
|
137
|
-
- Or skip the worker (and the search box) entirely by not using `setup()` for the ReDoc route:
|
|
138
|
-
pass `redoc_url=None` to `setup()` and wire `/redoc` yourself with
|
|
139
|
-
`get_redoc_html(disable_search=True)` instead — no `blob:` needed in `worker-src` then. See
|
|
140
|
-
[`examples/redoc_disable_search_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/redoc_disable_search_app.py).
|
|
141
|
-
|
|
142
|
-
**Logo image (`img-src`)** — ReDoc's "API docs by Redocly" attribution logo hardcodes an
|
|
143
|
-
absolute `cdn.redoc.ly` URL, with no supported way to override it or point it at a local file.
|
|
144
|
-
There's no CSP hash mechanism for images (hash-source only applies to `script-src`/`style-src`), so the only way to allow it is
|
|
145
|
-
whitelisting that origin in `img-src` — both examples do this, since it's a small, non-code
|
|
146
|
-
image request and doesn't weaken `script-src`/`style-src` at all. It's the one external origin
|
|
147
|
-
left in `self_hosted_app.py`'s otherwise fully self-hosted CSP.
|
|
91
|
+
Beyond the inline `<script>`/`<style>` tags this package removes from FastAPI's own generated HTML, ReDoc's *own* runtime does three more things that need explicit CSP entries. Both examples above already handle these (`examples/_csp.py` for the CDN-based ones, `examples/self_hosted_app.py` for the fully self-hosted one); this section explains why.
|
|
92
|
+
|
|
93
|
+
**Inline styles (`style-src`)**: ReDoc is built with `styled-components`, which injects `<style>` tags into the page at runtime instead of shipping one static stylesheet. This is unrelated to the inline content `fastapi-csp-docs` removes: it's ReDoc's own rendering mechanism, and this package can't eliminate it. A CSP hash allowlist is used to allow them without `'unsafe-inline'`: open DevTools, note the exact `'sha256-...'` value(s) the browser reports as blocked, and add them to `style-src`. These hashes are tied to the exact ReDoc build, so they need to be recaptured whenever `redoc.standalone.js` is upgraded.
|
|
94
|
+
|
|
95
|
+
**Web Worker (`worker-src`)**: ReDoc runs its search indexing in a Web Worker, instantiated from a `blob:` URL rather than a separate `.js` file: the worker's entire source is inlined as a string inside `redoc.standalone.js`, so there's no real file URL to point `worker-src` at instead. Worker instantiation is governed by `worker-src`, not `script-src`; without it set explicitly, browsers fall back to `script-src`, which never matches a `blob:` URL, so search breaks the first time it's used (`Creating a worker from 'blob:...' violates ... worker-src`). Two ways to fix it:
|
|
96
|
+
- Allow it explicitly: `worker-src 'self' blob:`, which is what `assisted_app.py` and the other `setup()`-based examples do, since `setup()` deliberately has no CSP-specific parameters (it mirrors FastAPI's own `docs_url`/`redoc_url` surface, nothing more).
|
|
97
|
+
- Or skip the worker (and the search box) entirely by not using `setup()` for the ReDoc route: pass `redoc_url=None` to `setup()` and wire `/redoc` yourself with `get_redoc_html(disable_search=True)` instead, so no `blob:` is needed in `worker-src`. See [`examples/redoc_disable_search_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/redoc_disable_search_app.py).
|
|
98
|
+
|
|
99
|
+
**Logo image (`img-src`)**: ReDoc's "API docs by Redocly" attribution logo hardcodes an absolute `cdn.redoc.ly` URL, with no supported way to override it or point it at a local file. There's no CSP hash mechanism for images (hash-source only applies to `script-src`/`style-src`), so the only way to allow it is whitelisting that origin in `img-src`; both examples do this, since it's a small, non-code image request and doesn't weaken `script-src`/`style-src` at all. It's the one external origin left in `self_hosted_app.py`'s otherwise fully self-hosted CSP.
|
|
148
100
|
|
|
149
101
|
## Reference
|
|
150
102
|
|
|
@@ -175,8 +127,8 @@ Raises `RuntimeError` if `app.docs_url`/`app.redoc_url` is still set for the mod
|
|
|
175
127
|
|-----------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
|
|
176
128
|
| [`examples/assisted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/assisted_app.py) | `setup()` + CDN-hosted Swagger UI/ReDoc bundles, CSP header set via middleware. |
|
|
177
129
|
| [`examples/self_hosted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/self_hosted_app.py) | Fully self-hosted docs (no CDN), manual wiring, vendor assets served from `examples/static/`. |
|
|
178
|
-
| [`examples/mounted_subapp_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/mounted_subapp_app.py) | `setup()` on a sub-app mounted under a prefix (`app.mount("/api", sub_app)`)
|
|
179
|
-
| [`examples/root_path_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/root_path_app.py) | `setup()` on an app created with `FastAPI(root_path="/api")
|
|
130
|
+
| [`examples/mounted_subapp_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/mounted_subapp_app.py) | `setup()` on a sub-app mounted under a prefix (`app.mount("/api", sub_app)`), so doc URLs resolve correctly via the ASGI `root_path`. |
|
|
131
|
+
| [`examples/root_path_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/root_path_app.py) | `setup()` on an app created with `FastAPI(root_path="/api")`, the reverse-proxy way of setting `root_path`, as opposed to mounting a sub-app. |
|
|
180
132
|
| [`examples/oauth2_redirect_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/oauth2_redirect_app.py) | OAuth2 "Authorize" flow with a real security scheme, exercising the `/docs/oauth2-redirect` endpoint `setup()` registers. |
|
|
181
133
|
| [`examples/redoc_disable_search_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/redoc_disable_search_app.py) | `setup()` for Swagger UI + manual ReDoc wiring with `disable_search=True`, avoiding `worker-src 'self' blob:` entirely. |
|
|
182
134
|
|
|
@@ -186,4 +138,4 @@ See [RELEASE_NOTES.md](https://github.com/mat81black/fastapi-csp-docs/blob/main/
|
|
|
186
138
|
|
|
187
139
|
## License
|
|
188
140
|
|
|
189
|
-
MIT
|
|
141
|
+
MIT. See [LICENSE](https://github.com/mat81black/fastapi-csp-docs/blob/main/LICENSE).
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Release Notes
|
|
2
|
+
|
|
3
|
+
## Latest Changes
|
|
4
|
+
|
|
5
|
+
## 0.1.1 (2026-07-08)
|
|
6
|
+
|
|
7
|
+
### Docs
|
|
8
|
+
|
|
9
|
+
* 📝 Update README badge styles and unwrap paragraphs. PR [#8](https://github.com/mat81black/fastapi-csp-docs/pull/8) by [@mat81black](https://github.com/mat81black).
|
|
10
|
+
|
|
11
|
+
## 0.1.0 (2026-07-08)
|
|
12
|
+
|
|
13
|
+
🚀 First official public release of **fastapi-csp-docs**.
|
|
14
|
+
|
|
15
|
+
FastAPI's built-in `/docs`, `/redoc`, and OAuth2 redirect pages embed inline `<script>`/`<style>` tags, breaking any Content-Security-Policy without `'unsafe-inline'`. Even FastAPI's own ["Custom Docs UI Static Assets"](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/) recipe only swaps CDN URLs for local ones, without removing that inline content. `fastapi-csp-docs` replaces all three pages with versions that load every script and stylesheet from a separate endpoint.
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* ✨ `setup(app, *, docs_url="/docs", redoc_url="/redoc")`: one call replaces FastAPI's built-in `/docs`, `/redoc`, and OAuth2 redirect wiring, with no inline script or style left anywhere.
|
|
20
|
+
* ✨ Fails fast on misconfiguration: `setup()` raises `RuntimeError` if the app's built-in docs aren't disabled first, instead of silently letting them shadow the CSP-safe routes.
|
|
21
|
+
* ✨ CDN or fully self-hosted: works with the default jsdelivr CDN (`script-src 'self' <cdn-host>`) or entirely offline with your own static assets (`script-src 'self'`, zero external origins).
|
|
22
|
+
* ✨ Reverse-proxy / sub-app aware: doc URLs include the ASGI `root_path` computed per request, so mounting under a prefix (`app.mount("/api", sub_app)`) works out of the box.
|
|
23
|
+
* ✨ Content generators (`get_swagger_ui_html`, `get_swagger_ui_init_js`, `get_redoc_html`, `get_redoc_css`, `get_swagger_ui_oauth2_redirect_html`, `get_swagger_ui_oauth2_redirect_js`) exported for manual wiring, enabling a fully self-hosted setup with no CDN at all.
|
|
24
|
+
* ✨ `get_redoc_html(disable_search=True)`: disables ReDoc's client-side search box, avoiding the Web Worker it runs in, which otherwise needs `worker-src 'self' blob:` in the CSP, since the worker is instantiated from a `blob:` URL with no separate file to point at instead.
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# Release Notes
|
|
2
|
-
|
|
3
|
-
## Latest Changes
|
|
4
|
-
|
|
5
|
-
## 0.1.0 (2026-07-08)
|
|
6
|
-
|
|
7
|
-
🚀 First official public release of **fastapi-csp-docs**.
|
|
8
|
-
|
|
9
|
-
FastAPI's built-in `/docs`, `/redoc`, and OAuth2 redirect pages embed inline `<script>`/`<style>`
|
|
10
|
-
tags, breaking any Content-Security-Policy without `'unsafe-inline'` — even FastAPI's own
|
|
11
|
-
["Custom Docs UI Static Assets"](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)
|
|
12
|
-
recipe only swaps CDN URLs for local ones, without removing that inline content.
|
|
13
|
-
`fastapi-csp-docs` replaces all three pages with versions that load every script and stylesheet
|
|
14
|
-
from a separate endpoint.
|
|
15
|
-
|
|
16
|
-
### Features
|
|
17
|
-
|
|
18
|
-
* ✨ `setup(app, *, docs_url="/docs", redoc_url="/redoc")`: one call replaces FastAPI's built-in
|
|
19
|
-
`/docs`, `/redoc`, and OAuth2 redirect wiring, with no inline script or style left anywhere.
|
|
20
|
-
* ✨ Fails fast on misconfiguration: `setup()` raises `RuntimeError` if the app's built-in docs
|
|
21
|
-
aren't disabled first, instead of silently letting them shadow the CSP-safe routes.
|
|
22
|
-
* ✨ CDN or fully self-hosted: works with the default jsdelivr CDN
|
|
23
|
-
(`script-src 'self' <cdn-host>`) or entirely offline with your own static assets
|
|
24
|
-
(`script-src 'self'`, zero external origins).
|
|
25
|
-
* ✨ Reverse-proxy / sub-app aware: doc URLs include the ASGI `root_path` computed per request,
|
|
26
|
-
so mounting under a prefix (`app.mount("/api", sub_app)`) works out of the box.
|
|
27
|
-
* ✨ Content generators (`get_swagger_ui_html`, `get_swagger_ui_init_js`, `get_redoc_html`,
|
|
28
|
-
`get_redoc_css`, `get_swagger_ui_oauth2_redirect_html`, `get_swagger_ui_oauth2_redirect_js`)
|
|
29
|
-
exported for manual wiring, enabling a fully self-hosted setup with no CDN at all.
|
|
30
|
-
* ✨ `get_redoc_html(disable_search=True)`: disables ReDoc's client-side search box, avoiding
|
|
31
|
-
the Web Worker it runs in — which otherwise needs `worker-src 'self' blob:` in the CSP, since
|
|
32
|
-
the worker is instantiated from a `blob:` URL with no separate file to point at instead.
|
|
File without changes
|
|
File without changes
|
{fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/create-draft-release.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-math-300-normal.woff2
RENAMED
|
File without changes
|
{fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-math-400-normal.woff2
RENAMED
|
File without changes
|
{fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-math-700-normal.woff2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|