chore: import clean Agent Desk snapshot (3/7)

This commit is contained in:
2026-10-16 11:58:36 +00:00
parent 89c5231c4c
commit fe4a99cf4b
127 changed files with 29641 additions and 0 deletions

View File

@@ -0,0 +1,268 @@
package storage
import (
"bytes"
"io"
"net/http"
"strings"
"testing"
"unicode/utf8"
)
var pngSignature = []byte("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR")
func TestValidateUploadBlocksBrowserActiveExtensions(t *testing.T) {
names := []string{
"session-stealer.html", "page.HTM", "vector.svg", "sheet.xml",
"transform.xsl", "bundle.min.js", "app.mjs", "shell.php",
"report.asp", "index.jsp", "legacy.swf", "widget.htc",
}
for _, name := range names {
if _, err := ValidateUpload(name, "application/octet-stream", "application/octet-stream"); err == nil {
t.Errorf("ValidateUpload(%q) accepted a browser-active extension", name)
}
}
}
func TestValidateUploadBlocksStoredXSSPayload(t *testing.T) {
payload := []byte(`<html><body><script>fetch("/api/dashboard/user/current")</script></body></html>`)
sniffed, err := SniffContentType(bytes.NewReader(payload))
if err != nil {
t.Fatalf("SniffContentType() error = %v", err)
}
if !IsBlockedMediaType(sniffed) {
t.Fatalf("expected the payload to sniff as a blocked media type, got %q", sniffed)
}
if _, err := ValidateUpload("proof.html", "text/html", sniffed); err == nil {
t.Error("expected an honestly named HTML upload to be rejected")
}
// Renaming the payload is the actual attack: the extension looks like an
// image and the declared type agrees, so only the bytes can catch it.
if _, err := ValidateUpload("profile.png", "image/png", sniffed); err == nil {
t.Error("expected an HTML payload disguised as a PNG to be rejected")
}
}
func TestValidateUploadBlocksSVGRegardlessOfDeclaredType(t *testing.T) {
payload := []byte(`<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>`)
sniffed, err := SniffContentType(bytes.NewReader(payload))
if err != nil {
t.Fatalf("SniffContentType() error = %v", err)
}
if _, err := ValidateUpload("logo.svg", "image/svg+xml", sniffed); err == nil {
t.Error("expected an SVG upload to be rejected")
}
// An SVG that arrives without the .svg extension is still refused, because
// the declared type alone is enough to identify it as an active document.
if _, err := ValidateUpload("logo.png", "image/svg+xml", sniffed); err == nil {
t.Error("expected an SVG declared as an image to be rejected")
}
}
func TestValidateUploadAcceptsOrdinarySupportFiles(t *testing.T) {
cases := []struct {
filename string
declared string
sniffed string
wantMime string
}{
{"screenshot.png", "image/png", http.DetectContentType(pngSignature), "image/png"},
// The client declared a generic type; the payload identified itself.
{"photo.jpg", "application/octet-stream", "image/jpeg", "image/jpeg"},
// net/http cannot recognise HEIC, so the declared type has to stand in.
{"IMG_0001.heic", "image/heic", "application/octet-stream", "image/heic"},
{"contract.pdf", "application/pdf", "application/pdf", "application/pdf"},
{"export.csv", "text/csv", "text/plain; charset=utf-8", "text/csv"},
{"notes.txt", "text/plain", "text/plain; charset=utf-8", "text/plain"},
{"logs.zip", "application/zip", "application/zip", "application/zip"},
{"build.apk", "application/vnd.android.package-archive", "application/octet-stream", "application/vnd.android.package-archive"},
{"data.json", "application/json", "application/json", "application/json"},
}
for _, tc := range cases {
got, err := ValidateUpload(tc.filename, tc.declared, tc.sniffed)
if err != nil {
t.Errorf("ValidateUpload(%q) error = %v", tc.filename, err)
continue
}
if got != tc.wantMime {
t.Errorf("ValidateUpload(%q) mime = %q want %q", tc.filename, got, tc.wantMime)
}
}
}
func TestSanitizeFilename(t *testing.T) {
cases := []struct {
in string
want string
}{
{"report.pdf", "report.pdf"},
{"../../etc/passwd", "passwd"},
{`C:\Users\victim\Desktop\evil.html`, "evil.html"},
{"/absolute/path/notes.txt", "notes.txt"},
{"tab\tand\nnewline.log", "tabandnewline.log"},
{"....", ""},
{"", ""},
{" spaced name.pdf ", "spaced name.pdf"},
}
for _, tc := range cases {
if got := SanitizeFilename(tc.in); got != tc.want {
t.Errorf("SanitizeFilename(%q) = %q want %q", tc.in, got, tc.want)
}
}
long := strings.Repeat("a", 300) + ".pdf"
got := SanitizeFilename(long)
if len(got) > 255 {
t.Errorf("SanitizeFilename() len = %d want <= 255", len(got))
}
if !strings.HasSuffix(got, ".pdf") {
t.Errorf("SanitizeFilename() = %q, expected the extension to survive truncation", got)
}
// A multibyte stem must not be cut in the middle of a rune.
wide := strings.Repeat("附件", 100) + ".pdf"
got = SanitizeFilename(wide)
if len(got) > 255 {
t.Errorf("SanitizeFilename() len = %d want <= 255", len(got))
}
if !utf8.ValidString(got) {
t.Errorf("SanitizeFilename() = %q is not valid UTF-8", got)
}
if !strings.HasSuffix(got, ".pdf") {
t.Errorf("SanitizeFilename() = %q, expected the extension to survive truncation", got)
}
// An extension longer than the whole budget cannot be preserved.
got = SanitizeFilename("a." + strings.Repeat("b", 300))
if len(got) > 255 {
t.Errorf("SanitizeFilename() len = %d want <= 255", len(got))
}
if !utf8.ValidString(got) {
t.Errorf("SanitizeFilename() = %q is not valid UTF-8", got)
}
// A name made entirely of dots leaves nothing worth keeping.
if got = SanitizeFilename(strings.Repeat(".", 300)); got != "" {
t.Errorf("SanitizeFilename() of a dot-only name = %q want empty", got)
}
}
func TestSniffContentTypeRewindsTheReader(t *testing.T) {
src := bytes.NewReader(pngSignature)
got, err := SniffContentType(src)
if err != nil {
t.Fatalf("SniffContentType() error = %v", err)
}
if got != "image/png" {
t.Fatalf("SniffContentType() = %q want image/png", got)
}
// The caller still streams the whole payload to storage afterwards, so the
// reader must be back at the start.
rest, err := io.ReadAll(src)
if err != nil {
t.Fatalf("ReadAll() error = %v", err)
}
if !bytes.Equal(rest, pngSignature) {
t.Fatalf("reader was not rewound: got %d bytes want %d", len(rest), len(pngSignature))
}
}
func TestSniffContentTypeHandlesShortAndEmptyPayloads(t *testing.T) {
short := bytes.NewReader([]byte("hi"))
got, err := SniffContentType(short)
if err != nil {
t.Fatalf("SniffContentType() error = %v", err)
}
if !strings.HasPrefix(got, "text/plain") {
t.Fatalf("SniffContentType() = %q want a text/plain result", got)
}
rest, err := io.ReadAll(short)
if err != nil {
t.Fatalf("ReadAll() error = %v", err)
}
if string(rest) != "hi" {
t.Fatalf("reader was not rewound: got %q", rest)
}
empty := bytes.NewReader(nil)
if _, err := SniffContentType(empty); err != nil {
t.Fatalf("SniffContentType() on an empty payload error = %v", err)
}
}
func TestGenerateStorageKeyNeverUsesBlockedExtension(t *testing.T) {
cases := []struct {
info UploadInfo
want string
}{
{UploadInfo{Filename: "evil.html", MimeType: "text/html"}, ".bin"},
{UploadInfo{Filename: "evil.svg", MimeType: "image/svg+xml"}, ".bin"},
// The extension is gone but the MIME type still names an active
// document, so the derived extension must be refused too.
{UploadInfo{Filename: "noext", MimeType: "text/html"}, ".bin"},
{UploadInfo{Filename: "notes.txt", MimeType: "text/plain"}, ".txt"},
{UploadInfo{Filename: "photo.png", MimeType: "image/png"}, ".png"},
{UploadInfo{Filename: "unknown", MimeType: "application/octet-stream"}, ".bin"},
}
for _, tc := range cases {
_, key := GenerateStorageKey(tc.info)
if !strings.HasSuffix(key, tc.want) {
t.Errorf("GenerateStorageKey(%+v) = %q, expected it to end in %q", tc.info, key, tc.want)
}
if strings.Contains(key, "..") {
t.Errorf("GenerateStorageKey(%+v) = %q contains a traversal segment", tc.info, key)
}
}
}
func TestGetExtByMimeTypeIsPlatformIndependent(t *testing.T) {
// mime.ExtensionsByType reads the Windows registry, where text/html resolved
// to ".ehtml" and slipped past the block list. The mapping has to come from
// our own table so a storage key is the same on every platform, and so a
// media type that describes an active document maps to nothing at all.
cases := map[string]string{
"text/html": "",
"application/xhtml+xml": "",
"image/svg+xml": "",
"text/xml": "",
"application/xml": "",
"application/javascript": "",
"text/javascript": "",
"image/jpeg": ".jpg",
"image/jfif": ".jpg",
"image/pjpeg": ".jpg",
"image/png": ".png",
"application/pdf": ".pdf",
"text/csv": ".csv",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
}
for mediaType, want := range cases {
if got := getExtByMimeType(mediaType); got != want {
t.Errorf("getExtByMimeType(%q) = %q want %q", mediaType, got, want)
}
}
for mediaType := range blockedMediaTypes {
if got := getExtByMimeType(mediaType); got != "" {
t.Errorf("getExtByMimeType(%q) = %q, a blocked media type must not yield an extension", mediaType, got)
}
}
}
func TestIsPreviewableExtension(t *testing.T) {
inline := []string{".png", ".JPG", ".jpeg", ".gif", ".webp", ".avif", ".mp4", ".webm", ".mp3", ".pdf"}
for _, ext := range inline {
if !IsPreviewableExtension(ext) {
t.Errorf("IsPreviewableExtension(%q) = false want true", ext)
}
}
download := []string{".html", ".svg", ".xml", ".js", ".zip", ".txt", ".csv", ".docx", ".apk", ""}
for _, ext := range download {
if IsPreviewableExtension(ext) {
t.Errorf("IsPreviewableExtension(%q) = true want false", ext)
}
}
}