Fix configuration path regex escaping (#14674)

This commit is contained in:
Sean McManus
2026-08-13 12:11:18 -07:00
committed by GitHub
parent 7a7c1f421b
commit 98055e0aa8
3 changed files with 44 additions and 4 deletions
@@ -14,6 +14,7 @@ import * as vscode from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
import * as which from 'which'; import * as which from 'which';
import { logAndReturn, returns } from '../Utility/Async/returns'; import { logAndReturn, returns } from '../Utility/Async/returns';
import { escapePathForSquiggles } from '../Utility/Text/escape';
import * as util from '../common'; import * as util from '../common';
import { isWindows } from '../constants'; import { isWindows } from '../constants';
import { getOutputChannelLogger } from '../logger'; import { getOutputChannelLogger } from '../logger';
@@ -2119,10 +2120,8 @@ export class CppProperties {
continue; continue;
} }
// Escape the path string for literal use in a regular expression // Escape the parsed path for a literal regex match against its JSON spelling.
// Need to escape any quotes to match the original text const escapedPath: string = escapePathForSquiggles(curPath);
let escapedPath: string = curPath.replace(/"/g, '\\"');
escapedPath = escapedPath.replace(/[-\"\/\\^$*+?.()|[\]{}]/g, '\\$&');
// Create a pattern to search for the path with either a quote or semicolon immediately before and after, // Create a pattern to search for the path with either a quote or semicolon immediately before and after,
// and extend that pattern to the next quote before and next quote after it. // and extend that pattern to the next quote before and next quote after it.
+9
View File
@@ -0,0 +1,9 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved.
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
export function escapePathForSquiggles(s: string): string {
return s.replace(/[-"\/\\^$*+?.()|[\]{}]/g, (character: string): string =>
character === '"' ? '\\\\"' : `\\${character}`);
}
+32
View File
@@ -0,0 +1,32 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved.
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import { describe, it } from 'mocha';
import { doesNotMatch, match, strictEqual } from 'node:assert';
import { escapePathForSquiggles } from '../../src/Utility/Text/escape';
describe('Text escaping', () => {
it('escapes paths for matching their JSON spelling', () => {
strictEqual(escapePathForSquiggles('"'), '\\\\"');
strictEqual(escapePathForSquiggles('\\'), '\\\\');
strictEqual(escapePathForSquiggles('.*'), '\\.\\*');
const parsedPath: string = String.raw`C:\src"quoted"`;
const sourcePath: string = String.raw`C:\src\"quoted\"`;
const pattern: RegExp = new RegExp(`^${escapePathForSquiggles(parsedPath)}$`);
match(sourcePath, pattern);
doesNotMatch(parsedPath, pattern);
});
it('handles repeated backslashes and regex metacharacters', () => {
const parsedPath: string = String.raw`C:\\sdk\\[headers]+(x)?.h\\say"hello"and"goodbye`;
const sourcePath: string = String.raw`C:\\sdk\\[headers]+(x)?.h\\say\"hello\"and\"goodbye`;
const pattern: RegExp = new RegExp(`^${escapePathForSquiggles(parsedPath)}$`);
match(sourcePath, pattern);
doesNotMatch(String.raw`C:\sdk\[headers]+(x)?.h\say\"hello\"and\"goodbye`, pattern);
doesNotMatch(String.raw`C:\\sdk\\headers+(x)?.h\\say\"hello\"and\"goodbye`, pattern);
});
});