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.
Files changed (92) hide show
  1. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/pre-commit.yml +2 -0
  2. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/test-redistribute.yml +1 -0
  3. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/test.yml +48 -24
  4. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/LICENSE +1 -1
  5. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/PKG-INFO +29 -76
  6. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/README.md +29 -76
  7. fastapi_csp_docs-0.1.2/RELEASE_NOTES.md +30 -0
  8. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/fastapi_csp_docs/__init__.py +1 -1
  9. fastapi_csp_docs-0.1.0/RELEASE_NOTES.md +0 -32
  10. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/dependabot.yml +0 -0
  11. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/labeler.yml +0 -0
  12. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/create-draft-release.yml +0 -0
  13. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/detect-conflicts.yml +0 -0
  14. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/guard-dependencies.yml +0 -0
  15. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/labeler.yml +0 -0
  16. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/latest-changes.yml +0 -0
  17. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/prepare-release.yml +0 -0
  18. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/publish.yml +0 -0
  19. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.github/workflows/zizmor.yml +0 -0
  20. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.gitignore +0 -0
  21. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.pre-commit-config.yaml +0 -0
  22. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/.python-version +0 -0
  23. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/__init__.py +0 -0
  24. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/_csp.py +0 -0
  25. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/assisted_app.py +0 -0
  26. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/mounted_subapp_app.py +0 -0
  27. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/oauth2_redirect_app.py +0 -0
  28. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/redoc_disable_search_app.py +0 -0
  29. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/root_path_app.py +0 -0
  30. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/self_hosted_app.py +0 -0
  31. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/favicon.png +0 -0
  32. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-cyrillic-300-normal.woff2 +0 -0
  33. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-cyrillic-400-normal.woff2 +0 -0
  34. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-cyrillic-700-normal.woff2 +0 -0
  35. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-cyrillic-ext-300-normal.woff2 +0 -0
  36. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-cyrillic-ext-400-normal.woff2 +0 -0
  37. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-cyrillic-ext-700-normal.woff2 +0 -0
  38. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-latin-300-normal.woff2 +0 -0
  39. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-latin-400-normal.woff2 +0 -0
  40. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-latin-700-normal.woff2 +0 -0
  41. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-latin-ext-300-normal.woff2 +0 -0
  42. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-latin-ext-400-normal.woff2 +0 -0
  43. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-latin-ext-700-normal.woff2 +0 -0
  44. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-vietnamese-300-normal.woff2 +0 -0
  45. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-vietnamese-400-normal.woff2 +0 -0
  46. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/montserrat-vietnamese-700-normal.woff2 +0 -0
  47. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-cyrillic-300-normal.woff2 +0 -0
  48. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-cyrillic-400-normal.woff2 +0 -0
  49. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-cyrillic-700-normal.woff2 +0 -0
  50. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-cyrillic-ext-300-normal.woff2 +0 -0
  51. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-cyrillic-ext-400-normal.woff2 +0 -0
  52. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-cyrillic-ext-700-normal.woff2 +0 -0
  53. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-greek-300-normal.woff2 +0 -0
  54. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-greek-400-normal.woff2 +0 -0
  55. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-greek-700-normal.woff2 +0 -0
  56. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-greek-ext-300-normal.woff2 +0 -0
  57. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-greek-ext-400-normal.woff2 +0 -0
  58. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-greek-ext-700-normal.woff2 +0 -0
  59. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-latin-300-normal.woff2 +0 -0
  60. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-latin-400-normal.woff2 +0 -0
  61. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-latin-700-normal.woff2 +0 -0
  62. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-latin-ext-300-normal.woff2 +0 -0
  63. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-latin-ext-400-normal.woff2 +0 -0
  64. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-latin-ext-700-normal.woff2 +0 -0
  65. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-math-300-normal.woff2 +0 -0
  66. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-math-400-normal.woff2 +0 -0
  67. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-math-700-normal.woff2 +0 -0
  68. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-symbols-300-normal.woff2 +0 -0
  69. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-symbols-400-normal.woff2 +0 -0
  70. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-symbols-700-normal.woff2 +0 -0
  71. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-vietnamese-300-normal.woff2 +0 -0
  72. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-vietnamese-400-normal.woff2 +0 -0
  73. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts/roboto-vietnamese-700-normal.woff2 +0 -0
  74. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/fonts.css +0 -0
  75. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/redoc.standalone.js +0 -0
  76. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/swagger-ui-bundle.js +0 -0
  77. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/examples/static/swagger-ui.css +0 -0
  78. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/fastapi_csp_docs/docs.py +0 -0
  79. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/fastapi_csp_docs/py.typed +0 -0
  80. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/fastapi_csp_docs/setup.py +0 -0
  81. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/pyproject.toml +0 -0
  82. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/scripts/format.sh +0 -0
  83. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/scripts/lint.sh +0 -0
  84. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/scripts/prepare_release.py +0 -0
  85. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/scripts/test-cov-html.sh +0 -0
  86. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/scripts/test-cov.sh +0 -0
  87. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/scripts/test.sh +0 -0
  88. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/tests/__init__.py +0 -0
  89. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/tests/test_docs.py +0 -0
  90. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/tests/test_prepare_release.py +0 -0
  91. {fastapi_csp_docs-0.1.0 → fastapi_csp_docs-0.1.2}/tests/test_setup.py +0 -0
  92. {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
@@ -12,6 +12,7 @@ jobs:
12
12
  changes:
13
13
  runs-on: ubuntu-latest
14
14
  permissions:
15
+ contents: read
15
16
  pull-requests: read
16
17
  timeout-minutes: 5
17
18
  outputs:
@@ -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
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
26
- with:
27
- persist-credentials: false
28
- - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
29
- id: filter
30
- with:
31
- filters: |
32
- src:
33
- - .github/workflows/test.yml
34
- - fastapi_csp_docs/**
35
- - scripts/**
36
- - tests/**
37
- - .python-version
38
- - pyproject.toml
39
- - uv.lock
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.os }}-${{ matrix.python-version }}-${{ matrix.uv-resolution }}-fa${{ matrix.fastapi-version }}
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 dependencies
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.${{ runner.os }}-py${{ matrix.python-version }}-fa${{ matrix.fastapi-version }}
104
- CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}-fa${{ matrix.fastapi-version }}
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-${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.fastapi-version }}
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: # This job does nothing and is only used for the branch protection
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
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018 Sebastián Ramírez
3
+ Copyright (c) 2026 Matteo Nieddu
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -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.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
- [![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
+ [![codecov](https://codecov.io/gh/mat81black/fastapi-csp-docs/graph/badge.svg)](https://codecov.io/gh/mat81black/fastapi-csp-docs)
36
+ [![Package version](https://badge.fury.io/py/fastapi-csp-docs.svg)](https://pypi.org/project/fastapi-csp-docs/)
37
+ [![Supported Python versions](https://img.shields.io/pypi/pyversions/fastapi-csp-docs.svg?color=%2334D058)](https://pypi.org/project/fastapi-csp-docs/)
38
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](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
- 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.
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
- 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.
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)`) 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. |
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 see [LICENSE](https://github.com/mat81black/fastapi-csp-docs/blob/main/LICENSE).
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
- [![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
+ [![codecov](https://codecov.io/gh/mat81black/fastapi-csp-docs/graph/badge.svg)](https://codecov.io/gh/mat81black/fastapi-csp-docs)
5
+ [![Package version](https://badge.fury.io/py/fastapi-csp-docs.svg)](https://pypi.org/project/fastapi-csp-docs/)
6
+ [![Supported Python versions](https://img.shields.io/pypi/pyversions/fastapi-csp-docs.svg?color=%2334D058)](https://pypi.org/project/fastapi-csp-docs/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](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
- 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.
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
- 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.
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)`) 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. |
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 see [LICENSE](https://github.com/mat81black/fastapi-csp-docs/blob/main/LICENSE).
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.
@@ -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.2"
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.