Skip to content

Bug: location blocks match path segments anywhere in the URL, not just at the start #639

@zdila

Description

@zdila

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions