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.
Files changed (92) hide show
  1. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/PKG-INFO +28 -76
  2. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/README.md +28 -76
  3. fastapi_csp_docs-0.1.1/RELEASE_NOTES.md +24 -0
  4. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/fastapi_csp_docs/__init__.py +1 -1
  5. fastapi_csp_docs-0.1.0/RELEASE_NOTES.md +0 -32
  6. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/dependabot.yml +0 -0
  7. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/labeler.yml +0 -0
  8. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/create-draft-release.yml +0 -0
  9. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/detect-conflicts.yml +0 -0
  10. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/guard-dependencies.yml +0 -0
  11. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/labeler.yml +0 -0
  12. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/latest-changes.yml +0 -0
  13. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/pre-commit.yml +0 -0
  14. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/prepare-release.yml +0 -0
  15. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/publish.yml +0 -0
  16. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/test-redistribute.yml +0 -0
  17. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/test.yml +0 -0
  18. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.github/workflows/zizmor.yml +0 -0
  19. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.gitignore +0 -0
  20. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.pre-commit-config.yaml +0 -0
  21. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/.python-version +0 -0
  22. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/LICENSE +0 -0
  23. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/__init__.py +0 -0
  24. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/_csp.py +0 -0
  25. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/assisted_app.py +0 -0
  26. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/mounted_subapp_app.py +0 -0
  27. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/oauth2_redirect_app.py +0 -0
  28. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/redoc_disable_search_app.py +0 -0
  29. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/root_path_app.py +0 -0
  30. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/self_hosted_app.py +0 -0
  31. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/favicon.png +0 -0
  32. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-cyrillic-300-normal.woff2 +0 -0
  33. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-cyrillic-400-normal.woff2 +0 -0
  34. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-cyrillic-700-normal.woff2 +0 -0
  35. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-cyrillic-ext-300-normal.woff2 +0 -0
  36. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-cyrillic-ext-400-normal.woff2 +0 -0
  37. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-cyrillic-ext-700-normal.woff2 +0 -0
  38. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-latin-300-normal.woff2 +0 -0
  39. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-latin-400-normal.woff2 +0 -0
  40. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-latin-700-normal.woff2 +0 -0
  41. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-latin-ext-300-normal.woff2 +0 -0
  42. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-latin-ext-400-normal.woff2 +0 -0
  43. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-latin-ext-700-normal.woff2 +0 -0
  44. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-vietnamese-300-normal.woff2 +0 -0
  45. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-vietnamese-400-normal.woff2 +0 -0
  46. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/montserrat-vietnamese-700-normal.woff2 +0 -0
  47. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-cyrillic-300-normal.woff2 +0 -0
  48. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-cyrillic-400-normal.woff2 +0 -0
  49. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-cyrillic-700-normal.woff2 +0 -0
  50. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-cyrillic-ext-300-normal.woff2 +0 -0
  51. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-cyrillic-ext-400-normal.woff2 +0 -0
  52. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-cyrillic-ext-700-normal.woff2 +0 -0
  53. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-greek-300-normal.woff2 +0 -0
  54. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-greek-400-normal.woff2 +0 -0
  55. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-greek-700-normal.woff2 +0 -0
  56. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-greek-ext-300-normal.woff2 +0 -0
  57. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-greek-ext-400-normal.woff2 +0 -0
  58. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-greek-ext-700-normal.woff2 +0 -0
  59. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-latin-300-normal.woff2 +0 -0
  60. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-latin-400-normal.woff2 +0 -0
  61. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-latin-700-normal.woff2 +0 -0
  62. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-latin-ext-300-normal.woff2 +0 -0
  63. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-latin-ext-400-normal.woff2 +0 -0
  64. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-latin-ext-700-normal.woff2 +0 -0
  65. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-math-300-normal.woff2 +0 -0
  66. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-math-400-normal.woff2 +0 -0
  67. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-math-700-normal.woff2 +0 -0
  68. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-symbols-300-normal.woff2 +0 -0
  69. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-symbols-400-normal.woff2 +0 -0
  70. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-symbols-700-normal.woff2 +0 -0
  71. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-vietnamese-300-normal.woff2 +0 -0
  72. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-vietnamese-400-normal.woff2 +0 -0
  73. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts/roboto-vietnamese-700-normal.woff2 +0 -0
  74. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/fonts.css +0 -0
  75. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/redoc.standalone.js +0 -0
  76. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/swagger-ui-bundle.js +0 -0
  77. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/examples/static/swagger-ui.css +0 -0
  78. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/fastapi_csp_docs/docs.py +0 -0
  79. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/fastapi_csp_docs/py.typed +0 -0
  80. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/fastapi_csp_docs/setup.py +0 -0
  81. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/pyproject.toml +0 -0
  82. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/scripts/format.sh +0 -0
  83. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/scripts/lint.sh +0 -0
  84. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/scripts/prepare_release.py +0 -0
  85. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/scripts/test-cov-html.sh +0 -0
  86. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/scripts/test-cov.sh +0 -0
  87. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/scripts/test.sh +0 -0
  88. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/tests/__init__.py +0 -0
  89. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/tests/test_docs.py +0 -0
  90. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/tests/test_prepare_release.py +0 -0
  91. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.1}/tests/test_setup.py +0 -0
  92. {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.0
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
- [![PyPI version](https://img.shields.io/pypi/v/fastapi-csp-docs.svg)](https://pypi.org/project/fastapi-csp-docs/)
35
- [![Test](https://github.com/mat81black/fastapi-csp-docs/actions/workflows/test.yml/badge.svg)](https://github.com/mat81black/fastapi-csp-docs/actions/workflows/test.yml)
36
- [![Python versions](https://img.shields.io/pypi/pyversions/fastapi-csp-docs.svg)](https://pypi.org/project/fastapi-csp-docs/)
37
- [![License: MIT](https://img.shields.io/pypi/l/fastapi-csp-docs.svg)](https://github.com/mat81black/fastapi-csp-docs/blob/main/LICENSE)
34
+ [![Build Status](https://github.com/mat81black/fastapi-csp-docs/workflows/Test/badge.svg)](https://github.com/mat81black/fastapi-csp-docs/actions)
35
+ [![Package version](https://badge.fury.io/py/fastapi-csp-docs.svg)](https://pypi.org/project/fastapi-csp-docs/)
36
+ [![Supported Python versions](https://img.shields.io/pypi/pyversions/fastapi-csp-docs.svg?color=%2334D058)](https://pypi.org/project/fastapi-csp-docs/)
37
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](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
- redirect wiring no inline script or style left anywhere.
52
- - **Fails fast on misconfiguration**: raises `RuntimeError` if the app's built-in docs aren't
53
- disabled first, instead of silently letting them shadow the CSP-safe routes.
54
- - **CDN or fully self-hosted**: works with the default jsdelivr CDN
55
- (`script-src 'self' <cdn-host>`) or entirely offline with your own static assets
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
- HTML, ReDoc's *own* runtime does three more things that need explicit CSP entries. Both
147
- examples above already handle these (`examples/_csp.py` for the CDN-based ones,
148
- `examples/self_hosted_app.py` for the fully self-hosted one) — this section explains why.
149
-
150
- **Inline styles (`style-src`)** ReDoc is built with `styled-components`, which injects
151
- `<style>` tags into the page at runtime instead of shipping one static stylesheet. This is
152
- unrelated to the inline content `fastapi-csp-docs` removes — it's ReDoc's own rendering
153
- mechanism, and this package can't eliminate it. A CSP hash allowlist is used to allow them
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)`) doc URLs resolve correctly via the ASGI `root_path`. |
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")` the reverse-proxy way of setting `root_path`, as opposed to mounting a sub-app. |
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 see [LICENSE](https://github.com/mat81black/fastapi-csp-docs/blob/main/LICENSE).
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
- [![PyPI version](https://img.shields.io/pypi/v/fastapi-csp-docs.svg)](https://pypi.org/project/fastapi-csp-docs/)
4
- [![Test](https://github.com/mat81black/fastapi-csp-docs/actions/workflows/test.yml/badge.svg)](https://github.com/mat81black/fastapi-csp-docs/actions/workflows/test.yml)
5
- [![Python versions](https://img.shields.io/pypi/pyversions/fastapi-csp-docs.svg)](https://pypi.org/project/fastapi-csp-docs/)
6
- [![License: MIT](https://img.shields.io/pypi/l/fastapi-csp-docs.svg)](https://github.com/mat81black/fastapi-csp-docs/blob/main/LICENSE)
7
-
8
- FastAPI's built-in `/docs`, `/redoc`, and OAuth2 redirect pages embed inline `<script>`/`<style>`
9
- tags, so they break under a Content-Security-Policy without `'unsafe-inline'`. Following FastAPI's
10
- own ["Custom Docs UI Static Assets"](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)
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
+ [![Build Status](https://github.com/mat81black/fastapi-csp-docs/workflows/Test/badge.svg)](https://github.com/mat81black/fastapi-csp-docs/actions)
4
+ [![Package version](https://badge.fury.io/py/fastapi-csp-docs.svg)](https://pypi.org/project/fastapi-csp-docs/)
5
+ [![Supported Python versions](https://img.shields.io/pypi/pyversions/fastapi-csp-docs.svg?color=%2334D058)](https://pypi.org/project/fastapi-csp-docs/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](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
- redirect wiring no inline script or style left anywhere.
21
- - **Fails fast on misconfiguration**: raises `RuntimeError` if the app's built-in docs aren't
22
- disabled first, instead of silently letting them shadow the CSP-safe routes.
23
- - **CDN or fully self-hosted**: works with the default jsdelivr CDN
24
- (`script-src 'self' <cdn-host>`) or entirely offline with your own static assets
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
- HTML, ReDoc's *own* runtime does three more things that need explicit CSP entries. Both
116
- examples above already handle these (`examples/_csp.py` for the CDN-based ones,
117
- `examples/self_hosted_app.py` for the fully self-hosted one) — this section explains why.
118
-
119
- **Inline styles (`style-src`)** ReDoc is built with `styled-components`, which injects
120
- `<style>` tags into the page at runtime instead of shipping one static stylesheet. This is
121
- unrelated to the inline content `fastapi-csp-docs` removes — it's ReDoc's own rendering
122
- mechanism, and this package can't eliminate it. A CSP hash allowlist is used to allow them
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)`) doc URLs resolve correctly via the ASGI `root_path`. |
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")` the reverse-proxy way of setting `root_path`, as opposed to mounting a sub-app. |
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 see [LICENSE](https://github.com/mat81black/fastapi-csp-docs/blob/main/LICENSE).
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.
@@ -8,7 +8,7 @@ from fastapi_csp_docs.docs import (
8
8
  )
9
9
  from fastapi_csp_docs.setup import setup
10
10
 
11
- __version__ = "0.1.0"
11
+ __version__ = "0.1.1"
12
12
 
13
13
  __all__ = [
14
14
  "get_redoc_css",
@@ -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.