Skip to content

Commit a116d26

Browse files
committed
tests and SVG handling
1 parent 13d950e commit a116d26

2 files changed

Lines changed: 115 additions & 1 deletion

File tree

internal/ui/model.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3466,7 +3466,7 @@ func (m Model) openWebVersion() (tea.Model, tea.Cmd) {
34663466
// If magic-byte detection returns something outside the expected prefix, the file is suspicious.
34673467
var expectedMimePrefix = map[string]string{
34683468
".png": "image/", ".jpg": "image/", ".jpeg": "image/", ".gif": "image/",
3469-
".webp": "image/", ".svg": "image/", ".bmp": "image/", ".ico": "image/",
3469+
".webp": "image/", ".bmp": "image/", ".ico": "image/",
34703470
".pdf": "application/pdf",
34713471
".zip": "application/zip", ".gz": "application/",
34723472
".doc": "application/", ".docx": "application/", ".xls": "application/", ".xlsx": "application/",

internal/ui/model_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ui
22

33
import (
4+
"net/http"
45
"reflect"
56
"strings"
67
"testing"
@@ -617,3 +618,116 @@ func TestReplyAllExcludesAllOwnAddresses(t *testing.T) {
617618
})
618619
}
619620
}
621+
622+
func TestIsMimeMismatch(t *testing.T) {
623+
tests := []struct {
624+
name string
625+
ext string
626+
detected string
627+
want bool
628+
}{
629+
// Disguised files — should be flagged
630+
{"sh disguised as png", ".png", "text/plain; charset=utf-8", true},
631+
{"html disguised as jpg", ".jpg", "text/html; charset=utf-8", true},
632+
{"elf binary as pdf", ".pdf", "application/octet-stream", true},
633+
{"script as gif", ".gif", "text/plain; charset=utf-8", true},
634+
635+
// Legitimate files — should pass
636+
{"real png", ".png", "image/png", false},
637+
{"real jpg", ".jpg", "image/jpeg", false},
638+
{"real gif", ".gif", "image/gif", false},
639+
{"real pdf", ".pdf", "application/pdf", false},
640+
{"real zip", ".zip", "application/zip", false},
641+
{"real mp3", ".mp3", "audio/mpeg", false},
642+
{"real mp4", ".mp4", "video/mp4", false},
643+
644+
// Unknown extensions — can't validate, should pass through
645+
{"unknown ext .xyz", ".xyz", "text/plain; charset=utf-8", false},
646+
{"unknown ext .foo", ".foo", "application/octet-stream", false},
647+
}
648+
for _, tt := range tests {
649+
t.Run(tt.name, func(t *testing.T) {
650+
got := isMimeMismatch(tt.ext, tt.detected)
651+
if got != tt.want {
652+
t.Errorf("isMimeMismatch(%q, %q) = %v, want %v", tt.ext, tt.detected, got, tt.want)
653+
}
654+
})
655+
}
656+
}
657+
658+
func TestDangerousExts(t *testing.T) {
659+
// Verify known dangerous extensions are in the blocklist.
660+
dangerous := []string{".sh", ".exe", ".desktop", ".bat", ".py", ".jar", ".ps1"}
661+
for _, ext := range dangerous {
662+
if !dangerousExts[ext] {
663+
t.Errorf("expected %q in dangerousExts", ext)
664+
}
665+
}
666+
// Verify safe extensions are NOT in the blocklist.
667+
safe := []string{".png", ".jpg", ".pdf", ".txt", ".html", ".md"}
668+
for _, ext := range safe {
669+
if dangerousExts[ext] {
670+
t.Errorf("unexpected %q in dangerousExts", ext)
671+
}
672+
}
673+
}
674+
675+
func TestMimeMismatchWithRealBytes(t *testing.T) {
676+
// Simulate real magic-byte detection scenarios using net/http.DetectContentType.
677+
tests := []struct {
678+
name string
679+
ext string
680+
data []byte // fake file content
681+
wantBad bool
682+
}{
683+
{
684+
name: "bash script disguised as .png",
685+
ext: ".png",
686+
data: []byte("#!/bin/bash\necho hello\n"),
687+
wantBad: true,
688+
},
689+
{
690+
name: "python script disguised as .jpg",
691+
ext: ".jpg",
692+
data: []byte("#!/usr/bin/env python3\nprint('hello')\n"),
693+
wantBad: true,
694+
},
695+
{
696+
name: "html with script disguised as .pdf",
697+
ext: ".pdf",
698+
data: []byte("<html><body>harmless content</body></html>"),
699+
wantBad: true,
700+
},
701+
{
702+
name: "real PNG file (magic bytes)",
703+
ext: ".png",
704+
// PNG magic: 89 50 4E 47 0D 0A 1A 0A
705+
data: []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00},
706+
wantBad: false,
707+
},
708+
{
709+
name: "real PDF file (magic bytes)",
710+
ext: ".pdf",
711+
// PDF magic: %PDF-
712+
data: []byte("%PDF-1.4 fake pdf content here"),
713+
wantBad: false,
714+
},
715+
{
716+
name: "real GIF file (magic bytes)",
717+
ext: ".gif",
718+
// GIF magic: GIF89a
719+
data: []byte("GIF89a" + strings.Repeat("\x00", 100)),
720+
wantBad: false,
721+
},
722+
}
723+
for _, tt := range tests {
724+
t.Run(tt.name, func(t *testing.T) {
725+
detected := http.DetectContentType(tt.data)
726+
got := isMimeMismatch(tt.ext, detected)
727+
if got != tt.wantBad {
728+
t.Errorf("isMimeMismatch(%q, %q) = %v, want %v (data: %q...)",
729+
tt.ext, detected, got, tt.wantBad, string(tt.data[:min(20, len(tt.data))]))
730+
}
731+
})
732+
}
733+
}

0 commit comments

Comments
 (0)