Fix race condition in content exclusion that causes intermittent bypass#308814
Draft
Fix race condition in content exclusion that causes intermittent bypass#308814
Conversation
e935283 to
cb24d7a
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a race in RemoteContentExclusion.isIgnored() where concurrent calls could evaluate against seeded empty exclusion rules and then cache stale “not ignored” results for an extended period, intermittently bypassing content exclusion.
Changes:
- Await an in-flight content exclusion fetch that may have been started by a concurrent caller after an
awaitpoint inisIgnored(). - Clear ignore-result caches after rules are fetched to invalidate any stale entries written during the fetch window.
- Add unit tests that reproduce the concurrency window using
Barrierto control async timing.
Show a summary per file
| File | Description |
|---|---|
| extensions/copilot/src/platform/ignore/node/remoteContentExclusion.ts | Adds an extra await for concurrent fetches and clears ignore caches again after successful rule fetch. |
| extensions/copilot/src/platform/ignore/node/test/remoteContentExclusion.spec.ts | Adds barrier-controlled tests covering the reported concurrency timing windows. |
Copilot's findings
- Files reviewed: 2/2 changed files
- Comments generated: 4
extensions/copilot/src/platform/ignore/node/test/remoteContentExclusion.spec.ts
Show resolved
Hide resolved
extensions/copilot/src/platform/ignore/node/test/remoteContentExclusion.spec.ts
Show resolved
Hide resolved
cb24d7a to
cbf4613
Compare
Adds tests that reproduce the intermittent content exclusion bypass reported in github/Copilot-Controls#650. The primary test uses per-call barriers to force a deterministic interleaving where Call A starts the CAPI fetch (seeding empty patterns) and Call B resumes before the fetch completes, matching against empty rules and incorrectly caching 'not ignored'. This test FAILS on the current code — the next commit provides the fix.
Concurrent isIgnored() calls could cache stale 'not ignored' results when a content exclusion rule fetch was in progress. This happened because: 1. After yielding at getRepositoryFetchUrls, a concurrent caller could have already started a fetch and seeded the cache with empty patterns. The current caller would skip the fetch (patterns already seeded, lastRuleFetch just set) and match against empty rules. 2. The glob result cache was only cleared at the start of the fetch, so any 'false' entries written during the fetch window persisted until the next 30-minute refresh. Fix: - Cache the result of shouldFetchContentExclusionRules to avoid calling it twice (it has side effects that seed the cache). - Re-check _contentExclusionFetchPromise after the getRepositoryFetchUrls yield point and wait for any in-progress fetch before pattern matching. - Clear both glob and regex result caches at both the start and end of the fetch to invalidate stale entries written by concurrent callers. Fixes github/Copilot-Controls#650
cbf4613 to
708647f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Concurrent
isIgnored()calls inRemoteContentExclusioncan permanently cache stale "not ignored" results when a content exclusion rule fetch is in progress, causing files that should be excluded to intermittently bypass content exclusion rules.Reported in: github/Copilot-Controls#650
Root Cause
The race condition occurs between concurrent
isIgnored()calls during the initial content exclusion rule fetch:isIgnored(), yields atgetRepositoryFetchUrls()(async)isIgnored(), yields atgetRepositoryFetchUrls()(async)shouldFetchContentExclusionRules()which seeds_contentExclusionCachewith empty patterns and triggers a network fetch — yields againshouldFetchContentExclusionRules()returnsfalse(entries already seeded), stale-time check passes (just set), no fetch is awaited. Matches against empty patterns → cachesfalsein_ignoreGlobResultCachefalseentry persists until the 30-minute refreshThis explains the customer's symptoms: "it manages to exclude files sometimes but after a reload it excludes another set of files" — which files hit the race window depends on timing.
Fix
Three changes to
remoteContentExclusion.ts:Cache
shouldFetchContentExclusionRulesresult — the method has side effects (seeds_contentExclusionCachewith empty entries), so calling it twice could produce different results and misleading log output. Store the result in a local variable.Re-check
_contentExclusionFetchPromiseafter thegetRepositoryFetchUrlsyield point — if a concurrent caller started a fetch while we were suspended, wait for it to complete before matching patterns. This prevents matching against empty seed entries.Clear both
_ignoreGlobResultCacheand_ignoreRegexResultCacheat both start and end of_contentExclusionRequest()— the start-of-method clear was already there but only for the glob cache. Now both caches are cleared symmetrically at both ends, invalidating any stale entries written by concurrent callers during the fetch window.Commits
main.Testing
Three new tests in
remoteContentExclusion.spec.ts:getRepositoryFetchUrlsand the CAPI fetch to force the exact interleaving: Call A seeds empty patterns and starts the fetch, Call B resumes and must wait instead of matching against empty rules. Fails without the fix, passes with it.