fastapi-csp-docs 0.1.0__tar.gz → 0.1.2__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.2}/.github/workflows/pre-commit.yml +2 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/test-redistribute.yml +1 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/test.yml +48 -24
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/LICENSE +1 -1
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/PKG-INFO +29 -76
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/README.md +29 -76
- fastapi_csp_docs-0.1.2/RELEASE_NOTES.md +30 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/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.2}/.github/dependabot.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/labeler.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/create-draft-release.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/detect-conflicts.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/guard-dependencies.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/labeler.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/latest-changes.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/prepare-release.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/publish.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/zizmor.yml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.gitignore +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.pre-commit-config.yaml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.python-version +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/__init__.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/_csp.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/assisted_app.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/mounted_subapp_app.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/oauth2_redirect_app.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/redoc_disable_search_app.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/root_path_app.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/self_hosted_app.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/favicon.png +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-cyrillic-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-cyrillic-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-cyrillic-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-cyrillic-ext-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-cyrillic-ext-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-cyrillic-ext-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-latin-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-latin-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-latin-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-latin-ext-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-latin-ext-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-latin-ext-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-vietnamese-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-vietnamese-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-vietnamese-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-cyrillic-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-cyrillic-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-cyrillic-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-cyrillic-ext-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-cyrillic-ext-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-cyrillic-ext-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-greek-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-greek-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-greek-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-greek-ext-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-greek-ext-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-greek-ext-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-latin-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-latin-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-latin-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-latin-ext-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-latin-ext-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-latin-ext-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-math-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-math-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-math-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-symbols-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-symbols-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-symbols-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-vietnamese-300-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-vietnamese-400-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-vietnamese-700-normal.woff2 +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts.css +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/redoc.standalone.js +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/swagger-ui-bundle.js +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/swagger-ui.css +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/fastapi_csp_docs/docs.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/fastapi_csp_docs/py.typed +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/fastapi_csp_docs/setup.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/pyproject.toml +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/scripts/format.sh +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/scripts/lint.sh +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/scripts/prepare_release.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/scripts/test-cov-html.sh +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/scripts/test-cov.sh +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/scripts/test.sh +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/tests/__init__.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/tests/test_docs.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/tests/test_prepare_release.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/tests/test_setup.py +0 -0
- {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/uv.lock +0 -0
|
@@ -32,6 +32,8 @@ jobs:
|
|
|
32
32
|
- name: Setup uv
|
|
33
33
|
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
|
|
34
34
|
with:
|
|
35
|
+
# Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
|
|
36
|
+
# See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
|
|
35
37
|
version: "0.11.18"
|
|
36
38
|
cache-dependency-glob: |
|
|
37
39
|
pyproject.toml
|
|
@@ -6,9 +6,10 @@ on:
|
|
|
6
6
|
- main
|
|
7
7
|
pull_request:
|
|
8
8
|
schedule:
|
|
9
|
+
# cron every week on monday
|
|
9
10
|
- cron: "0 0 * * 1"
|
|
10
11
|
|
|
11
|
-
permissions: {
|
|
12
|
+
permissions: {}
|
|
12
13
|
|
|
13
14
|
env:
|
|
14
15
|
UV_NO_SYNC: true
|
|
@@ -16,27 +17,31 @@ env:
|
|
|
16
17
|
jobs:
|
|
17
18
|
changes:
|
|
18
19
|
runs-on: ubuntu-latest
|
|
20
|
+
# Required permissions
|
|
19
21
|
permissions:
|
|
22
|
+
contents: read
|
|
20
23
|
pull-requests: read
|
|
21
24
|
timeout-minutes: 5
|
|
25
|
+
# Set job outputs to values from filter step
|
|
22
26
|
outputs:
|
|
23
27
|
src: ${{ steps.filter.outputs.src }}
|
|
24
28
|
steps:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
30
|
+
with:
|
|
31
|
+
persist-credentials: false
|
|
32
|
+
# For pull requests it's not necessary to checkout the code but for the main branch it is
|
|
33
|
+
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
|
34
|
+
id: filter
|
|
35
|
+
with:
|
|
36
|
+
filters: |
|
|
37
|
+
src:
|
|
38
|
+
- .github/workflows/test.yml
|
|
39
|
+
- fastapi_csp_docs/**
|
|
40
|
+
- scripts/**
|
|
41
|
+
- tests/**
|
|
42
|
+
- .python-version
|
|
43
|
+
- pyproject.toml
|
|
44
|
+
- uv.lock
|
|
40
45
|
|
|
41
46
|
test:
|
|
42
47
|
needs:
|
|
@@ -79,14 +84,16 @@ jobs:
|
|
|
79
84
|
- name: Setup uv
|
|
80
85
|
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
|
|
81
86
|
with:
|
|
87
|
+
# Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
|
|
88
|
+
# See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
|
|
82
89
|
version: "0.11.18"
|
|
83
90
|
enable-cache: true
|
|
84
|
-
cache-suffix: ${{ matrix.
|
|
91
|
+
cache-suffix: ${{ matrix.python-version }}-fa${{ matrix.fastapi-version }}
|
|
85
92
|
cache-dependency-glob: |
|
|
86
93
|
pyproject.toml
|
|
87
94
|
uv.lock
|
|
88
95
|
|
|
89
|
-
- name: Install
|
|
96
|
+
- name: Install Dependencies
|
|
90
97
|
run: uv sync
|
|
91
98
|
|
|
92
99
|
- name: Override FastAPI version
|
|
@@ -97,17 +104,16 @@ jobs:
|
|
|
97
104
|
run: mkdir coverage
|
|
98
105
|
|
|
99
106
|
- name: Test
|
|
100
|
-
# GitHub Windows runners ship bash natively
|
|
101
107
|
run: uv run --no-sync bash scripts/test-cov.sh
|
|
102
108
|
env:
|
|
103
|
-
COVERAGE_FILE: coverage/.coverage
|
|
104
|
-
CONTEXT:
|
|
109
|
+
COVERAGE_FILE: coverage/.coverage.py${{ matrix.python-version }}-fa${{ matrix.fastapi-version }}
|
|
110
|
+
CONTEXT: py${{ matrix.python-version }}-fa${{ matrix.fastapi-version }}
|
|
105
111
|
|
|
106
112
|
- name: Store coverage files
|
|
107
113
|
if: matrix.coverage == 'coverage'
|
|
108
114
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
109
115
|
with:
|
|
110
|
-
name: coverage-${{
|
|
116
|
+
name: coverage-${{ matrix.python-version }}-${{ matrix.fastapi-version }}
|
|
111
117
|
path: coverage
|
|
112
118
|
include-hidden-files: true
|
|
113
119
|
|
|
@@ -119,6 +125,11 @@ jobs:
|
|
|
119
125
|
permissions:
|
|
120
126
|
contents: read
|
|
121
127
|
steps:
|
|
128
|
+
- name: Dump GitHub context
|
|
129
|
+
env:
|
|
130
|
+
GITHUB_CONTEXT: ${{ toJson(github) }}
|
|
131
|
+
run: echo "$GITHUB_CONTEXT"
|
|
132
|
+
|
|
122
133
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
123
134
|
with:
|
|
124
135
|
persist-credentials: false
|
|
@@ -126,6 +137,8 @@ jobs:
|
|
|
126
137
|
- name: Setup uv
|
|
127
138
|
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
|
|
128
139
|
with:
|
|
140
|
+
# Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
|
|
141
|
+
# See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
|
|
129
142
|
version: "0.11.18"
|
|
130
143
|
enable-cache: true
|
|
131
144
|
cache-dependency-glob: |
|
|
@@ -145,6 +158,7 @@ jobs:
|
|
|
145
158
|
- run: ls -la coverage
|
|
146
159
|
- run: uv run coverage combine coverage
|
|
147
160
|
- run: uv run coverage html --title "Coverage for ${{ github.sha }}"
|
|
161
|
+
- run: uv run coverage xml
|
|
148
162
|
|
|
149
163
|
- name: Store coverage HTML
|
|
150
164
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
@@ -153,18 +167,28 @@ jobs:
|
|
|
153
167
|
path: htmlcov
|
|
154
168
|
include-hidden-files: true
|
|
155
169
|
|
|
170
|
+
- name: Upload coverage to Codecov
|
|
171
|
+
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
|
172
|
+
with:
|
|
173
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
174
|
+
files: coverage.xml
|
|
175
|
+
fail_ci_if_error: true
|
|
176
|
+
|
|
156
177
|
- run: uv run coverage report --fail-under=100
|
|
157
178
|
|
|
158
179
|
# https://github.com/marketplace/actions/alls-green#why
|
|
159
|
-
test-alls-green:
|
|
180
|
+
test-alls-green: # This job does nothing and is only used for the branch protection
|
|
160
181
|
if: always()
|
|
161
182
|
needs:
|
|
162
183
|
- test
|
|
163
184
|
- coverage-combine
|
|
164
185
|
runs-on: ubuntu-latest
|
|
165
186
|
timeout-minutes: 5
|
|
166
|
-
permissions: { }
|
|
167
187
|
steps:
|
|
188
|
+
- name: Dump GitHub context
|
|
189
|
+
env:
|
|
190
|
+
GITHUB_CONTEXT: ${{ toJson(github) }}
|
|
191
|
+
run: echo "$GITHUB_CONTEXT"
|
|
168
192
|
- name: Decide whether the needed jobs succeeded or failed
|
|
169
193
|
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
|
|
170
194
|
with:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-csp-docs
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
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,22 @@ Description-Content-Type: text/markdown
|
|
|
31
31
|
|
|
32
32
|
# FastAPI CSP Docs
|
|
33
33
|
|
|
34
|
-
[](https://github.com/mat81black/fastapi-csp-docs/actions)
|
|
35
|
+
[](https://codecov.io/gh/mat81black/fastapi-csp-docs)
|
|
36
|
+
[](https://pypi.org/project/fastapi-csp-docs/)
|
|
37
|
+
[](https://pypi.org/project/fastapi-csp-docs/)
|
|
38
|
+
[](LICENSE)
|
|
38
39
|
|
|
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.
|
|
40
|
+
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
41
|
|
|
48
42
|
## Features
|
|
49
43
|
|
|
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.
|
|
44
|
+
- **Drop-in `setup()`**: one call replaces FastAPI's built-in `/docs`, `/redoc`, and OAuth2 redirect wiring, with no inline script or style left anywhere.
|
|
45
|
+
- **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.
|
|
46
|
+
- **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).
|
|
47
|
+
- **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.
|
|
48
|
+
- **Independently configurable mount paths**: `docs_url`/`redoc_url` are explicit `setup()` parameters, each can be disabled on its own by passing `None`.
|
|
49
|
+
- **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
50
|
|
|
64
51
|
## Requirements
|
|
65
52
|
|
|
@@ -101,20 +88,13 @@ async def add_csp_header(request: Request, call_next) -> Response:
|
|
|
101
88
|
fastapi_csp_docs.setup(app)
|
|
102
89
|
```
|
|
103
90
|
|
|
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.
|
|
91
|
+
`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
92
|
|
|
108
|
-
Full runnable version:
|
|
109
|
-
[`examples/assisted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/assisted_app.py).
|
|
93
|
+
Full runnable version: [`examples/assisted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/assisted_app.py).
|
|
110
94
|
|
|
111
95
|
### Fully self-hosted mode (no CDN at all)
|
|
112
96
|
|
|
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:
|
|
97
|
+
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
98
|
|
|
119
99
|
```python
|
|
120
100
|
from fastapi import FastAPI
|
|
@@ -136,46 +116,19 @@ async def swagger_ui_html():
|
|
|
136
116
|
)
|
|
137
117
|
```
|
|
138
118
|
|
|
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).
|
|
119
|
+
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
120
|
|
|
143
121
|
## ReDoc and Content-Security-Policy
|
|
144
122
|
|
|
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.
|
|
123
|
+
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.
|
|
124
|
+
|
|
125
|
+
**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.
|
|
126
|
+
|
|
127
|
+
**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:
|
|
128
|
+
- 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).
|
|
129
|
+
- 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).
|
|
130
|
+
|
|
131
|
+
**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
132
|
|
|
180
133
|
## Reference
|
|
181
134
|
|
|
@@ -206,8 +159,8 @@ Raises `RuntimeError` if `app.docs_url`/`app.redoc_url` is still set for the mod
|
|
|
206
159
|
|-----------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
|
|
207
160
|
| [`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
161
|
| [`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")
|
|
162
|
+
| [`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`. |
|
|
163
|
+
| [`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
164
|
| [`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
165
|
| [`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
166
|
|
|
@@ -217,4 +170,4 @@ See [RELEASE_NOTES.md](https://github.com/mat81black/fastapi-csp-docs/blob/main/
|
|
|
217
170
|
|
|
218
171
|
## License
|
|
219
172
|
|
|
220
|
-
MIT
|
|
173
|
+
MIT. See [LICENSE](https://github.com/mat81black/fastapi-csp-docs/blob/main/LICENSE).
|
|
@@ -1,34 +1,21 @@
|
|
|
1
1
|
# FastAPI CSP Docs
|
|
2
2
|
|
|
3
|
-
[
|
|
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://codecov.io/gh/mat81black/fastapi-csp-docs)
|
|
5
|
+
[](https://pypi.org/project/fastapi-csp-docs/)
|
|
6
|
+
[](https://pypi.org/project/fastapi-csp-docs/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
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
10
|
|
|
17
11
|
## Features
|
|
18
12
|
|
|
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.
|
|
13
|
+
- **Drop-in `setup()`**: one call replaces FastAPI's built-in `/docs`, `/redoc`, and OAuth2 redirect wiring, with no inline script or style left anywhere.
|
|
14
|
+
- **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.
|
|
15
|
+
- **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).
|
|
16
|
+
- **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.
|
|
17
|
+
- **Independently configurable mount paths**: `docs_url`/`redoc_url` are explicit `setup()` parameters, each can be disabled on its own by passing `None`.
|
|
18
|
+
- **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
19
|
|
|
33
20
|
## Requirements
|
|
34
21
|
|
|
@@ -70,20 +57,13 @@ async def add_csp_header(request: Request, call_next) -> Response:
|
|
|
70
57
|
fastapi_csp_docs.setup(app)
|
|
71
58
|
```
|
|
72
59
|
|
|
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.
|
|
60
|
+
`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
61
|
|
|
77
|
-
Full runnable version:
|
|
78
|
-
[`examples/assisted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/assisted_app.py).
|
|
62
|
+
Full runnable version: [`examples/assisted_app.py`](https://github.com/mat81black/fastapi-csp-docs/blob/main/examples/assisted_app.py).
|
|
79
63
|
|
|
80
64
|
### Fully self-hosted mode (no CDN at all)
|
|
81
65
|
|
|
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:
|
|
66
|
+
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
67
|
|
|
88
68
|
```python
|
|
89
69
|
from fastapi import FastAPI
|
|
@@ -105,46 +85,19 @@ async def swagger_ui_html():
|
|
|
105
85
|
)
|
|
106
86
|
```
|
|
107
87
|
|
|
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).
|
|
88
|
+
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
89
|
|
|
112
90
|
## ReDoc and Content-Security-Policy
|
|
113
91
|
|
|
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.
|
|
92
|
+
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.
|
|
93
|
+
|
|
94
|
+
**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.
|
|
95
|
+
|
|
96
|
+
**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:
|
|
97
|
+
- 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).
|
|
98
|
+
- 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).
|
|
99
|
+
|
|
100
|
+
**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
101
|
|
|
149
102
|
## Reference
|
|
150
103
|
|
|
@@ -175,8 +128,8 @@ Raises `RuntimeError` if `app.docs_url`/`app.redoc_url` is still set for the mod
|
|
|
175
128
|
|-----------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
|
|
176
129
|
| [`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
130
|
| [`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")
|
|
131
|
+
| [`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`. |
|
|
132
|
+
| [`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
133
|
| [`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
134
|
| [`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
135
|
|
|
@@ -186,4 +139,4 @@ See [RELEASE_NOTES.md](https://github.com/mat81black/fastapi-csp-docs/blob/main/
|
|
|
186
139
|
|
|
187
140
|
## License
|
|
188
141
|
|
|
189
|
-
MIT
|
|
142
|
+
MIT. See [LICENSE](https://github.com/mat81black/fastapi-csp-docs/blob/main/LICENSE).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Release Notes
|
|
2
|
+
|
|
3
|
+
## Latest Changes
|
|
4
|
+
|
|
5
|
+
## 0.1.2 (2026-07-09)
|
|
6
|
+
|
|
7
|
+
### Internal
|
|
8
|
+
|
|
9
|
+
* 🔧 Integrate Codecov for coverage tracking and update README. PR [#10](https://github.com/mat81black/fastapi-csp-docs/pull/10) by [@mat81black](https://github.com/mat81black).
|
|
10
|
+
|
|
11
|
+
## 0.1.1 (2026-07-08)
|
|
12
|
+
|
|
13
|
+
### Docs
|
|
14
|
+
|
|
15
|
+
* 📝 Update README badge styles and unwrap paragraphs. PR [#8](https://github.com/mat81black/fastapi-csp-docs/pull/8) by [@mat81black](https://github.com/mat81black).
|
|
16
|
+
|
|
17
|
+
## 0.1.0 (2026-07-08)
|
|
18
|
+
|
|
19
|
+
🚀 First official public release of **fastapi-csp-docs**.
|
|
20
|
+
|
|
21
|
+
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.
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* ✨ `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.
|
|
26
|
+
* ✨ 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.
|
|
27
|
+
* ✨ 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).
|
|
28
|
+
* ✨ 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.
|
|
29
|
+
* ✨ 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.
|
|
30
|
+
* ✨ `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.2}/.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
|
{fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-math-300-normal.woff2
RENAMED
|
File without changes
|
{fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-math-400-normal.woff2
RENAMED
|
File without changes
|
{fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/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
|