package gateway import ( "io" "net/http" "net/http/httptest" "strings" "testing" ) func TestPluginProxyRejectsUnsafeTargetsAndOrigin(t *testing.T) { g, _ := gatewayWithSpy(t) for _, target := range []string{"http://example.com:80", "http://localhost:80", "https://127.0.0.1:80", "http://127.0.0.1", "http://u:p@127.0.0.1:80", "http://127.0.0.1:80/path", "http://127.0.0.1:80/?x=1"} { rr := httptest.NewRecorder() g.servePluginProxy(rr, httptest.NewRequest("GET", "http://gateway/api/plugins/http/relay/s/live.js", nil), target, "/api/plugins/http/relay/s") if rr.Code != 502 { t.Errorf("unsafe target %q accepted: %d", target, rr.Code) } } rr := httptest.NewRecorder() req := httptest.NewRequest("GET", "http://gateway/api/plugins/http/relay/s/websockify", nil) req.Header.Set("Origin", "https://evil.example") g.servePluginProxy(rr, req, "http://127.0.0.1:12345", "/api/plugins/http/relay/s") if rr.Code != 403 { t.Fatalf("foreign origin accepted: %d", rr.Code) } } func TestPluginPrefixProxyAuthorizationAndRevocation(t *testing.T) { backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { t.Error("denied request reached backend") })) defer backend.Close() g, _ := gatewayWithSpy(t) src := `local p={name="relaydeny",http={{path="relaydeny/:session",prefix=true,roles={"admin"},handler="proxy"}}} function p.proxy(req) return {status=404,body="expired session"} end return p` if err := g.core.Plugins().LoadSource("relaydeny", src); err != nil { t.Fatal(err) } server := httptest.NewServer(g.Handler()) defer server.Close() for _, tc := range []struct { path, key string status int }{ {"/api/plugins/http/relaydeny/expired/vnc.html", "", 401}, {"/api/plugins/http/relaydeny/expired/vnc.html", "sk-test", 404}, {"/api/plugins/http/relaydeny-not-a-child/expired/vnc.html", "sk-test", 404}, } { req, _ := http.NewRequest("GET", server.URL+tc.path, nil) if tc.key != "" { req.Header.Set("Authorization", "Bearer "+tc.key) } resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } b, _ := io.ReadAll(resp.Body) resp.Body.Close() if resp.StatusCode != tc.status { t.Fatalf("%s got %d %s", tc.path, resp.StatusCode, b) } if tc.key != "" && strings.Contains(tc.path, "/expired/") && strings.Contains(tc.path, "/relaydeny/") && string(b) != "expired session" { t.Fatalf("revocation response=%s", b) } } }