Serve artifacts of a type that can run script with CSP sandbox and nosniff
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user