Skip to content

fix: FindBestPatternMatch incorrectly allows wildcard pattern to override exact match #3617

@kuishou68

Description

@kuishou68

Bug Description

In internal/core/pattern.go, the FindBestPatternMatch function has a logic error where a wildcard pattern can override an exact match if it appears later in the values slice.

Root Cause

The Go implementation uses pattern.StarIndex for the "betterness" comparison:

func FindBestPatternMatch[T any](values []T, getPattern func(v T) Pattern, candidate string) T {
    var bestPattern T
    longestMatchPrefixLength := -1
    for _, value := range values {
        pattern := getPattern(value)
        if (pattern.StarIndex == -1 || pattern.StarIndex > longestMatchPrefixLength) && pattern.Matches(candidate) {
            bestPattern = value
            longestMatchPrefixLength = pattern.StarIndex  // BUG: -1 for exact match
        }
    }
    return bestPattern
}

When an exact match is found (StarIndex == -1), longestMatchPrefixLength is set to -1. Any subsequent wildcard pattern with StarIndex >= 0 satisfies the condition pattern.StarIndex > longestMatchPrefixLength (i.e., 0 > -1 = true), so it overrides the exact match.

Original TypeScript

The original TypeScript uses pattern.prefix.length which is the full string length for exact matches — always a large positive number preventing any later wildcard from overriding:

export function findBestPatternMatch<T>(values: readonly T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined {
    let matchedValue: T | undefined;
    let longestMatchPrefixLength = -1;
    for (let i = 0; i < values.length; i++) {
        const v = values[i];
        const pattern = getPattern(v);
        if (pattern.prefix.length > longestMatchPrefixLength && isPatternMatch(pattern, candidate)) {
            longestMatchPrefixLength = pattern.prefix.length;  // for exact match: full string length
            matchedValue = v;
        }
    }
    return matchedValue;
}

Fix

After finding an exact match, set longestMatchPrefixLength = math.MaxInt so no wildcard can override it:

if (pattern.StarIndex == -1 || pattern.StarIndex > longestMatchPrefixLength) && pattern.Matches(candidate) {
    bestPattern = value
    if pattern.StarIndex == -1 {
        longestMatchPrefixLength = math.MaxInt  // exact match cannot be overridden
    } else {
        longestMatchPrefixLength = pattern.StarIndex
    }
}

Impact

This affects path mapping and module resolution: an exact path mapping like "./foo/bar" could be incorrectly overridden by a wildcard like "./foo/*" if the wildcard appears later in the paths configuration.

Signed-off-by: cocoon 54054995+kuishou68@users.noreply.github.com

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions