mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-09-18 14:12:38 +00:00
fix(quickproxy): reject Exclude entries outside their rule's URLPrefix
An Exclude entry only ever matches requests that already fall under its rule's URLPrefix, so one written without that prefix (e.g. "/health" on a rule for "/api") silently never triggered. Validate that each Exclude entry itself starts with the rule's URLPrefix, failing NewService instead of accepting a no-op config.
This commit is contained in:
@@ -28,9 +28,11 @@ type Rule struct {
|
|||||||
Target string
|
Target string
|
||||||
|
|
||||||
// Exclude is a list of URL path prefixes that this rule should not
|
// Exclude is a list of URL path prefixes that this rule should not
|
||||||
// proxy, even though they fall under URLPrefix. Each entry must start
|
// proxy, even though they fall under URLPrefix. Each entry is a full
|
||||||
// with "/". A request matching an Exclude prefix is treated as if this
|
// path from root and must itself start with URLPrefix (e.g. rule
|
||||||
// rule didn't match at all: matching continues against any other
|
// URLPrefix "/api" excluding a subpath must use "/api/health", not
|
||||||
|
// "/health"). A request matching an Exclude prefix is treated as if
|
||||||
|
// this rule didn't match at all: matching continues against any other
|
||||||
// configured rule, falling back if none match. This is typically used
|
// configured rule, falling back if none match. This is typically used
|
||||||
// to carve out paths (e.g. "/health") from a catch-all "/" rule so
|
// to carve out paths (e.g. "/health") from a catch-all "/" rule so
|
||||||
// they're served by the fallback handler instead of being proxied.
|
// they're served by the fallback handler instead of being proxied.
|
||||||
@@ -122,6 +124,9 @@ func NewService(rules []Rule, opts ...Option) (*Service, error) {
|
|||||||
if !strings.HasPrefix(ex, "/") {
|
if !strings.HasPrefix(ex, "/") {
|
||||||
return nil, fmt.Errorf("quickproxy: exclude prefix %q for rule %q must start with /", ex, r.URLPrefix)
|
return nil, fmt.Errorf("quickproxy: exclude prefix %q for rule %q must start with /", ex, r.URLPrefix)
|
||||||
}
|
}
|
||||||
|
if !strings.HasPrefix(ex, r.URLPrefix) {
|
||||||
|
return nil, fmt.Errorf("quickproxy: exclude prefix %q for rule %q must itself start with the rule's URLPrefix", ex, r.URLPrefix)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compiled = append(compiled, compiledRule{
|
compiled = append(compiled, compiledRule{
|
||||||
|
|||||||
@@ -26,10 +26,16 @@ func TestNewService_Validation(t *testing.T) {
|
|||||||
{"bad exclude prefix", []Rule{
|
{"bad exclude prefix", []Rule{
|
||||||
{URLPrefix: "/", Target: "http://localhost:1", Exclude: []string{"health"}},
|
{URLPrefix: "/", Target: "http://localhost:1", Exclude: []string{"health"}},
|
||||||
}, true},
|
}, true},
|
||||||
|
{"exclude outside rule's URLPrefix", []Rule{
|
||||||
|
{URLPrefix: "/api", Target: "http://localhost:1", Exclude: []string{"/health"}},
|
||||||
|
}, true},
|
||||||
{"valid", []Rule{{URLPrefix: "/api", Target: "http://localhost:1"}}, false},
|
{"valid", []Rule{{URLPrefix: "/api", Target: "http://localhost:1"}}, false},
|
||||||
{"valid with exclude", []Rule{
|
{"valid with exclude", []Rule{
|
||||||
{URLPrefix: "/", Target: "http://localhost:1", Exclude: []string{"/health"}},
|
{URLPrefix: "/", Target: "http://localhost:1", Exclude: []string{"/health"}},
|
||||||
}, false},
|
}, false},
|
||||||
|
{"valid with nested exclude", []Rule{
|
||||||
|
{URLPrefix: "/api", Target: "http://localhost:1", Exclude: []string{"/api/health"}},
|
||||||
|
}, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|||||||
Reference in New Issue
Block a user