package assets import ( "bytes" "fmt" "io" "io/fs" "os" "strings" "testing" "github.com/andybalholm/brotli" ) func TestParseManifestRejectsBadInputs(t *testing.T) { t.Parallel() good := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" pin := func(digest string, size int) string { return fmt.Sprintf(`"sha256":%q,"size":%d`, digest, size) } entry := func(css, wasm, js string) string { return `{"version":1,"files":{"systemviews.css":{` + css + `},"systemviews.wasm":{` + wasm + `},"wasm_exec.js":{` + js + `}}}` } valid := entry(pin(good, 1), pin(good, 2), pin(good, 3)) if _, err := ParseManifest([]byte(valid)); err != nil { t.Fatalf("ParseManifest(valid) error = %v", err) } cases := map[string]string{ "bad version": strings.Replace(valid, `"version":1`, `"version":2`, 1), "missing pin": `{"version":1,"files":{"systemviews.css":{` + pin(good, 1) + `},"systemviews.wasm":{` + pin(good, 2) + `}}}`, "unknown file": strings.TrimSuffix(valid, `}`) + `,"evil.js":{` + pin(good, 4) + `}}`, "empty digest": entry(pin("", 1), pin(good, 2), pin(good, 3)), "short digest": entry(pin("abc", 1), pin(good, 2), pin(good, 3)), "long digest": entry(pin(good+"00", 1), pin(good, 2), pin(good, 3)), "nonhex digest": entry(pin(strings.Repeat("g", 64), 1), pin(good, 2), pin(good, 3)), "upper digest": entry(pin(strings.Repeat("A", 64), 1), pin(good, 2), pin(good, 3)), // Pins breaking out of the shell's quoted attributes must fail // closed instead of reaching HTML/JS interpolation. "quote breakout": entry(pin(good, 1), pin("');alert(1);//"+strings.Repeat("0", 52), 2), pin(good, 3)), "markup pin": entry(pin(good, 1), pin("