Migration This is my new public git source viewer. Source coming soon. Please contact with concerns/bugs. Contact me

internal/modules/image_test.go

main · 3.7 KB · 99 lines raw

1 package modules
2
3 import "testing"
4
5 // A file only earns an inline image MIME when its extension and its magic
6 // bytes agree and the format is a raster one. The cases that must return ""
7 // are the attacker-influenced ones: content that is not an image at all, and
8 // SVG, which is an active document rather than a picture.
9 func TestImageContentType(t *testing.T) {
10 png := []byte("\x89PNG\r\n\x1a\n" + "rest of the file")
11 tests := []struct {
12 name string
13 path string
14 data []byte
15 want string
16 }{
17 {"png", "logo.png", png, "image/png"},
18 {"uppercase extension", "LOGO.PNG", png, "image/png"},
19 {"jpeg", "a.jpg", []byte{0xff, 0xd8, 0xff, 0xe0}, "image/jpeg"},
20 {"jpeg long extension", "a.jpeg", []byte{0xff, 0xd8, 0xff, 0xe0}, "image/jpeg"},
21 {"gif87", "a.gif", []byte("GIF87a...."), "image/gif"},
22 {"gif89", "a.gif", []byte("GIF89a...."), "image/gif"},
23 {"webp", "a.webp", []byte("RIFF\x00\x00\x00\x00WEBPVP8 "), "image/webp"},
24 {"bmp", "a.bmp", []byte("BM...."), "image/bmp"},
25 {"ico", "a.ico", []byte{0x00, 0x00, 0x01, 0x00}, "image/x-icon"},
26 {"avif", "a.avif", []byte("\x00\x00\x00\x18ftypavif"), "image/avif"},
27
28 // HTML wearing a .png name, and a real PNG named .txt, must both
29 // fall back to the download path.
30 {"html named png", "evil.png", []byte("<html><script>x</script>"), ""},
31 {"png extension without magic", "evil.png", []byte("not an image"), ""},
32 {"magic without extension", "evil.txt", png, ""},
33 {"truncated png", "evil.png", []byte("\x89PNG"), ""},
34
35 // SVG can carry <script> and would be stored XSS if served as a
36 // document on this origin, so it is never recognised here.
37 {"svg", "evil.svg", []byte(`<svg xmlns="http://www.w3.org/2000/svg"><script/></svg>`), ""},
38 {"no extension", "Makefile", []byte("all:\n"), ""},
39 }
40 for _, tc := range tests {
41 if got := imageContentType(tc.path, tc.data); got != tc.want {
42 t.Errorf("%s: imageContentType(%q) = %q, want %q", tc.name, tc.path, got, tc.want)
43 }
44 }
45 }
46
47 // The blob view only renders a preview for a validated raster image, and
48 // only while it is under the size cap; both facts are what tell the view
49 // whether to show an <img>, a download notice, or numbered bytes.
50 func TestImageBlobPreview(t *testing.T) {
51 png := append([]byte("\x89PNG\r\n\x1a\n"), make([]byte, 64)...)
52 big := append([]byte("\x89PNG\r\n\x1a\n"), make([]byte, maxPreviewSize)...)
53 c := blobCtx(t, map[string]string{
54 "logo.png": string(png),
55 "big.png": string(big),
56 "evil.png": "<html>not an image</html>",
57 })
58
59 b := blobAt(t, c, "logo.png")
60 if b.Image != "image/png" || !b.Preview() || !b.Binary {
61 t.Errorf("small image: Image=%q Preview=%v Binary=%v", b.Image, b.Preview(), b.Binary)
62 }
63 if b.PreviewLimit() != maxPreviewSize {
64 t.Errorf("PreviewLimit = %d, want %d", b.PreviewLimit(), maxPreviewSize)
65 }
66
67 bigBlob := blobAt(t, c, "big.png")
68 if bigBlob.Image != "image/png" {
69 t.Errorf("large image: Image = %q, want image/png", bigBlob.Image)
70 }
71 if bigBlob.Preview() {
72 t.Error("large image was previewed past the size cap")
73 }
74
75 // Not an image, so it keeps the plain binary path rather than a preview.
76 evil := blobAt(t, c, "evil.png")
77 if evil.Image != "" || evil.Preview() {
78 t.Errorf("misnamed html: Image=%q Preview=%v, want none", evil.Image, evil.Preview())
79 }
80 }
81
82 // /raw/ must declare the image MIME so the preview and direct navigation
83 // render a picture, and must expose it through the interface core checks.
84 func TestRawBlobInlineImage(t *testing.T) {
85 png := []byte("\x89PNG\r\n\x1a\n" + "body")
86 c := blobCtx(t, map[string]string{"logo.png": string(png)})
87 c.Path = "logo.png"
88 v, err := blobRawMod(c)
89 if err != nil {
90 t.Fatal(err)
91 }
92 r, ok := v.(RawBlob)
93 if !ok {
94 t.Fatalf("got %T", v)
95 }
96 if r.InlineContentType() != "image/png" {
97 t.Errorf("InlineContentType = %q, want image/png", r.InlineContentType())
98 }
99 }