From a980fe4c44f4f940f28e4f794ef51a92d9fb3247 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 29 Nov 2023 11:59:54 -0800 Subject: [PATCH 1/6] Move all replacements of star prefix out to common function --- src/compiler/moduleNameResolver.ts | 7 ++++--- src/compiler/moduleSpecifiers.ts | 7 ++++--- src/compiler/utilities.ts | 10 ++++++++++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 6b30c540c4241..67f821b09213b 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -89,6 +89,7 @@ import { removeExtension, removeFileExtension, removePrefix, + replaceFirstStar, ResolutionMode, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, @@ -2286,8 +2287,8 @@ function loadEntrypointsFromExportMap( /*excludes*/ undefined, [ isDeclarationFileName(target) - ? target.replace("*", "**/*") - : changeAnyExtension(target.replace("*", "**/*"), getDeclarationEmitExtensionForPath(target)), + ? replaceFirstStar(target, "**/*") + : changeAnyExtension(replaceFirstStar(target, "**/*"), getDeclarationEmitExtensionForPath(target)), ], ).forEach(entry => { entrypoints = appendIfUnique(entrypoints, { @@ -3096,7 +3097,7 @@ function tryLoadModuleUsingPaths(extensions: Extensions, moduleName: string, bas trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText); } const resolved = forEach(paths[matchedPatternText], subst => { - const path = matchedStar ? subst.replace("*", matchedStar) : subst; + const path = matchedStar ? replaceFirstStar(subst, matchedStar) : subst; // When baseUrl is not specified, the command line parser resolves relative paths to the config file location. const candidate = normalizePath(combinePaths(baseDirectory, path)); if (state.traceEnabled) { diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index ab1d439725518..2dddc47d30c69 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -93,6 +93,7 @@ import { removeFileExtension, removeSuffix, removeTrailingDirectorySeparator, + replaceFirstStar, ResolutionMode, resolvePath, ScriptKind, @@ -810,7 +811,7 @@ function tryGetModuleNameFromPaths(relativeToBaseUrl: string, paths: MapLike Date: Wed, 29 Nov 2023 12:02:23 -0800 Subject: [PATCH 2/6] fmt --- src/compiler/utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4947ea9b2ea24..159e24c7b83b0 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -10511,7 +10511,7 @@ export function replaceFirstStar(s: string, replacement: string): string { // This code triggers CodeQL as they think it's a potentially incorrect string escaping. // See: https://codeql.github.com/codeql-query-help/javascript/js-incomplete-sanitization/ // But, we really do want to replace only the first star. - + // TODO(jakebailey): Leaving this here to verify that CodeQL complains. return s.replace("*", replacement); } From 0062550ded18cc28109b3202b9e80c3fb22d8fa4 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:08:48 -0800 Subject: [PATCH 3/6] Try prototype --- src/compiler/utilities.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 159e24c7b83b0..738538d24520e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -10506,12 +10506,13 @@ export function hasResolutionModeOverride(node: ImportTypeNode | ImportDeclarati return !!getResolutionModeOverride(node.attributes); } +const stringReplace = String.prototype.replace; + /** @internal */ export function replaceFirstStar(s: string, replacement: string): string { - // This code triggers CodeQL as they think it's a potentially incorrect string escaping. + // `s.replace("*", replacement)` triggers CodeQL as they think it's a potentially incorrect string escaping. // See: https://codeql.github.com/codeql-query-help/javascript/js-incomplete-sanitization/ // But, we really do want to replace only the first star. - - // TODO(jakebailey): Leaving this here to verify that CodeQL complains. - return s.replace("*", replacement); + // Attempt to defeat this analysis by indirectly calling the method. + return stringReplace.call(s, "*", replacement); } From 7759ed7d73ad1b3d41d19e2515b73a4d489d8d88 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:30:35 -0800 Subject: [PATCH 4/6] Use arrow function in quote --- src/services/utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 5cbde51f82843..bb505008bedb8 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -3393,7 +3393,7 @@ export function quote(sourceFile: SourceFile, preferences: UserPreferences, text // Editors can pass in undefined or empty string - we want to infer the preference in those cases. const quotePreference = getQuotePreference(sourceFile, preferences); const quoted = JSON.stringify(text); - return quotePreference === QuotePreference.Single ? `'${stripQuotes(quoted).replace(/'/g, "\\'").replace(/\\"/g, '"')}'` : quoted; + return quotePreference === QuotePreference.Single ? `'${stripQuotes(quoted).replace(/'/g, () => "\\'").replace(/\\"/g, '"')}'` : quoted; } /** @internal */ From 5b4bd7ca6f694f01d92bd605ef673507e7986d96 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:34:31 -0800 Subject: [PATCH 5/6] Use arrow function --- src/compiler/moduleNameResolver.ts | 7 +++---- src/compiler/moduleSpecifiers.ts | 7 +++---- src/compiler/utilities.ts | 11 ----------- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 67f821b09213b..b726526d516db 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -89,7 +89,6 @@ import { removeExtension, removeFileExtension, removePrefix, - replaceFirstStar, ResolutionMode, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, @@ -2287,8 +2286,8 @@ function loadEntrypointsFromExportMap( /*excludes*/ undefined, [ isDeclarationFileName(target) - ? replaceFirstStar(target, "**/*") - : changeAnyExtension(replaceFirstStar(target, "**/*"), getDeclarationEmitExtensionForPath(target)), + ? target.replace("*", () => "**/*") + : changeAnyExtension(target.replace("*", () => "**/*"), getDeclarationEmitExtensionForPath(target)), ], ).forEach(entry => { entrypoints = appendIfUnique(entrypoints, { @@ -3097,7 +3096,7 @@ function tryLoadModuleUsingPaths(extensions: Extensions, moduleName: string, bas trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText); } const resolved = forEach(paths[matchedPatternText], subst => { - const path = matchedStar ? replaceFirstStar(subst, matchedStar) : subst; + const path = matchedStar ? subst.replace("*", () => matchedStar) : subst; // When baseUrl is not specified, the command line parser resolves relative paths to the config file location. const candidate = normalizePath(combinePaths(baseDirectory, path)); if (state.traceEnabled) { diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index 2dddc47d30c69..ff37d04b2ac0c 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -93,7 +93,6 @@ import { removeFileExtension, removeSuffix, removeTrailingDirectorySeparator, - replaceFirstStar, ResolutionMode, resolvePath, ScriptKind, @@ -811,7 +810,7 @@ function tryGetModuleNameFromPaths(relativeToBaseUrl: string, paths: MapLike matchedStar); } } } @@ -865,11 +864,11 @@ function tryGetModuleNameFromExports(options: CompilerOptions, targetFilePath: s const trailingSlice = pathOrPattern.slice(starPos + 1); if (startsWith(targetFilePath, leadingSlice) && endsWith(targetFilePath, trailingSlice)) { const starReplacement = targetFilePath.slice(leadingSlice.length, targetFilePath.length - trailingSlice.length); - return { moduleFileToTry: replaceFirstStar(packageName, starReplacement) }; + return { moduleFileToTry: packageName.replace("*", () => starReplacement) }; } if (extensionSwappedTarget && startsWith(extensionSwappedTarget, leadingSlice) && endsWith(extensionSwappedTarget, trailingSlice)) { const starReplacement = extensionSwappedTarget.slice(leadingSlice.length, extensionSwappedTarget.length - trailingSlice.length); - return { moduleFileToTry: replaceFirstStar(packageName, starReplacement) }; + return { moduleFileToTry: packageName.replace("*", () => starReplacement) }; } break; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 738538d24520e..64c9b4ee3d009 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -10505,14 +10505,3 @@ export function hasResolutionModeOverride(node: ImportTypeNode | ImportDeclarati } return !!getResolutionModeOverride(node.attributes); } - -const stringReplace = String.prototype.replace; - -/** @internal */ -export function replaceFirstStar(s: string, replacement: string): string { - // `s.replace("*", replacement)` triggers CodeQL as they think it's a potentially incorrect string escaping. - // See: https://codeql.github.com/codeql-query-help/javascript/js-incomplete-sanitization/ - // But, we really do want to replace only the first star. - // Attempt to defeat this analysis by indirectly calling the method. - return stringReplace.call(s, "*", replacement); -} From 793832a1c5a4ccd4d0b1ccae88395eb008002aa8 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:48:09 -0800 Subject: [PATCH 6/6] Revert "Use arrow function" This reverts commit 5b4bd7ca6f694f01d92bd605ef673507e7986d96. --- src/compiler/moduleNameResolver.ts | 7 ++++--- src/compiler/moduleSpecifiers.ts | 7 ++++--- src/compiler/utilities.ts | 11 +++++++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index b726526d516db..67f821b09213b 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -89,6 +89,7 @@ import { removeExtension, removeFileExtension, removePrefix, + replaceFirstStar, ResolutionMode, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, @@ -2286,8 +2287,8 @@ function loadEntrypointsFromExportMap( /*excludes*/ undefined, [ isDeclarationFileName(target) - ? target.replace("*", () => "**/*") - : changeAnyExtension(target.replace("*", () => "**/*"), getDeclarationEmitExtensionForPath(target)), + ? replaceFirstStar(target, "**/*") + : changeAnyExtension(replaceFirstStar(target, "**/*"), getDeclarationEmitExtensionForPath(target)), ], ).forEach(entry => { entrypoints = appendIfUnique(entrypoints, { @@ -3096,7 +3097,7 @@ function tryLoadModuleUsingPaths(extensions: Extensions, moduleName: string, bas trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText); } const resolved = forEach(paths[matchedPatternText], subst => { - const path = matchedStar ? subst.replace("*", () => matchedStar) : subst; + const path = matchedStar ? replaceFirstStar(subst, matchedStar) : subst; // When baseUrl is not specified, the command line parser resolves relative paths to the config file location. const candidate = normalizePath(combinePaths(baseDirectory, path)); if (state.traceEnabled) { diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index ff37d04b2ac0c..2dddc47d30c69 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -93,6 +93,7 @@ import { removeFileExtension, removeSuffix, removeTrailingDirectorySeparator, + replaceFirstStar, ResolutionMode, resolvePath, ScriptKind, @@ -810,7 +811,7 @@ function tryGetModuleNameFromPaths(relativeToBaseUrl: string, paths: MapLike matchedStar); + return replaceFirstStar(key, matchedStar); } } } @@ -864,11 +865,11 @@ function tryGetModuleNameFromExports(options: CompilerOptions, targetFilePath: s const trailingSlice = pathOrPattern.slice(starPos + 1); if (startsWith(targetFilePath, leadingSlice) && endsWith(targetFilePath, trailingSlice)) { const starReplacement = targetFilePath.slice(leadingSlice.length, targetFilePath.length - trailingSlice.length); - return { moduleFileToTry: packageName.replace("*", () => starReplacement) }; + return { moduleFileToTry: replaceFirstStar(packageName, starReplacement) }; } if (extensionSwappedTarget && startsWith(extensionSwappedTarget, leadingSlice) && endsWith(extensionSwappedTarget, trailingSlice)) { const starReplacement = extensionSwappedTarget.slice(leadingSlice.length, extensionSwappedTarget.length - trailingSlice.length); - return { moduleFileToTry: packageName.replace("*", () => starReplacement) }; + return { moduleFileToTry: replaceFirstStar(packageName, starReplacement) }; } break; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 64c9b4ee3d009..738538d24520e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -10505,3 +10505,14 @@ export function hasResolutionModeOverride(node: ImportTypeNode | ImportDeclarati } return !!getResolutionModeOverride(node.attributes); } + +const stringReplace = String.prototype.replace; + +/** @internal */ +export function replaceFirstStar(s: string, replacement: string): string { + // `s.replace("*", replacement)` triggers CodeQL as they think it's a potentially incorrect string escaping. + // See: https://codeql.github.com/codeql-query-help/javascript/js-incomplete-sanitization/ + // But, we really do want to replace only the first star. + // Attempt to defeat this analysis by indirectly calling the method. + return stringReplace.call(s, "*", replacement); +}