| 1 | package tinyclient |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | ) |
| 7 | |
| 8 | func TestInjectBasePreservesExternalURLsAndContent(t *testing.T) { |
| 9 | for _, tc := range []struct { |
| 10 | name, input, want string |
| 11 | }{ |
| 12 | {"local link" , `<a href="/repo?path=a%26b">code</a>` , `<a href="/r/alice/repo?path=a%26b">code</a>` }, |
| 13 | {"external link" , `<a href="//example.org/file">external</a>` , `<a href="//example.org/file">external</a>` }, |
| 14 | {"external image" , `<img src="//example.org/icon.png">` , `<img src="//example.org/icon.png">` }, |
| 15 | {"already scoped" , `<a href="/r/alice/files">files</a>` , `<a href="/r/alice/files">files</a>` }, |
| 16 | {"script source" , `<script src="/fixi.js"></script>` , `<script src="/r/alice/fixi.js"></script>` }, |
| 17 | {"room avatar fallback" , `<room-avatar fallback="/avatars/v1/avatar.svg"><img src="/avatars/v1/avatar.svg"></room-avatar>` , `<room-avatar fallback="/r/alice/avatars/v1/avatar.svg"><img src="/r/alice/avatars/v1/avatar.svg"></room-avatar>` }, |
| 18 | {"script text" , `<script>const example = ' href="/example"';</script>` , `<script>const example = ' href="/example"';</script>` }, |
| 19 | {"textarea" , `<textarea> href="/example"</textarea>` , `<textarea> href="/example"</textarea>` }, |
| 20 | {"local action" , `<button fx-action="/connect/fragment">preview</button>` , `<button fx-action="/r/alice/connect/fragment">preview</button>` }, |
| 21 | } { |
| 22 | t.Run(tc.name, func(t *testing.T) { |
| 23 | if got := injectBase(tc.input, "/r/alice" ); got != tc.want { |
| 24 | t.Fatalf("got %q, want %q" , got, tc.want) |
| 25 | } |
| 26 | }) |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | func FuzzInjectBasePreservesText(f *testing.F) { |
| 31 | for _, seed := range []string{"hello" , ` href="/file"` , `fetch("/example")` , `new URL("/file")` , "emoji 🧪" , "a\x00b" } { |
| 32 | f.Add(seed) |
| 33 | } |
| 34 | f.Fuzz(func(t *testing.T, content string) { |
| 35 | // Textarea content can contain quotes and URL examples literally. |
| 36 | content = strings.ReplaceAll(content, "<" , "<" ) |
| 37 | input := "<textarea>" + content + "</textarea>" |
| 38 | if got := injectBase(input, "/r/alice" ); got != input { |
| 39 | t.Fatalf("content changed: got %q, want %q" , got, input) |
| 40 | } |
| 41 | }) |
| 42 | } |