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:
116
modules/lagoon/attach/inline_test.go
Normal file
116
modules/lagoon/attach/inline_test.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package attach
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gocloud.dev/blob"
|
||||
"gocloud.dev/blob/fileblob"
|
||||
"gocloud.dev/blob/memblob"
|
||||
)
|
||||
|
||||
func TestIsInlineContentType(t *testing.T) {
|
||||
cases := []struct {
|
||||
in string
|
||||
want bool
|
||||
}{
|
||||
{"image/jpeg", true},
|
||||
{"image/png", true},
|
||||
{"image/gif", true},
|
||||
{"image/webp", true},
|
||||
{"image/avif", true},
|
||||
{"IMAGE/WEBP; charset=binary", true},
|
||||
{"text/html; charset=utf-8", false},
|
||||
{"image/svg+xml", false},
|
||||
{"application/pdf", false},
|
||||
{"text/plain", false},
|
||||
{"application/octet-stream", false},
|
||||
{"application/zip", false},
|
||||
{"image/bmp", false},
|
||||
{"image/x-icon", false},
|
||||
{"video/mp4", false},
|
||||
{"audio/mpeg", false},
|
||||
{"", false},
|
||||
{"not a media type;;", false},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
if got := IsInlineContentType(tc.in); got != tc.want {
|
||||
t.Errorf("IsInlineContentType(%q) = %v, want %v", tc.in, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestWriteBlobDisposition is the driver half of the public-bucket fix: a
|
||||
// public bucket serves the headers Store stores with the object, so the
|
||||
// attachment disposition must survive the write on the real drivers. The
|
||||
// metadata-skip fileblob bucket shows that a driver which does not keep
|
||||
// attributes still accepts the option without an error.
|
||||
func TestWriteBlobDisposition(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, ct, disposition string
|
||||
}{
|
||||
{"html", "text/html", "attachment"},
|
||||
{"text", "text/plain", "attachment"},
|
||||
{"pdf", "application/pdf", "attachment"},
|
||||
{"svg", "image/svg+xml", "attachment"},
|
||||
{"zip", "application/zip", "attachment"},
|
||||
{"png", "image/png", ""},
|
||||
{"jpeg", "image/jpeg", ""},
|
||||
{"avif", "image/avif", ""},
|
||||
}
|
||||
sidecar, err := fileblob.OpenBucket(t.TempDir(), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
noMeta, err := fileblob.OpenBucket(t.TempDir(), &fileblob.Options{Metadata: fileblob.MetadataDontWrite})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
buckets := []struct {
|
||||
name string
|
||||
bucket *blob.Bucket
|
||||
keepsAttrs bool
|
||||
}{
|
||||
{"memblob", memblob.OpenBucket(nil), true},
|
||||
{"fileblob sidecar", sidecar, true},
|
||||
{"fileblob metadata skipped", noMeta, false},
|
||||
}
|
||||
const body = "<html><script>alert(1)</script></html>"
|
||||
for _, b := range buckets {
|
||||
bucket := b.bucket
|
||||
t.Cleanup(func() { _ = bucket.Close() })
|
||||
t.Run(b.name, func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
for _, tc := range cases {
|
||||
key := "abc/def/012/" + tc.name + "-file.bin"
|
||||
n, err := writeBlob(ctx, bucket, key, strings.NewReader(body), tc.ct, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: writeBlob: %v", tc.name, err)
|
||||
}
|
||||
if n != int64(len(body)) {
|
||||
t.Fatalf("%s: wrote %d bytes, want %d", tc.name, n, len(body))
|
||||
}
|
||||
got, err := bucket.ReadAll(ctx, key)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: ReadAll: %v", tc.name, err)
|
||||
}
|
||||
if string(got) != body {
|
||||
t.Fatalf("%s: body = %q, want %q", tc.name, got, body)
|
||||
}
|
||||
if !b.keepsAttrs {
|
||||
continue
|
||||
}
|
||||
attrs, err := bucket.Attributes(ctx, key)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: Attributes: %v", tc.name, err)
|
||||
}
|
||||
if attrs.ContentType != tc.ct {
|
||||
t.Errorf("%s: ContentType = %q, want %q", tc.name, attrs.ContentType, tc.ct)
|
||||
}
|
||||
if attrs.ContentDisposition != tc.disposition {
|
||||
t.Errorf("%s: ContentDisposition = %q, want %q", tc.name, attrs.ContentDisposition, tc.disposition)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user