From 9f505b6d9617715aebd43cfb858b347123df12ba Mon Sep 17 00:00:00 2001 From: Winston Howes Date: Fri, 24 Apr 2026 18:51:51 -0700 Subject: [PATCH 1/2] fix allowlist case-insensitive lookup --- app/allowlist.go | 2 +- app/allowlist_test.go | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/allowlist.go b/app/allowlist.go index 996572d..567e5f6 100644 --- a/app/allowlist.go +++ b/app/allowlist.go @@ -113,7 +113,7 @@ func SetAllowlist(name string, callers []CallerConfig) error { // GetAllowlist retrieves the allowlist for an integration. func GetAllowlist(name string) []CallerConfig { allowlists.RLock() - m := allowlists.m[name] + m := allowlists.m[strings.ToLower(name)] res := make([]CallerConfig, 0, len(m)) for _, c := range m { res = append(res, c) diff --git a/app/allowlist_test.go b/app/allowlist_test.go index ec5eebc..426aac7 100644 --- a/app/allowlist_test.go +++ b/app/allowlist_test.go @@ -193,6 +193,21 @@ func TestSetAllowlistIndexing(t *testing.T) { } } +func TestGetAllowlistCaseInsensitive(t *testing.T) { + allowlists.Lock() + allowlists.m = make(map[string]map[string]CallerConfig) + allowlists.Unlock() + + if err := SetAllowlist("MiXeD", []CallerConfig{{ID: "caller"}}); err != nil { + t.Fatalf("failed to set allowlist: %v", err) + } + + got := GetAllowlist("MIXED") + if len(got) != 1 || got[0].ID != "caller" { + t.Fatalf("expected case-insensitive allowlist lookup, got %#v", got) + } +} + func TestFindConstraintWildcard(t *testing.T) { allowlists.Lock() allowlists.m = make(map[string]map[string]CallerConfig) From 40b8cef1b83a044c87331fe9a894fc4a586716eb Mon Sep 17 00:00:00 2001 From: Winston Howes Date: Fri, 24 Apr 2026 19:03:47 -0700 Subject: [PATCH 2/2] fix outgoing auth destination rewriting --- app/integration.go | 24 +++++---------- app/main.go | 16 ++++++++-- app/proxy_test.go | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 18 deletions(-) diff --git a/app/integration.go b/app/integration.go index d3c6234..0487584 100644 --- a/app/integration.go +++ b/app/integration.go @@ -213,24 +213,16 @@ func prepareIntegration(i *Integration) error { i.proxy = httputil.NewSingleHostReverseProxy(u) oldDirector := i.proxy.Director - if hasWildcard { - i.proxy.Director = func(req *http.Request) { - dest, ok := resolvedDestinationFromContext(req.Context()) - if !ok { - oldDirector(req) - req.Host = u.Host - return - } - if resolvedDestinationApplied(req.Context()) { - return - } - applyResolvedDestination(req, dest) + i.proxy.Director = func(req *http.Request) { + if resolvedDestinationApplied(req.Context()) { + return } - } else { - i.proxy.Director = func(req *http.Request) { - oldDirector(req) - req.Host = u.Host + if dest, ok := resolvedDestinationFromContext(req.Context()); ok { + applyResolvedDestination(req, dest) + return } + oldDirector(req) + req.Host = u.Host } i.proxy.ModifyResponse = func(resp *http.Response) error { diff --git a/app/main.go b/app/main.go index 2cf5808..0b41547 100644 --- a/app/main.go +++ b/app/main.go @@ -1418,6 +1418,14 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) { } } + metricsHost := r.Host + metricsRequestURI := r.RequestURI + var metricsURL *url.URL + if r.URL != nil { + u := *r.URL + metricsURL = &u + } + resolvedDest, err := integ.resolveRequestDestination(r) if err != nil { logger.Warn("invalid destination header", "integration", integ.Name, "error", err) @@ -1429,8 +1437,8 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) { return } r = r.WithContext(contextWithResolvedDestination(r.Context(), resolvedDest)) + applyResolvedDestination(r, resolvedDest) if integ.requiresDestinationHeader { - applyResolvedDestination(r, resolvedDest) r.Header.Del("X-AT-Destination") } @@ -1459,7 +1467,11 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) { return } - metrics.OnRequest(integ.Name, r) + metricsReq := r.Clone(r.Context()) + metricsReq.Host = metricsHost + metricsReq.RequestURI = metricsRequestURI + metricsReq.URL = metricsURL + metrics.OnRequest(integ.Name, metricsReq) handoffStart := time.Now() r = r.WithContext(metrics.WithUpstreamRoundtripStart(r.Context(), handoffStart)) metrics.RecordPreProxyDuration(integ.Name, handoffStart.Sub(start)) diff --git a/app/proxy_test.go b/app/proxy_test.go index c9f38e3..4d1212c 100644 --- a/app/proxy_test.go +++ b/app/proxy_test.go @@ -1133,6 +1133,82 @@ func TestProxyHandlerWildcardAddAuthSeesResolvedDestination(t *testing.T) { } } +func TestProxyHandlerStaticAddAuthSeesResolvedDestination(t *testing.T) { + denylists.Lock() + denylists.m = make(map[string]map[string][]CallRule) + denylists.Unlock() + + captureAddAuthCount = 0 + captureLastURL = "" + + upstream, err := url.Parse("http://backend.example.com/base?static=1") + if err != nil { + t.Fatalf("parse upstream: %v", err) + } + + integ := Integration{ + Name: "static-auth-url", + Destination: upstream.String(), + InRateLimit: 1, + OutRateLimit: 1, + OutgoingAuth: []AuthPluginConfig{{ + Type: "test_capture", + Params: map[string]interface{}{"expect_host": upstream.Host}, + }}, + } + if err := AddIntegration(&integ); err != nil { + t.Fatalf("failed to add integration: %v", err) + } + t.Cleanup(func() { + integ.inLimiter.Stop() + integ.outLimiter.Stop() + DeleteIntegration("static-auth-url") + }) + + called := false + integ.proxy.Transport = roundTripFunc(func(req *http.Request) (*http.Response, error) { + called = true + if req.URL.Host != upstream.Host { + t.Fatalf("unexpected upstream host: %s", req.URL.Host) + } + if req.Host != upstream.Host { + t.Fatalf("unexpected request host: %s", req.Host) + } + if req.URL.Path != "/base/test" { + t.Fatalf("unexpected upstream path: %s", req.URL.Path) + } + if req.URL.RawQuery != "static=1&foo=bar" { + t.Fatalf("unexpected query: %s", req.URL.RawQuery) + } + resp := &http.Response{ + StatusCode: http.StatusNoContent, + Header: make(http.Header), + Body: io.NopCloser(strings.NewReader("")), + Request: req, + } + return resp, nil + }) + + req := httptest.NewRequest(http.MethodGet, "http://static-auth-url/test?foo=bar", nil) + req.Host = "static-auth-url" + rr := httptest.NewRecorder() + + proxyHandler(rr, req) + + if rr.Code != http.StatusNoContent { + t.Fatalf("expected 204, got %d", rr.Code) + } + if !called { + t.Fatal("expected transport to be invoked") + } + if captureAddAuthCount != 1 { + t.Fatalf("expected AddAuth to be called once, got %d", captureAddAuthCount) + } + if captureLastURL != "http://backend.example.com/base/test?static=1&foo=bar" { + t.Fatalf("unexpected URL seen by AddAuth: %s", captureLastURL) + } +} + func TestProxyHandlerOutgoingAuthError(t *testing.T) { denylists.Lock() denylists.m = make(map[string]map[string][]CallRule)