Clean up wrong vanity rules
This commit is contained in:
@@ -17,6 +17,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
"time"
|
||||
|
||||
"uncensored-send/internal/auth"
|
||||
@@ -414,17 +415,46 @@ func TestPathTraversalIsRejected(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReservedNamesAreRejected(t *testing.T) {
|
||||
// A vanity name has only the spelling rules to satisfy. Names that look like
|
||||
// routes or like the site's own assets are ordinary names, because an id is
|
||||
// reachable only under /d/ and /i/ and never collides with anything of ours.
|
||||
func TestVanityNamesThatLookLikeRoutesAreOrdinary(t *testing.T) {
|
||||
h := newHarness(t, nil)
|
||||
for _, name := range []string{"api", "static", "d", "i", "upload", "robots.txt"} {
|
||||
|
||||
for _, name := range []string{"favicon.png", "admin", "login", "upload", "static", "robots.txt", "tokens.json"} {
|
||||
resp := h.formUpload(t, map[string]string{"vanity": name, "token": h.token}, "f.txt", "body of "+name)
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
t.Errorf("vanity %q => %s: %s", name, resp.Status, strings.TrimSpace(string(body)))
|
||||
continue
|
||||
}
|
||||
// And it is genuinely reachable at the name that was asked for.
|
||||
got := h.get(t, "/d/"+name, "")
|
||||
content, _ := io.ReadAll(got.Body)
|
||||
got.Body.Close()
|
||||
if got.StatusCode != http.StatusOK {
|
||||
t.Errorf("GET /d/%s => %s, want 200", name, got.Status)
|
||||
}
|
||||
if string(content) != "body of "+name {
|
||||
t.Errorf("GET /d/%s served %q, not the file that was uploaded", name, content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The spelling rules themselves still stand: they are what keeps a name from
|
||||
// becoming a path element it should not be.
|
||||
func TestMalformedVanityNamesAreRefused(t *testing.T) {
|
||||
h := newHarness(t, nil)
|
||||
for _, name := range []string{"d", "i", "no spaces allowed", "-leading-dash", "..", "trailing.", "a/b"} {
|
||||
resp := h.upload(t, []byte("x"), map[string]string{
|
||||
"Vanity": name,
|
||||
"Authorization": "Bearer " + h.token,
|
||||
})
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusBadRequest {
|
||||
t.Errorf("vanity %q => %s, want 400", name, resp.Status)
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -942,6 +972,54 @@ func (h *harness) formUploadWith(t *testing.T, cookie *http.Cookie, fields map[s
|
||||
return resp
|
||||
}
|
||||
|
||||
// --- favicon --------------------------------------------------------------
|
||||
|
||||
// No icon is committed, so the selection logic is exercised against stand-in
|
||||
// filesystems: a build that has one is a build nobody can write a test for.
|
||||
func TestFaviconSelection(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
files []string
|
||||
want string
|
||||
}{
|
||||
{"nothing shipped", nil, ""},
|
||||
{"a png", []string{"favicon.png"}, "favicon.png"},
|
||||
{"an ico", []string{"favicon.ico"}, "favicon.ico"},
|
||||
{"both, png wins", []string{"favicon.ico", "favicon.png"}, "favicon.png"},
|
||||
{"something else entirely", []string{"logo.png"}, ""},
|
||||
} {
|
||||
fsys := fstest.MapFS{}
|
||||
for _, f := range c.files {
|
||||
fsys[f] = &fstest.MapFile{Data: []byte("x")}
|
||||
}
|
||||
if got := faviconFor(fsys); got != c.want {
|
||||
t.Errorf("%s: faviconFor = %q, want %q", c.name, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// With no icon in the build, the markup must not promise one: a link to a
|
||||
// missing file costs every visitor a 404 on every page.
|
||||
func TestNoFaviconMeansNoLink(t *testing.T) {
|
||||
h := newHarness(t, nil)
|
||||
if h.favicon != "" {
|
||||
t.Skipf("this build embeds %q, so the empty case cannot be checked here", h.favicon)
|
||||
}
|
||||
|
||||
resp := h.get(t, "/", "")
|
||||
page, _ := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if strings.Contains(string(page), `rel="icon"`) {
|
||||
t.Error("the page links an icon that this build does not carry")
|
||||
}
|
||||
|
||||
resp = h.get(t, "/favicon.ico", "")
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusNotFound {
|
||||
t.Errorf("GET /favicon.ico = %s, want 404 when no icon is embedded", resp.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// --- content security policy ---------------------------------------------
|
||||
|
||||
// The page's own behaviour and its CSP have to agree, and nothing in a Go test
|
||||
|
||||
Reference in New Issue
Block a user