create-caspian-app 1.0.2 → 1.0.4
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 +21 -58
- package/dist/public/js/pp-reactive-v2.js +1 -1
- package/dist/settings/build-static.py +3 -15
- package/package.json +1 -1
package/dist/main.py
CHANGED
|
@@ -26,7 +26,6 @@ from fastapi import (
|
|
|
26
26
|
)
|
|
27
27
|
from fastapi.responses import (
|
|
28
28
|
RedirectResponse,
|
|
29
|
-
FileResponse,
|
|
30
29
|
HTMLResponse,
|
|
31
30
|
JSONResponse,
|
|
32
31
|
)
|
|
@@ -68,7 +67,8 @@ from casp.runtime_security import (
|
|
|
68
67
|
client_error_message,
|
|
69
68
|
get_session_secret,
|
|
70
69
|
is_production_environment,
|
|
71
|
-
|
|
70
|
+
PublicFilesMiddleware,
|
|
71
|
+
resolve_safe_public_path,
|
|
72
72
|
)
|
|
73
73
|
from contextlib import (
|
|
74
74
|
asynccontextmanager,
|
|
@@ -347,7 +347,6 @@ REQUEST_TIMEOUT_SECONDS = max(
|
|
|
347
347
|
STREAMING_PATH_PREFIXES = ('/mcp',)
|
|
348
348
|
# Public assets: exempt from auth, request logging, and rate limiting, since one
|
|
349
349
|
# page load pulls many of them.
|
|
350
|
-
STATIC_PATH_PREFIXES = ('/css/', '/js/', '/assets/', '/favicon.ico')
|
|
351
350
|
MAX_CONTENT_LENGTH_BYTES = max(1, MAX_CONTENT_LENGTH_MB) * 1024 * 1024
|
|
352
351
|
|
|
353
352
|
|
|
@@ -410,49 +409,6 @@ SESSION_COOKIE_NAME = _scoped_cookie_name(
|
|
|
410
409
|
os.getenv('AUTH_COOKIE_NAME', 'session')
|
|
411
410
|
)
|
|
412
411
|
|
|
413
|
-
# ====
|
|
414
|
-
# Static File Routes
|
|
415
|
-
# ====
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
@app.get('/css/{filename:path}')
|
|
419
|
-
async def serve_css(filename: str):
|
|
420
|
-
return public_file_response('public/css', filename, media_type='text/css')
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
@app.get('/js/{filename:path}')
|
|
424
|
-
async def serve_js(filename: str):
|
|
425
|
-
return public_file_response(
|
|
426
|
-
'public/js',
|
|
427
|
-
filename,
|
|
428
|
-
media_type='application/javascript',
|
|
429
|
-
)
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
@app.get('/assets/{filename:path}')
|
|
433
|
-
async def serve_assets(filename: str):
|
|
434
|
-
return public_file_response('public/assets', filename)
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
@app.get('/uploads/{filename:path}')
|
|
438
|
-
async def serve_uploads(filename: str):
|
|
439
|
-
# User-supplied content served from the app's own origin: only real images
|
|
440
|
-
# render inline, everything else downloads. An uploaded .html or .svg would
|
|
441
|
-
# otherwise be stored XSS with full access to the session cookie.
|
|
442
|
-
return public_file_response(
|
|
443
|
-
'public/uploads',
|
|
444
|
-
filename,
|
|
445
|
-
inline_safe_media_types=INLINE_SAFE_UPLOAD_MEDIA_TYPES,
|
|
446
|
-
)
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
@app.get('/favicon.ico')
|
|
450
|
-
async def favicon():
|
|
451
|
-
file_path = Path('public/favicon.ico')
|
|
452
|
-
if not file_path.exists():
|
|
453
|
-
return Response(status_code=404)
|
|
454
|
-
return FileResponse(file_path, media_type='image/x-icon')
|
|
455
|
-
|
|
456
412
|
# ====
|
|
457
413
|
# Pure ASGI Middleware Classes
|
|
458
414
|
# ====
|
|
@@ -468,13 +424,6 @@ class CSRFMiddleware:
|
|
|
468
424
|
await self.app(scope, receive, send)
|
|
469
425
|
return
|
|
470
426
|
|
|
471
|
-
# Static assets carry no session and never issue RPC calls. Attaching a
|
|
472
|
-
# Set-Cookie to them makes every asset response uncacheable by shared
|
|
473
|
-
# caches and CDNs for no security benefit.
|
|
474
|
-
if scope.get("path", "").startswith(STATIC_PATH_PREFIXES):
|
|
475
|
-
await self.app(scope, receive, send)
|
|
476
|
-
return
|
|
477
|
-
|
|
478
427
|
request = Request(scope, receive, send)
|
|
479
428
|
csrf_token = request.session.get("csrf_token")
|
|
480
429
|
if not csrf_token:
|
|
@@ -588,9 +537,6 @@ class AuthMiddleware:
|
|
|
588
537
|
return
|
|
589
538
|
request = Request(scope, receive, send)
|
|
590
539
|
path = request.url.path
|
|
591
|
-
if path.startswith(STATIC_PATH_PREFIXES):
|
|
592
|
-
await self.app(scope, receive, send)
|
|
593
|
-
return
|
|
594
540
|
StateManager.init(request)
|
|
595
541
|
Auth.set_request(request)
|
|
596
542
|
auth_inst = Auth.get_instance()
|
|
@@ -699,7 +645,7 @@ class RateLimitMiddleware:
|
|
|
699
645
|
return
|
|
700
646
|
|
|
701
647
|
path = scope.get("path", "")
|
|
702
|
-
if path
|
|
648
|
+
if path == '/health':
|
|
703
649
|
await self.app(scope, receive, send)
|
|
704
650
|
return
|
|
705
651
|
|
|
@@ -735,7 +681,14 @@ class RequestDiagnosticsMiddleware:
|
|
|
735
681
|
|
|
736
682
|
method = scope.get("method", "GET")
|
|
737
683
|
path = scope.get("path", "")
|
|
738
|
-
|
|
684
|
+
is_public_file = (
|
|
685
|
+
method in {"GET", "HEAD"}
|
|
686
|
+
and resolve_safe_public_path(
|
|
687
|
+
'public',
|
|
688
|
+
path.lstrip('/'),
|
|
689
|
+
) is not None
|
|
690
|
+
)
|
|
691
|
+
should_log = not is_public_file
|
|
739
692
|
started = time.perf_counter()
|
|
740
693
|
|
|
741
694
|
if should_log and not IS_PRODUCTION:
|
|
@@ -1511,6 +1464,16 @@ app.add_middleware(BodySizeLimitMiddleware)
|
|
|
1511
1464
|
# Outermost of the security layers: reject flooding before any session
|
|
1512
1465
|
# decryption, template rendering, or database work is spent on the request.
|
|
1513
1466
|
app.add_middleware(RateLimitMiddleware)
|
|
1467
|
+
# The public directory is itself the URL contract: any existing nested file is
|
|
1468
|
+
# served without adding a directory-specific route above. Keep upload content
|
|
1469
|
+
# in attachment mode unless its MIME type is explicitly safe to render inline.
|
|
1470
|
+
app.add_middleware(
|
|
1471
|
+
PublicFilesMiddleware,
|
|
1472
|
+
directory='public',
|
|
1473
|
+
inline_safe_subdirectories={
|
|
1474
|
+
'uploads': INLINE_SAFE_UPLOAD_MEDIA_TYPES,
|
|
1475
|
+
},
|
|
1476
|
+
)
|
|
1514
1477
|
app.add_middleware(SecurityHeadersMiddleware)
|
|
1515
1478
|
|
|
1516
1479
|
if not IS_PRODUCTION:
|