Bug: location blocks match path segments anywhere in the URL, not just at the start
Summary
A location "/foo" block incorrectly matches requests whose URL contains /foo as any path segment, not only requests whose path starts with /foo. For example, location "/branding" matches /user/account/branding.
Environment
- Ferron version: (checked against current
develop-2.x branch source)
- Configuration format: KDL
Steps to reproduce
Given ferron.kdl:
example.com {
location "/branding" {
proxy "https://s3.amazonaws.com/example/branding/"
}
location "/" {
proxy "http://localhost:8080/"
}
}
Send a request to https://example.com/user/account/branding. It is proxied to S3 instead of localhost:8080.
Expected behavior
location "/branding" should only match requests whose path equals /branding or starts with /branding/ (i.e. standard prefix matching anchored to the beginning of the path).
Actual behavior
location "/branding" matches any request containing branding as a path segment regardless of position — e.g. /user/account/branding, /a/b/c/branding/d.
Root cause
In ferron/src/config/lookup/tree.rs, the get method walks the request path segment by segment through the config trie. When a segment has no matching child node and no predicate matches, the inner while loop falls through to its next iteration instead of stopping. This causes unmatched segments to be silently skipped, so the traversal can eventually land on a deeper node (e.g. branding) even when the intermediate segments (user, account) did not match the configured location path.
Relevant code (before fix):
if conditional_predicate_matched {
break;
}
// If nothing happened, probably no matching fixed multi-key or predicate single key found...
// ← no break here; loop continues with next segment
Fix
Add a break when no fixed key and no predicate matched. A wildcard_matched flag is needed to distinguish this case from a successful wildcard hostname match (e.g. *.example.com), which also exits the predicate for loop without setting conditional_predicate_matched:
if conditional_predicate_matched {
break;
}
if !wildcard_matched {
// No match found for this segment — stop traversal to prevent matching location
// segments that appear later in the path.
break;
}
The wildcard_matched flag is set to true inside the HostDomainLevelWildcard branch (replacing the old unmarked break out of the predicate loop).
Additional bug (unrelated)
In ferron-common/src/util/match_location.rs, line 16 reads:
req_path_prepared = path_prepared.replace("//", "/");
It should be:
req_path_prepared = req_path_prepared.replace("//", "/");
Using path_prepared here replaces the request path with the (already-normalized) location path whenever the request URL contains //, causing incorrect matching for such URLs.
Bug:
locationblocks match path segments anywhere in the URL, not just at the startSummary
A
location "/foo"block incorrectly matches requests whose URL contains/fooas any path segment, not only requests whose path starts with/foo. For example,location "/branding"matches/user/account/branding.Environment
develop-2.xbranch source)Steps to reproduce
Given
ferron.kdl:Send a request to
https://example.com/user/account/branding. It is proxied to S3 instead oflocalhost:8080.Expected behavior
location "/branding"should only match requests whose path equals/brandingor starts with/branding/(i.e. standard prefix matching anchored to the beginning of the path).Actual behavior
location "/branding"matches any request containingbrandingas a path segment regardless of position — e.g./user/account/branding,/a/b/c/branding/d.Root cause
In
ferron/src/config/lookup/tree.rs, thegetmethod walks the request path segment by segment through the config trie. When a segment has no matching child node and no predicate matches, the innerwhileloop falls through to its next iteration instead of stopping. This causes unmatched segments to be silently skipped, so the traversal can eventually land on a deeper node (e.g.branding) even when the intermediate segments (user,account) did not match the configured location path.Relevant code (before fix):
Fix
Add a
breakwhen no fixed key and no predicate matched. Awildcard_matchedflag is needed to distinguish this case from a successful wildcard hostname match (e.g.*.example.com), which also exits the predicateforloop without settingconditional_predicate_matched:The
wildcard_matchedflag is set totrueinside theHostDomainLevelWildcardbranch (replacing the old unmarkedbreakout of the predicate loop).Additional bug (unrelated)
In
ferron-common/src/util/match_location.rs, line 16 reads:It should be:
Using
path_preparedhere replaces the request path with the (already-normalized) location path whenever the request URL contains//, causing incorrect matching for such URLs.