tinyrelayfile
tinyrelay/tinyclient/social_content_test.go/

tinyclient/social_content_test.go raw
1package tinyclient
2
3import (
4 "strings"
5 "testing"
6)
7
8func TestSocialBodyRendersMarkdownAndMedia(t *testing.T) {
9 row := map[string]any{
10 "kind": "30023",
11 "content": "# Hello\n\nA **note** with a table:\n\n| one | two |\n| --- | --- |\n| a | b |\n\n![sun](https://cdn.example.test/sun.png)",
12 "tags": [][]string{{"imeta", "url https://cdn.example.test/sun.png", "m image/png", "alt sunset"}},
13 }
14 got := string(socialBody(row))
15 for _, want := range []string{"<h1>Hello</h1>", "<strong>note</strong>", "data-markdown-table", "<img", `alt="sunset"`, `loading="lazy"`} {
16 if !strings.Contains(got, want) {
17 t.Errorf("social body missing %q: %s", want, got)
18 }
19 }
20 if strings.Count(got, "sun.png") != 1 {
21 t.Fatalf("media should have one image: %s", got)
22 }
23}
24
25func TestSocialBodySupportsMediaTypesAndIgnoresCode(t *testing.T) {
26 row := map[string]any{
27 "kind": "1",
28 "content": "https://cdn.example.test/clip.mp4\n\nhttps://cdn.example.test/song.mp3\n\n```\nhttps://cdn.example.test/hidden.png\n```",
29 "tags": [][]string{{"imeta", "url https://cdn.example.test/clip.mp4", "m video/mp4"}, {"imeta", "url https://cdn.example.test/song.mp3", "m audio/mpeg"}, {"imeta", "url https://cdn.example.test/hidden.png", "m image/png"}},
30 }
31 got := string(socialBody(row))
32 if !strings.Contains(got, `<video controls`) || !strings.Contains(got, `<audio controls`) {
33 t.Fatalf("native media missing: %s", got)
34 }
35 if !strings.Contains(got, "hidden.png") || strings.Contains(got, "<iframe") {
36 t.Fatalf("code or iframe leaked into media: %s", got)
37 }
38}
39
40func TestSocialNoteIsPlaintext(t *testing.T) {
41 got := string(socialBody(map[string]any{"kind": "1", "content": "**literal**\nhttps://cdn.example.test/note.png", "tags": [][]string{{"imeta", "url https://cdn.example.test/note.png", "m image/png"}}}))
42 if strings.Contains(got, "<strong>") || !strings.Contains(got, "**literal**") || !strings.Contains(got, "<img") {
43 t.Fatalf("note rendering = %s", got)
44 }
45}
46
47func TestSocialMediaSkipsInlineAndFencedCode(t *testing.T) {
48 url := "https://cdn.example.test/hidden.png"
49 row := map[string]any{"kind": "30023", "content": "`![x](" + url + ")`\n\n````\n![x](" + url + ")\n````", "tags": [][]string{{"imeta", "url " + url, "m image/png"}}}
50 got := string(socialBody(row))
51 if strings.Contains(got, "<img") || strings.Contains(got, "<figure") {
52 t.Fatalf("code media was embedded: %s", got)
53 }
54 if !strings.Contains(got, "hidden.png") {
55 t.Fatalf("code source missing: %s", got)
56 }
57}
58
59func TestSocialBodyEscapesUnsafeContent(t *testing.T) {
60 got := string(socialBody(map[string]any{"content": `<script>alert(1)</script> [x](javascript:alert(1)) https://cdn.example.test/a.svg`, "tags": [][]string{{"imeta", "url https://cdn.example.test/a.svg", "m image/svg+xml"}}}))
61 if strings.Contains(got, "<script") || strings.Contains(got, `href="javascript:`) || strings.Contains(got, "<iframe") {
62 t.Fatalf("unsafe social markup: %s", got)
63 }
64 if !strings.Contains(got, "a.svg") {
65 t.Fatalf("safe media link missing: %s", got)
66 }
67}
68
69func TestSocialPreviewAndMediaURL(t *testing.T) {
70 if got := socialPreview(map[string]any{"content": "hello\nworld"}); got != "hello world" {
71 t.Fatalf("preview = %q", got)
72 }
73 if got := socialMediaURL("javascript:alert(1)"); got != "" {
74 t.Fatalf("unsafe URL = %q", got)
75 }
76 if got := socialMediaURL("https://example.test/a?x=1"); got != "https://example.test/a?x=1" {
77 t.Fatalf("safe URL = %q", got)
78 }
79}
80
81func TestSocialArticleMediaWithoutMetadata(t *testing.T) {
82 got := string(socialBody(map[string]any{"kind": 30023, "content": "![Cover](https://cdn.test/a.jpg)\n\n[Watch](https://cdn.test/a.mp4)\n\nhttps://cdn.test/a.mp3"}))
83 for _, want := range []string{`<img src="https://cdn.test/a.jpg"`, `alt="Cover"`, `<video controls`, `<audio controls`} {
84 if !strings.Contains(got, want) {
85 t.Errorf("missing %s: %s", want, got)
86 }
87 }
88 if strings.Contains(got, "!Cover") || strings.Contains(got, "<p><figure") {
89 t.Fatal(got)
90 }
91}
92func TestSocialNoteImageWithoutMetadataAndInvalidClientMIME(t *testing.T) {
93 for _, tags := range [][][]string{nil, {{"imeta", "url https://cdn.test/a.jpg", "m jpeg"}}} {
94 got := string(socialBody(map[string]any{"kind": 1, "content": "A photo\nhttps://cdn.test/a.jpg", "tags": tags}))
95 if !strings.Contains(got, `<img src="https://cdn.test/a.jpg"`) {
96 t.Fatal(got)
97 }
98 }
99}
100func TestSocialMediaTokenCannotBeForgedInCode(t *testing.T) {
101 raw := "https://cdn.test/a.png"
102 got := string(socialBody(map[string]any{"kind": 30023, "content": "`" + socialMediaToken(raw) + "`\n\n![A](" + raw + ")"}))
103 if strings.Count(got, "<img") != 1 || !strings.Contains(got, "<code>"+socialMediaToken(raw)+"</code>") {
104 t.Fatal(got)
105 }
106}
107
108func TestSocialMediaReservesValidMetadataDimensions(t *testing.T) {
109 for _, dimension := range []string{"1170x1613", "1170.0x1613.0"} {
110 got := string(socialBody(map[string]any{"kind": 1, "content": "https://cdn.test/a.jpg", "tags": [][]string{{"imeta", "url https://cdn.test/a.jpg", "m image/jpeg", "dim " + dimension}}}))
111 if !strings.Contains(got, `width="1170" height="1613"`) {
112 t.Fatal(got)
113 }
114 }
115 for _, dimension := range []string{"0x3", "NaNx3", "1e90x3", `1" onload="bad`} {
116 got := string(socialBody(map[string]any{"kind": 1, "content": "https://cdn.test/a.jpg", "tags": [][]string{{"imeta", "url https://cdn.test/a.jpg", "m image/jpeg", "dim " + dimension}}}))
117 if strings.Contains(got, "width=") || strings.Contains(got, "onload=") {
118 t.Fatal(got)
119 }
120 }
121}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs