Add link previews

This commit is contained in:
2026-09-18 20:56:41 +02:00
parent 601053c51d
commit f152f84139
7 changed files with 188 additions and 13 deletions
+127
View File
@@ -2346,3 +2346,130 @@ func TestFilesPageIsLaidOutForATable(t *testing.T) {
t.Error("the listing still relies on a horizontal scroll container")
}
}
// --- link previews -------------------------------------------------------
// metaContent pulls a meta tag's content out of a rendered page, keyed by
// either the property or the name attribute, so a test reads what an unfurler
// would rather than matching a whole tag it does not care about.
func metaContent(page, key string) string {
for _, attr := range []string{"property", "name"} {
marker := fmt.Sprintf("<meta %s=%q content=", attr, key)
i := strings.Index(page, marker)
if i < 0 {
continue
}
rest := page[i+len(marker):]
if !strings.HasPrefix(rest, `"`) {
continue
}
if end := strings.Index(rest[1:], `"`); end >= 0 {
return rest[1 : 1+end]
}
}
return ""
}
// A /i/ link is the thing people paste at each other, so it has to unfurl into
// something that says what is on the other end of it.
func TestInfoPageUnfurls(t *testing.T) {
h := newHarness(t, func(c *config.Config) { c.PublicURL = "https://drop.example" })
res := decode[uploadResult](t, h.uploadReader(t,
strings.NewReader(strings.Repeat("x", 4096)),
map[string]string{
"Content-Disposition": `attachment; filename="MyGame.zip"`,
"Authorization": "Bearer " + h.token,
}))
info := h.get(t, "/i/"+res.ID, "")
raw, _ := io.ReadAll(info.Body)
info.Body.Close()
page := string(raw)
if got, want := metaContent(page, "og:title"), "MyGame.zip - Uncensored Send"; got != want {
t.Errorf("og:title = %q, want %q", got, want)
}
if got, want := metaContent(page, "og:url"), "https://drop.example/i/"+res.ID; got != want {
t.Errorf("og:url = %q, want %q", got, want)
}
if got, want := metaContent(page, "author"), "friend"; got != want {
t.Errorf("author = %q, want %q", got, want)
}
if metaContent(page, "twitter:card") != "summary" {
t.Error("no twitter:card, so clients holding to that vocabulary draw nothing")
}
desc := metaContent(page, "og:description")
for _, want := range []string{"4 KiB", "uploaded by friend", "expires 2026-09-15 10:00 UTC"} {
if !strings.Contains(desc, want) {
t.Errorf("og:description = %q, missing %q", desc, want)
}
}
if !strings.Contains(desc, "Open the page") {
t.Errorf("og:description = %q, says nothing about what to do with the link", desc)
}
// The deadline is cached by the chat platform and read back days later, so
// it has to be an absolute time rather than a countdown that goes stale.
if strings.Contains(desc, "in 3d") {
t.Errorf("og:description = %q, carries a relative deadline", desc)
}
if metaContent(page, "description") != desc {
t.Error("the plain description tag disagrees with the Open Graph one")
}
}
// An anonymous upload has no uploader to name, and the card must not claim one.
func TestAnonymousUploadHasNoAuthorInItsCard(t *testing.T) {
h := newHarness(t, nil)
res := decode[uploadResult](t, h.upload(t, []byte("x"), nil))
info := h.get(t, "/i/"+res.ID, "")
raw, _ := io.ReadAll(info.Body)
info.Body.Close()
page := string(raw)
if got := metaContent(page, "author"); got != "" {
t.Errorf("author = %q, want none", got)
}
if desc := metaContent(page, "og:description"); strings.Contains(desc, "uploaded by") {
t.Errorf("og:description = %q, names an uploader there is none of", desc)
}
if !strings.Contains(page, "anonymous") {
t.Error("the page itself does not say the upload was anonymous")
}
}
// A filename is caller-supplied and ends up inside an attribute; the escaping
// has to hold there as well as in the body.
func TestPreviewEscapesTheFilename(t *testing.T) {
h := newHarness(t, nil)
res := decode[uploadResult](t, h.upload(t, []byte("x"), map[string]string{
"Content-Disposition": `attachment; filename="\" onload=\"alert(1)"`}))
info := h.get(t, "/i/"+res.ID, "")
raw, _ := io.ReadAll(info.Body)
info.Body.Close()
page := string(raw)
if strings.Contains(page, `onload="alert(1)"`) {
t.Fatalf("a filename broke out of the meta attribute:\n%s", page)
}
if got, want := metaContent(page, "og:title"), res.Filename; !strings.Contains(got, "onload") && got != want {
// The value is escaped, so this only checks the tag is there at all.
t.Errorf("og:title = %q", got)
}
}
// Pages whose content depends on who is asking say nothing to an unfurler,
// which presents no token and would otherwise be shown a stranger's view.
func TestOnlyTheInfoPageCarriesACard(t *testing.T) {
h := newHarness(t, nil)
for _, path := range []string{"/", "/login", "/files"} {
resp := h.get(t, path, "")
raw, _ := io.ReadAll(resp.Body)
resp.Body.Close()
if got := metaContent(string(raw), "og:title"); got != "" {
t.Errorf("%s carries og:title = %q", path, got)
}
}
}