The route answered with the blob's stored Content-Type, which attach.Store
sniffs from the bytes, so a public text upload starting with <html> was
served as text/html on the application origin; nosniff does not help when
the declared type is already HTML. Every response now carries a sandboxing
Content-Security-Policy, and a successful response whose type is not a
raster image (JPEG, PNG, GIF, WebP, AVIF) gets Content-Disposition:
attachment, on GET and HEAD. Covers stay inline.
- TestUploadsMount: key off registers nothing; on, a public blob is served
with its content type and nosniff, HEAD has no body, private, missing,
partition and encoded traversal paths 404, an unpublished bucket or
database 404s, a custom prefix is honoured
- mountUploads refuses a plugin route at or under the prefix, so
BuildRouter fails instead of only an identical pattern failing at
Assemble (README and storage docs updated)
- TestAccessLog: pattern and status for 200, handler 404 and recovered
panic, path only for unmatched requests, locale-prefixed requests keep
the pattern, integer duration_ms, no query, header or Authorization
value, ResponseController flush still works
- surf mounts GET {storage.uploads.public_path_prefix}/{path...} on
attach.StaticHandlerPublic when storage.uploads.serve is true
- bucket and database resolved per request; X-Content-Type-Options: nosniff
- surf README, storage and configuration docs describe the key