Skip to content

Match repository patterns in linear time, and refuse a timed-out match [patch] - #113

Merged
matt-edmondson merged 2 commits into
mainfrom
fix/111-linear-repository-pattern
Nov 4, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
fix/111-linear-repository-pattern

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #111

What changes

GitBranchStateCache/Upstreams/RepositoryPattern.cs:

  • The translated expression is now built with RegexOptions.NonBacktracking, so matching time is linear in the path whatever the pattern. A pattern with two stars in one segment (team/*-svc-*) no longer backtracks polynomially on a long segment that can't match. Case-insensitive, culture-invariant matching and the anchoring are unchanged.
  • Matches catches RegexMatchTimeoutException and returns false. Even if the 100 ms guard ever fires, IsAllowed then fails and BranchStateHandler.Resolve returns its existing 404 no-such-repository, never an unhandled 500.
  • The remarks now say why both measures are there. The old remark claimed the timeout "cannot fire in practice", which was wrong.

I didn't move the segment-length check ahead of the allow-list. With linear matching it isn't needed for this issue, and it would change the order of 404 reasons, which is a separate decision.

Tests

In RepositoryPatternTests:

  • Matches_SeveralStarsInOneSegment_RejectsALongNonMatchingPathWithoutTimingOut runs studio/*-*-* against 1000 dashes, studio/*-* against 4000 dashes, and team/*-svc-* against 4000 dashes. After a warm-up match, each must return false in under 50 ms.
  • Matches_SeveralStarsInOneSegment_StillMatchesWhatItShould checks that a multi-star pattern still matches, case-insensitively, and still respects segment boundaries.

With the source change reverted, the studio/*-*-* and studio/*-* rows fail: the match times out after about 100 ms and throws. With it applied, the full GitBranchStateCache.Tests suite passes on net10.0.


Generated by Claude Code

…h [patch]

A pattern with two stars in one segment, such as team/*-svc-*, became
adjacent [^/]* runs that the backtracking engine took polynomial time to
reject. The allow-list runs before admission, so an unauthenticated long
path could time the match out and get a 500. The expression is now built
with RegexOptions.NonBacktracking, and a match that still times out counts as
not allowed.

Fixes #111

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW6ERvyATBYDH9pPgb7
Adds an internal TryParse overload that takes the match timeout, so a test
can make a match time out and check that Matches reads it as not allowed
rather than throwing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW6ERvyATBYDH9pPgb7

Copy link
Copy Markdown
Contributor Author

ci / .NET / Test on macos-latest on 52e9328 went red, but the failure isn't from this change. All 290 tests passed in that job. The job failed afterwards, in the post-job cleanup of the .NET setup step: The 'dotnet nuget locals all --list --force-english-output' command failed with exit code: 137, meaning the process was killed on the runner. No code in this PR runs in that step.

I've re-run the failed job once. The 52e9328 commit also adds a test for the timed-out match path, which addresses SonarCloud's earlier 50% new-code coverage failure on 3370d8e.


Generated by Claude Code

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A repository allow-list pattern with two * in one segment lets an unauthenticated long path time out the regex and get a 500

2 participants