create-caspian-app 1.3.19 → 1.3.21
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.
package/dist/main.py
CHANGED
|
@@ -27,6 +27,7 @@ from fastapi.responses import (
|
|
|
27
27
|
RedirectResponse,
|
|
28
28
|
HTMLResponse,
|
|
29
29
|
JSONResponse,
|
|
30
|
+
PlainTextResponse,
|
|
30
31
|
)
|
|
31
32
|
from starlette.datastructures import MutableHeaders
|
|
32
33
|
from starlette.middleware import Middleware
|
|
@@ -472,6 +473,54 @@ class SecurityHeadersMiddleware:
|
|
|
472
473
|
await self.app(scope, receive, send_wrapper)
|
|
473
474
|
|
|
474
475
|
|
|
476
|
+
# Top-level directories under `public/` are asset namespaces, not page routes:
|
|
477
|
+
# `public/js/**` owns `/js/**` and nothing in `src/app/` can answer there. Built
|
|
478
|
+
# once, like SECURITY_HEADERS -- adding a new asset *directory* is a restart-level
|
|
479
|
+
# change, unlike adding a file to an existing one, which stays live.
|
|
480
|
+
PUBLIC_ASSET_NAMESPACES: frozenset[str] = (
|
|
481
|
+
frozenset(entry.name.casefold() for entry in Path("public").iterdir() if entry.is_dir())
|
|
482
|
+
if Path("public").is_dir()
|
|
483
|
+
else frozenset()
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
class MissingPublicAssetMiddleware:
|
|
488
|
+
"""404 a missing file in a public asset namespace instead of falling through.
|
|
489
|
+
|
|
490
|
+
`PublicFilesMiddleware` deliberately falls through when no file matches, so
|
|
491
|
+
normal routing keeps working. With `is_all_routes_private=True` that means a
|
|
492
|
+
missing asset reaches `AuthMiddleware` and answers `303 -> /signin`, which is
|
|
493
|
+
the wrong answer twice over: a `<script src="/js/typo.js">` then receives the
|
|
494
|
+
sign-in *page* as `200 text/html` and fails with a parse error that names the
|
|
495
|
+
wrong file, and every bogus asset path returns a full HTML page to anonymous
|
|
496
|
+
traffic. A path whose first segment is a real `public/` directory can only be
|
|
497
|
+
an asset request, so a miss there is a genuine 404.
|
|
498
|
+
|
|
499
|
+
Runs inside the rate limiter -- a 404 flood is still a flood -- but outside
|
|
500
|
+
sessions, CSRF, and auth, so a missing asset costs no session decryption.
|
|
501
|
+
"""
|
|
502
|
+
|
|
503
|
+
def __init__(self, app: ASGIApp):
|
|
504
|
+
self.app = app
|
|
505
|
+
|
|
506
|
+
async def __call__(self, scope: Scope, receive: Receive, send: Send):
|
|
507
|
+
if scope["type"] != "http" or scope.get("method", "GET").upper() not in {
|
|
508
|
+
"GET",
|
|
509
|
+
"HEAD",
|
|
510
|
+
}:
|
|
511
|
+
await self.app(scope, receive, send)
|
|
512
|
+
return
|
|
513
|
+
|
|
514
|
+
segment = str(scope.get("path", "")).lstrip("/").split("/", 1)[0].casefold()
|
|
515
|
+
if segment not in PUBLIC_ASSET_NAMESPACES:
|
|
516
|
+
await self.app(scope, receive, send)
|
|
517
|
+
return
|
|
518
|
+
|
|
519
|
+
# PublicFilesMiddleware sits outside this one, so reaching here means it
|
|
520
|
+
# already declined: the file does not exist or escapes the public root.
|
|
521
|
+
await PlainTextResponse("Not Found", status_code=404)(scope, receive, send)
|
|
522
|
+
|
|
523
|
+
|
|
475
524
|
class BodySizeLimitMiddleware:
|
|
476
525
|
"""Reject oversized HTTP request bodies before route or RPC parsing."""
|
|
477
526
|
|
|
@@ -1534,6 +1583,9 @@ app.add_middleware(
|
|
|
1534
1583
|
path="/",
|
|
1535
1584
|
)
|
|
1536
1585
|
app.add_middleware(BodySizeLimitMiddleware)
|
|
1586
|
+
# Sits between the limiter and the session/auth layers: a miss under a public
|
|
1587
|
+
# asset namespace is a 404, not a sign-in redirect, and costs no session work.
|
|
1588
|
+
app.add_middleware(MissingPublicAssetMiddleware)
|
|
1537
1589
|
# Outermost of the security layers: reject flooding before any session
|
|
1538
1590
|
# decryption, template rendering, or database work is spent on the request.
|
|
1539
1591
|
app.add_middleware(RateLimitMiddleware)
|