- lagoon: Date and TimeOfDay through JSON, text and real DATE/TIME columns; Fill text fallback without changing earlier conversions; required on zero dates; deferred_bindings shape, store isolation and envelope; PurgeDeferred cut-off, after-commit blobs, SKIP LOCKED, skipped types and the deferred:purge command - attach: Store limits, extensions, MIME patterns, default lists, key shape and blob cleanup; IsAllowedImage formats, polyglots, ceiling - conga and pact: framework purge schedule entry and forged jobs; the six relation child hook interfaces - cabana: fileupload and datepicker compile, upload, remove, caption, reorder and bounds; deferred commit order, rollback, applied-only and concurrent saves; relation contracts, forms, CRUD, deferral, schema
110 lines
3.5 KiB
Go
110 lines
3.5 KiB
Go
package attach_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"hash/crc32"
|
|
"image"
|
|
"image/color"
|
|
"image/gif"
|
|
"image/jpeg"
|
|
"image/png"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/lagoon/attach"
|
|
)
|
|
|
|
// guardWebP is a 16x12 lossless WebP.
|
|
var guardWebP = []byte{
|
|
0x52, 0x49, 0x46, 0x46, 0x2a, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50, 0x56, 0x50, 0x38, 0x4c,
|
|
0x1d, 0x00, 0x00, 0x00, 0x2f, 0x0f, 0xc0, 0x02, 0x00, 0x0f, 0x70, 0x14, 0xfb, 0x53, 0xd0, 0x5e,
|
|
0x88, 0x7b, 0xfe, 0x83, 0x07, 0x62, 0xc1, 0x64, 0xfe, 0xd2, 0xbd, 0x21, 0x44, 0xf4, 0x3f, 0x74,
|
|
0x01, 0x00,
|
|
}
|
|
|
|
func guardGIF(t *testing.T) []byte {
|
|
t.Helper()
|
|
var buf bytes.Buffer
|
|
if err := gif.Encode(&buf, image.NewPaletted(image.Rect(0, 0, 3, 2), []color.Color{color.Black, color.White}), nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func guardJPEG(t *testing.T) []byte {
|
|
t.Helper()
|
|
var buf bytes.Buffer
|
|
if err := jpeg.Encode(&buf, image.NewRGBA(image.Rect(0, 0, 3, 2)), nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return buf.Bytes()
|
|
}
|
|
|
|
// pngHeader is a PNG signature and a valid IHDR chunk for w x h, the part
|
|
// image.DecodeConfig reads, without pixel data.
|
|
func pngHeader(w, h uint32) []byte {
|
|
var ihdr bytes.Buffer
|
|
ihdr.WriteString("IHDR")
|
|
_ = binary.Write(&ihdr, binary.BigEndian, w)
|
|
_ = binary.Write(&ihdr, binary.BigEndian, h)
|
|
ihdr.Write([]byte{8, 0, 0, 0, 0}) // 8-bit grayscale, no interlace
|
|
var out bytes.Buffer
|
|
out.Write([]byte("\x89PNG\r\n\x1a\n"))
|
|
_ = binary.Write(&out, binary.BigEndian, uint32(13))
|
|
out.Write(ihdr.Bytes())
|
|
_ = binary.Write(&out, binary.BigEndian, crc32.ChecksumIEEE(ihdr.Bytes()))
|
|
return out.Bytes()
|
|
}
|
|
|
|
// TestIsAllowedImageAccepts the four formats the thumbnailer decodes, and
|
|
// an image exactly at the pixel ceiling.
|
|
func TestIsAllowedImageAccepts(t *testing.T) {
|
|
var p bytes.Buffer
|
|
if err := png.Encode(&p, image.NewRGBA(image.Rect(0, 0, 2, 2))); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for name, data := range map[string][]byte{
|
|
"png": p.Bytes(),
|
|
"gif": guardGIF(t),
|
|
"jpeg": guardJPEG(t),
|
|
"webp": guardWebP,
|
|
"at the ceiling": pngHeader(4096, 4096),
|
|
"one tall column": pngHeader(1, 4096*4096),
|
|
} {
|
|
if !attach.IsAllowedImage(data) {
|
|
t.Errorf("%s refused", name)
|
|
}
|
|
}
|
|
if attach.MaxImagePixels != 4096*4096 {
|
|
t.Fatalf("MaxImagePixels = %d", attach.MaxImagePixels)
|
|
}
|
|
}
|
|
|
|
// TestIsAllowedImageRefuses SVG, HTML, a GIF signature followed by script,
|
|
// a truncated PNG, empty input, an image over 4096x4096 pixels, a zero-size
|
|
// image and a BMP (a real image the thumbnailer does not decode).
|
|
func TestIsAllowedImageRefuses(t *testing.T) {
|
|
var p bytes.Buffer
|
|
if err := png.Encode(&p, image.NewRGBA(image.Rect(0, 0, 8, 8))); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bmp := append([]byte("BM"), make([]byte, 60)...)
|
|
for name, data := range map[string][]byte{
|
|
"svg": []byte(`<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>`),
|
|
"html": []byte("<!DOCTYPE html><html><script>alert(1)</script></html>"),
|
|
"gif polyglot": []byte("GIF89a<script>alert(document.domain)</script>"),
|
|
"png polyglot": append([]byte("\x89PNG\r\n\x1a\n"), []byte("<html><script>alert(1)</script>")...),
|
|
"truncated png": p.Bytes()[:20],
|
|
"empty": nil,
|
|
"over the ceiling": pngHeader(4097, 4096),
|
|
"huge": pngHeader(100000, 100000),
|
|
"zero width": pngHeader(0, 10),
|
|
"bmp": bmp,
|
|
"text": []byte("just text"),
|
|
} {
|
|
if attach.IsAllowedImage(data) {
|
|
t.Errorf("%s accepted", name)
|
|
}
|
|
}
|
|
}
|