tinyrelayfile
tinyrelay/tinyclient/remote_scope_test.go/

tinyclient/remote_scope_test.go raw
1package tinyclient
2
3import (
4 "net/http"
5 "net/http/httptest"
6 "strings"
7 "sync/atomic"
8 "testing"
9)
10
11func TestRemoteRejectsCrossTenantRoutesBeforeRenderingOrProxying(t *testing.T) {
12 app, err := New(&fakeBackend{policy: DefaultPolicy(strings.Repeat("a", 64))}, Options{})
13 if err != nil {
14 t.Fatal(err)
15 }
16 var calls atomic.Int32
17 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
18 calls.Add(1)
19 if strings.HasSuffix(r.URL.Path, BackendPath) {
20 app.BackendHandler().ServeHTTP(w, r)
21 return
22 }
23 w.WriteHeader(http.StatusAccepted)
24 }))
25 defer upstream.Close()
26 for _, mount := range []string{"", "/r/team"} {
27 t.Run("mount="+mount, func(t *testing.T) {
28 remote, err := NewRemote(RemoteOptions{BackendURL: upstream.URL + mount, PublicURL: "http://relay.example" + mount})
29 if err != nil {
30 t.Fatal(err)
31 }
32 for _, tc := range []struct {
33 name, method, path, header, value string
34 }{
35 {"page", "GET", "/r/other/account", "", ""},
36 {"head", "HEAD", "/r/other/account", "", ""},
37 {"feed", "GET", "/r/other/articles.json", "", ""},
38 {"asset", "GET", "/r/other/scripts/bridge.js", "", ""},
39 {"tenant root", "GET", "/r/other", "", ""},
40 {"tenant home", "GET", "/r/other/", "", ""},
41 {"events", "POST", "/r/other/events", "", ""},
42 {"session", "POST", "/r/other/session", "", ""},
43 {"delete", "DELETE", "/r/other/events", "", ""},
44 {"options", "OPTIONS", "/r/other/events", "", ""},
45 {"signed page", "GET", "/r/other/account", "Authorization", "Nostr exact-proof"},
46 {"websocket", "GET", "/r/other/", "Upgrade", "websocket"},
47 {"nostr info", "GET", "/r/other/", "Accept", "application/nostr+json"},
48 {"stream", "GET", "/r/other/rooms/build/stream", "", ""},
49 {"adapter", "GET", "/r/other" + BackendPath, "", ""},
50 {"encoded tenant prefix", "GET", "/%72/other/account", "", ""},
51 } {
52 t.Run(tc.name, func(t *testing.T) {
53 req := httptest.NewRequest(tc.method, "http://relay.example"+tc.path, nil)
54 req.AddCookie(&http.Cookie{Name: "tiny_session", Value: "root-session"})
55 if tc.header != "" {
56 req.Header.Set(tc.header, tc.value)
57 }
58 if tc.header == "Upgrade" {
59 req.Header.Set("Connection", "Upgrade")
60 }
61 before := calls.Load()
62 got := httptest.NewRecorder()
63 remote.ServeHTTP(got, req)
64 if got.Code != http.StatusNotFound || calls.Load() != before {
65 t.Fatalf("cross-tenant request reached renderer or proxy: status=%d upstream calls=%d", got.Code, calls.Load()-before)
66 }
67 })
68 }
69 // The scope guard must not disable the configured tenant's routes.
70 for _, tc := range []struct {
71 method, path string
72 status int
73 }{
74 {"GET", "/account", 200},
75 {"POST", "/events", 202},
76 {"GET", "/rooms/build/stream", 202},
77 } {
78 got := httptest.NewRecorder()
79 remote.ServeHTTP(got, httptest.NewRequest(tc.method, "http://relay.example"+mount+tc.path, nil))
80 if got.Code != tc.status {
81 t.Errorf("valid tenant %s %s: status=%d want=%d", tc.method, tc.path, got.Code, tc.status)
82 }
83 }
84 })
85 }
86}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

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

Refs