Serve artifacts of a type that can run script with CSP sandbox and nosniff

This commit is contained in:
2026-10-07 08:43:29 +02:00
parent 953a983919
commit 094fed7cea
2 changed files with 42 additions and 8 deletions
+16 -8
View File
@@ -27,6 +27,11 @@ from fluksio.flow.artifacts import ArtifactStore
#: trusting one — anything else is served as bytes.
_MEDIA_TYPE = re.compile(r"^[\w.+-]+/[\w.+-]+$")
#: What a browser shows without running anything, so it is served as it is.
#: Every other type is served sandboxed: HTML, SVG and XML run script when
#: opened as a page, and so may a type nobody listed here.
_INERT = re.compile(r"^(audio|video)/|^image/(?!svg)|^application/(pdf|json)$")
def artifact_caller(request: Request) -> str:
"""Who may move artifacts: a signed-in person, or an attached worker.
@@ -137,11 +142,14 @@ def get_artifact(
path = store.path(digest)
if path is None:
raise HTTPException(status_code=404, detail="No such artifact")
return FileResponse(
path,
media_type=(
media_type if _MEDIA_TYPE.match(media_type) else "application/octet-stream"
),
# The digest is the content, so it is also the perfect validator.
headers={"ETag": f'"{digest}"'},
)
if not _MEDIA_TYPE.match(media_type):
media_type = "application/octet-stream"
# The digest is the content, so it is also the perfect validator.
headers = {"ETag": f'"{digest}"'}
if not _INERT.match(media_type.lower()):
# The caller names the type and any member can upload, so an artifact
# asked for as text/html would otherwise run its script on this origin,
# beside the session, for whoever follows a link to it.
headers["Content-Security-Policy"] = "sandbox"
headers["X-Content-Type-Options"] = "nosniff"
return FileResponse(path, media_type=media_type, headers=headers)