Files
traefikoidc/header_templates_owned_test.go
T
lukaszraczylo dc4da7b4c9 fix(security): drop client values for templated headers
A templated header skipped its Set when the render failed, came out
empty or failed sanitising. The client's own value of that header then
reached the backend. A client could forge a header such as X-Tenant by
using a token that lacks the claim. The middleware now removes the
inbound value first. Identity headers and Authorization are left alone.
2026-11-03 01:10:20 +00:00

55 lines
2.0 KiB
Go

package traefikoidc
import (
"net/http/httptest"
"testing"
"text/template"
)
func ownedHeaderOIDC(t *testing.T, names ...string) *TraefikOidc {
t.Helper()
m := map[string]*template.Template{}
for _, n := range names {
m[n] = template.Must(template.New(n).Option("missingkey=zero").Parse("{{.Claims.tenant}}"))
}
return &TraefikOidc{logger: NewLogger("error"), headerTemplates: m}
}
// A templated header the middleware owns must never carry a client value:
// an optional claim that renders empty must leave the header absent.
func TestTemplatedHeaderDropsClientSpoof(t *testing.T) {
o := ownedHeaderOIDC(t, "X-Tenant")
req := httptest.NewRequest("GET", "/", nil)
req.Header.Set("X-Tenant", "admin-corp")
o.applyHeaderTemplates(req, &principal{Claims: map[string]interface{}{"sub": "u"}})
if got := req.Header.Get("X-Tenant"); got != "" {
t.Fatalf("client-supplied X-Tenant reached the backend: %q", got)
}
}
func TestTemplatedHeaderRenderedValueWins(t *testing.T) {
o := ownedHeaderOIDC(t, "X-Tenant")
req := httptest.NewRequest("GET", "/", nil)
req.Header.Set("X-Tenant", "evil")
o.applyHeaderTemplates(req, &principal{Claims: map[string]interface{}{"tenant": "acme"}})
if got := req.Header.Get("X-Tenant"); got != "acme" {
t.Fatalf("X-Tenant = %q, want acme", got)
}
}
// Identity headers are injected before templates run; an empty render must
// not clobber them, and Authorization stays as received.
func TestTemplatedHeaderKeepsIdentityAndAuthorization(t *testing.T) {
o := ownedHeaderOIDC(t, "X-Forwarded-User", "Authorization")
req := httptest.NewRequest("GET", "/", nil)
req.Header.Set("X-Forwarded-User", "alice")
req.Header.Set("Authorization", "Bearer abc")
o.applyHeaderTemplates(req, &principal{Claims: map[string]interface{}{"sub": "u"}})
if got := req.Header.Get("X-Forwarded-User"); got != "alice" {
t.Fatalf("identity header clobbered: %q", got)
}
if got := req.Header.Get("Authorization"); got != "Bearer abc" {
t.Fatalf("Authorization changed: %q", got)
}
}