fix(lagoon/attach): store non-inline uploads with Content-Disposition attachment (quick-261028-rqe)

- attach.IsInlineContentType: the shared inline raster list (JPEG, PNG,
  GIF, WebP, AVIF), parameters and case ignored, false for empty or
  unparsable values
- writeBlob stores Content-Disposition: attachment for every other type,
  so a public bucket or CDN makes the browser download HTML, SVG, PDF or
  plain text; content_type and thumbnails are unchanged
- driver tests on memblob and fileblob, Postgres-backed Store assertion,
  lagoon README and the storage and attachments pages
This commit is contained in:
Jakub Zych
2026-10-28 21:10:48 +01:00
parent 61d10436c7
commit ba8432cfb8
8 changed files with 205 additions and 8 deletions

View File

@@ -506,13 +506,31 @@ func TestStoreDiskExtension(t *testing.T) {
if err := gdb.First(&stored, f.ID).Error; err != nil {
t.Fatal(err)
}
if stored.DiskName != f.DiskName || stored.FileName != tc.fileName {
if stored.DiskName != f.DiskName || stored.FileName != tc.fileName || stored.ContentType != f.ContentType {
t.Fatalf("stored row %+v", stored)
}
got, err := bucket.ReadAll(ctx, attach.BlobKey(f.DiskName))
if err != nil || !bytes.Equal(got, tc.body) {
t.Fatalf("blob under %s: %v", attach.BlobKey(f.DiskName), err)
}
attrs, err := bucket.Attributes(ctx, attach.BlobKey(f.DiskName))
if err != nil {
t.Fatalf("blob attributes: %v", err)
}
if attrs.ContentType != f.ContentType {
t.Fatalf("blob Content-Type %q, want the stored %q", attrs.ContentType, f.ContentType)
}
// An HTML body uploaded as note.txt keeps text/html as its type
// but is stored with an attachment disposition, so a public
// bucket makes the browser download it. Only raster images stay
// inline.
wantDisposition := "attachment"
if tc.ct == "image/png" || tc.ct == "image/jpeg" {
wantDisposition = ""
}
if attrs.ContentDisposition != wantDisposition {
t.Fatalf("blob Content-Disposition %q, want %q", attrs.ContentDisposition, wantDisposition)
}
})
}
}