Skip to content

Commit d166640

Browse files
committed
fix: add Dash and Shiny framework integration guides to README
1 parent 175b6bc commit d166640

1 file changed

Lines changed: 51 additions & 24 deletions

File tree

README.md

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ If you want your app to ship with a strong security baseline without pulling in
5050
| [aiohttp](https://docs.aiohttp.org) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#aiohttp) |
5151
| [Bottle](https://bottlepy.org) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#bottle) |
5252
| [CherryPy](https://cherrypy.dev/) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#cherrypy) |
53+
| [Dash](https://dash.plotly.com/) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#dash) |
5354
| [Django](https://www.djangoproject.com) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#django) |
5455
| [Falcon](https://falconframework.org) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#falcon) |
5556
| [FastAPI](https://fastapi.tiangolo.com) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#fastapi) |
@@ -60,6 +61,7 @@ If you want your app to ship with a strong security baseline without pulling in
6061
| [Quart](https://quart.palletsprojects.com/en/latest/) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#quart) |
6162
| [Responder](https://responder.kennethreitz.org/) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#responder) |
6263
| [Sanic](https://sanicframework.org) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#sanic) |
64+
| [Shiny](https://shiny.posit.co/py/) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#shiny) |
6365
| [Starlette](https://www.starlette.io/) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#starlette) |
6466
| [Tornado](https://www.tornadoweb.org/) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#tornado) |
6567
| [TurboGears](https://turbogears.org/) | [Integration Guide](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#turbogears) |
@@ -154,7 +156,7 @@ If your framework uses a different contract, see the framework specific guides o
154156

155157
## Middleware
156158

157-
`secure.middleware` re-exports `SecureWSGIMiddleware` and `SecureASGIMiddleware`. Each middleware accepts a `Secure` instance (defaulting to `Secure.with_default_headers()`), overwrites headers by default, and only appends duplicates when a normalized name is included in `multi_ok` (the default `secure.secure.MULTI_OK` includes `Content-Security-Policy`).
159+
`secure.middleware` re-exports `SecureWSGIMiddleware` and `SecureASGIMiddleware`. Each middleware accepts a `Secure` instance (defaulting to `Secure.with_default_headers()`), overwrites headers by default, and only appends duplicates when a normalized name is included in `multi_ok` (the default `secure.MULTI_OK` includes `Content-Security-Policy`).
158160

159161
### WSGI (Flask + Django)
160162

@@ -295,10 +297,10 @@ Cross-Origin-Resource-Policy: same-origin
295297
Content-Security-Policy: default-src 'self'; base-uri 'self'; font-src 'self' https: data:; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src 'self'; script-src-attr 'none'; style-src 'self' https: 'unsafe-inline'; upgrade-insecure-requests
296298
Strict-Transport-Security: max-age=31536000; includeSubDomains
297299
Referrer-Policy: no-referrer
298-
X-Content-Type-Options: nosniff
299-
X-Frame-Options: SAMEORIGIN
300300
X-Permitted-Cross-Domain-Policies: none
301301
X-DNS-Prefetch-Control: off
302+
X-Content-Type-Options: nosniff
303+
X-Frame-Options: SAMEORIGIN
302304
Origin-Agent-Cluster: ?1
303305
X-Download-Options: noopen
304306
X-XSS-Protection: 0
@@ -334,18 +336,19 @@ Start with `BALANCED` and move to `STRICT` once you have validated that your app
334336
### Content Security Policy
335337

336338
```python
337-
import secure
339+
from secure import Secure
340+
from secure.headers import ContentSecurityPolicy
338341

339342
csp = (
340-
secure.ContentSecurityPolicy()
343+
ContentSecurityPolicy()
341344
.default_src("'self'")
342345
.script_src("'self'", "cdn.typeerror.com")
343346
.style_src("'unsafe-inline'")
344347
.img_src("'self'", "images.typeerror.com")
345348
.connect_src("'self'", "api.typeerror.com")
346349
)
347350

348-
secure_headers = secure.Secure(csp=csp)
351+
secure_headers = Secure(csp=csp)
349352
```
350353

351354
Resulting header:
@@ -359,22 +362,20 @@ You can treat the CSP builder as a safe string builder for CSP directives and ke
359362
### Permissions Policy
360363

361364
```python
362-
import secure
365+
from secure import Secure
366+
from secure.headers import PermissionsPolicy
363367

364368
permissions = (
365-
secure.PermissionsPolicy()
366-
.geolocation("'self'")
367-
.camera("'none'")
368-
.microphone("'none'")
369+
PermissionsPolicy().geolocation("'self'").camera("'none'").microphone("'none'")
369370
)
370371

371-
secure_headers = secure.Secure(permissions=permissions)
372+
secure_headers = Secure(permissions=permissions)
372373
```
373374

374375
Resulting header:
375376

376377
```http
377-
Permissions-Policy: geolocation=('self'), camera=('none'), microphone=('none')
378+
Permissions-Policy: geolocation=(self), camera=(), microphone=()
378379
```
379380

380381
Other headers, such as `StrictTransportSecurity`, `CrossOriginOpenerPolicy`, `CrossOriginEmbedderPolicy`, `ReferrerPolicy`, `Server`, and `XFrameOptions`, also have small builder classes that mirror their directive structure.
@@ -387,27 +388,28 @@ For most applications, it is enough to construct a `Secure` instance and call `s
387388

388389
```python
389390
import logging
390-
import secure
391+
392+
from secure import COMMA_JOIN_OK, DEFAULT_ALLOWED_HEADERS, MULTI_OK, Secure
391393

392394
logger = logging.getLogger("secure")
393395

394396
secure_headers = (
395-
secure.Secure.with_default_headers()
397+
Secure.with_default_headers()
396398
.allowlist_headers(
397-
allowed=secure.DEFAULT_ALLOWED_HEADERS,
399+
allowed=DEFAULT_ALLOWED_HEADERS,
398400
allow_extra=["X-My-App-Header"],
399-
on_unexpected="warn", # "raise" (default), "drop", or "warn"
401+
on_unexpected="warn", # "raise" (default), "drop", or "warn"
400402
allow_x_prefixed=False,
401403
logger=logger,
402404
)
403405
.deduplicate_headers(
404-
action="raise", # "raise" (default), "first", "last", or "concat"
405-
comma_join_ok=secure.COMMA_JOIN_OK,
406-
multi_ok=secure.MULTI_OK,
406+
action="raise", # "raise" (default), "first", "last", or "concat"
407+
comma_join_ok=COMMA_JOIN_OK,
408+
multi_ok=MULTI_OK,
407409
logger=logger,
408410
)
409411
.validate_and_normalize_headers(
410-
on_invalid="drop", # "drop" (default), "warn", or "raise"
412+
on_invalid="drop", # "drop" (default), "warn", or "raise"
411413
strict=False,
412414
allow_obs_text=False,
413415
logger=logger,
@@ -509,6 +511,27 @@ def read_root():
509511
return {"Hello": "World"}
510512
```
511513

514+
### Starlette
515+
516+
#### Recommended: `add_middleware` (ASGI)
517+
518+
```python
519+
from secure import Secure
520+
from secure.middleware import SecureASGIMiddleware
521+
from starlette.applications import Starlette
522+
from starlette.responses import JSONResponse
523+
524+
secure_headers = Secure.with_default_headers()
525+
526+
app = Starlette()
527+
app.add_middleware(SecureASGIMiddleware, secure=secure_headers)
528+
529+
530+
@app.route("/")
531+
async def read_root(request):
532+
return JSONResponse({"hello": "world"})
533+
```
534+
512535
### Flask
513536

514537
#### Recommended: `after_request` hook
@@ -631,15 +654,19 @@ This project is licensed under the terms of the [MIT License](https://opensource
631654

632655
## Contributing
633656

634-
Issues and pull requests are welcome. If you would like to discuss an idea, open an issue on GitHub so we can talk about the design before implementation.
657+
Issues and pull requests are welcome. If you’d like to discuss an idea, please open a GitHub issue so we can align on the design before implementation. See [CONTRIBUTING](https://github.com/TypeError/secure/blob/main/CONTRIBUTING.md) for details.
658+
659+
---
660+
661+
## Code of Conduct
635662

636-
Repository: <https://github.com/TypeError/secure>
663+
See [CODE_OF_CONDUCT](https://github.com/TypeError/secure/blob/main/CODE_OF_CONDUCT.md) for our Code of Conduct.
637664

638665
---
639666

640667
## Changelog
641668

642-
See the [CHANGELOG](https://github.com/TypeError/secure/blob/main/CHANGELOG.md) for a detailed list of changes by release.
669+
See [CHANGELOG](https://github.com/TypeError/secure/blob/main/CHANGELOG.md) for a detailed list of changes by release.
643670

644671
---
645672

0 commit comments

Comments
 (0)