Does this issue occur when all extensions are disabled?: Yes
- VS Code Version: Current
main branch, built from source (1.132.0)
- OS Version: Windows 11 24H2 (Build 26200.x)
Summary
UriIdentityService does not invalidate cached canonical URIs when the path-casing capability of a filesystem provider changes at runtime.
The provider capability-change listener correctly recomputes the new ignorePathCasing value for the affected URI scheme. However, it compares the newly computed value to itself rather than comparing the previous cached value with the new value.
Because this condition is always true, the listener returns before removing stale entries from _canonicalUris.
As a result, direct URI comparisons can correctly reflect the updated case-sensitivity behavior while asCanonicalUri() continues returning canonical URI values that were cached under the previous casing semantics.
Affected Component
- File:
src/vs/platform/uriIdentity/common/uriIdentityService.ts
- Component:
UriIdentityService
- Affected behavior: canonical URI cache invalidation after filesystem provider capability changes
Root Cause
The filesystem provider registration/capability-change listener contains the following condition:
if (newIgnorePathCasingValue === newIgnorePathCasingValue) {
return;
}
This comparison is tautological and always evaluates to true.
The intended behavior appears to be comparing the previously cached path-casing value with the newly computed value:
if (oldIgnorePathCasingValue === newIgnorePathCasingValue) {
return;
}
If the provider's path-casing behavior has not changed, returning early is appropriate. If the behavior has changed, canonical URI cache entries for the affected scheme should be removed.
Because the existing condition always returns, that invalidation step is never reached.
Steps to Reproduce
-
Create or register a filesystem scheme whose paths are initially treated as case-insensitive.
-
Canonicalize the following URIs:
const upper = URI.parse('foo://bar/BANG');
const lower = URI.parse('foo://bar/bang');
- Call
asCanonicalUri() for both URIs while the scheme is case-insensitive.
The lowercase URI is canonicalized to the previously cached uppercase URI:
- Change the filesystem provider capability for the
foo scheme to:
FileSystemProviderCapabilities.PathCaseSensitive
-
Emit the corresponding filesystem provider capability-change event.
-
Verify direct URI comparison:
service.extUri.isEqual(upper, lower)
This correctly changes from true to false, showing that the current path-casing capability is being used for direct comparisons.
- Canonicalize the lowercase URI again:
service.asCanonicalUri(lower)
Expected Behavior
After the filesystem provider changes from case-insensitive to case-sensitive:
foo://bar/BANG and foo://bar/bang should no longer compare as equal.
- Cached canonical URI entries created under the previous case-insensitive rules should be invalidated.
asCanonicalUri(foo://bar/bang) should return:
Actual Behavior
Direct URI comparison correctly reflects the new case-sensitive behavior:
service.extUri.isEqual(upper, lower) === false
However, asCanonicalUri(lower) continues returning the stale canonical URI cached under the previous case-insensitive rules:
This produces inconsistent behavior between direct URI identity comparison and canonical URI lookup.
Observed Regression Test Failure
A focused regression test reproduces the stale-cache behavior on the current implementation:
URI Identity
asCanonicalUri clears cache when provider path casing changes:
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected
+ 'foo://bar/BANG'
- 'foo://bar/bang'
Proposed Fix
Compare the previous path-casing value with the newly computed value:
- if (newIgnorePathCasingValue === newIgnorePathCasingValue) {
+ if (oldIgnorePathCasingValue === newIgnorePathCasingValue) {
return;
}
This allows the existing cache invalidation logic to run when the filesystem provider's path-casing capability actually changes.
Regression Test
I prepared a deterministic regression test in:
src/vs/platform/uriIdentity/test/common/uriIdentityService.test.ts
The test:
- caches
foo://bar/BANG while the foo scheme is case-insensitive;
- confirms
foo://bar/BANG and foo://bar/bang are initially treated as equal;
- changes the provider to
PathCaseSensitive;
- emits the provider capability-change event;
- verifies direct URI comparison becomes case-sensitive;
- verifies
asCanonicalUri(foo://bar/bang) no longer returns the stale uppercase URI.
The test fails before the fix and passes after the proposed one-line change.
Validation
The proposed fix has been validated with:
Result:
compile-src ... with 0 errors
Focused regression test:
.\scripts\test.bat --grep "asCanonicalUri clears cache when provider path casing changes"
Result:
URI Identity test suite:
.\scripts\test.bat --grep "URI Identity"
Result:
Whitespace validation:
Result:
Impact
This is a functional cache invalidation bug.
When a filesystem provider changes its path-casing semantics after canonical URI entries have already been cached, asCanonicalUri() can return stale URI values based on the previous case-sensitivity rules.
This may result in inconsistent canonicalization, deduplication, or identity behavior for providers that support runtime changes to PathCaseSensitive.
I did not identify a demonstrated security impact or Workspace Trust boundary violation.
Additional Notes
The issue was identified during a focused review of URI identity and path-casing behavior.
I have prepared a minimal fix and a focused regression test and will shortly open a pull request referencing this issue.
Does this issue occur when all extensions are disabled?: Yes
mainbranch, built from source (1.132.0)Summary
UriIdentityServicedoes not invalidate cached canonical URIs when the path-casing capability of a filesystem provider changes at runtime.The provider capability-change listener correctly recomputes the new
ignorePathCasingvalue for the affected URI scheme. However, it compares the newly computed value to itself rather than comparing the previous cached value with the new value.Because this condition is always true, the listener returns before removing stale entries from
_canonicalUris.As a result, direct URI comparisons can correctly reflect the updated case-sensitivity behavior while
asCanonicalUri()continues returning canonical URI values that were cached under the previous casing semantics.Affected Component
src/vs/platform/uriIdentity/common/uriIdentityService.tsUriIdentityServiceRoot Cause
The filesystem provider registration/capability-change listener contains the following condition:
This comparison is tautological and always evaluates to
true.The intended behavior appears to be comparing the previously cached path-casing value with the newly computed value:
If the provider's path-casing behavior has not changed, returning early is appropriate. If the behavior has changed, canonical URI cache entries for the affected scheme should be removed.
Because the existing condition always returns, that invalidation step is never reached.
Steps to Reproduce
Create or register a filesystem scheme whose paths are initially treated as case-insensitive.
Canonicalize the following URIs:
asCanonicalUri()for both URIs while the scheme is case-insensitive.The lowercase URI is canonicalized to the previously cached uppercase URI:
fooscheme to:Emit the corresponding filesystem provider capability-change event.
Verify direct URI comparison:
This correctly changes from
truetofalse, showing that the current path-casing capability is being used for direct comparisons.Expected Behavior
After the filesystem provider changes from case-insensitive to case-sensitive:
foo://bar/BANGandfoo://bar/bangshould no longer compare as equal.asCanonicalUri(foo://bar/bang)should return:Actual Behavior
Direct URI comparison correctly reflects the new case-sensitive behavior:
However,
asCanonicalUri(lower)continues returning the stale canonical URI cached under the previous case-insensitive rules:This produces inconsistent behavior between direct URI identity comparison and canonical URI lookup.
Observed Regression Test Failure
A focused regression test reproduces the stale-cache behavior on the current implementation:
Proposed Fix
Compare the previous path-casing value with the newly computed value:
This allows the existing cache invalidation logic to run when the filesystem provider's path-casing capability actually changes.
Regression Test
I prepared a deterministic regression test in:
The test:
foo://bar/BANGwhile thefooscheme is case-insensitive;foo://bar/BANGandfoo://bar/bangare initially treated as equal;PathCaseSensitive;asCanonicalUri(foo://bar/bang)no longer returns the stale uppercase URI.The test fails before the fix and passes after the proposed one-line change.
Validation
The proposed fix has been validated with:
Result:
Focused regression test:
Result:
URI Identity test suite:
Result:
Whitespace validation:
Result:
Impact
This is a functional cache invalidation bug.
When a filesystem provider changes its path-casing semantics after canonical URI entries have already been cached,
asCanonicalUri()can return stale URI values based on the previous case-sensitivity rules.This may result in inconsistent canonicalization, deduplication, or identity behavior for providers that support runtime changes to
PathCaseSensitive.I did not identify a demonstrated security impact or Workspace Trust boundary violation.
Additional Notes
The issue was identified during a focused review of URI identity and path-casing behavior.
I have prepared a minimal fix and a focused regression test and will shortly open a pull request referencing this issue.