Escape single quotes in submodule Foreach shell commands

This commit is contained in:
Juwan-Hwang 2026-08-18 10:44:10 +08:00
commit 3a65af85aa
5 changed files with 143 additions and 6 deletions

27
dist/index.js vendored
View file

@ -35021,6 +35021,26 @@ function hasContent(text, whitespaceMode) {
return refinedText.length > 0;
}
;// CONCATENATED MODULE: ./src/shell-escape.ts
/**
* Escapes a value for safe use inside a single-quoted shell string.
*
* In POSIX shells, single-quoted strings treat every character literally
* except for the single quote itself (there is no escape sequence inside
* single quotes). The standard technique is to:
* 1. Close the current single-quoted segment: '
* 2. Add an escaped single quote: \'
* 3. Re-open a new single-quoted segment: '
*
* Example: "it's" "it'\''s"
*
* This prevents shell injection when interpolating values into commands
* executed via `sh -c` (e.g. `git submodule foreach`).
*/
function escapeSingleQuote(value) {
return value.replace(/'/g, "'\\''");
}
;// CONCATENATED MODULE: ./src/git-auth-helper.ts
@ -35033,6 +35053,7 @@ function hasContent(text, whitespaceMode) {
const git_auth_helper_IS_WINDOWS = process.platform === 'win32';
const SSH_COMMAND_KEY = 'core.sshCommand';
function createAuthHelper(git, settings) {
@ -35167,12 +35188,12 @@ class GitAuthHelper {
}
if (this.settings.sshKey) {
// Configure core.sshCommand
await this.git.submoduleForeach(`git config --local '${SSH_COMMAND_KEY}' '${this.sshCommand}'`, this.settings.nestedSubmodules);
await this.git.submoduleForeach(`git config --local '${SSH_COMMAND_KEY}' '${escapeSingleQuote(this.sshCommand)}'`, this.settings.nestedSubmodules);
}
else {
// Configure HTTPS instead of SSH
for (const insteadOfValue of this.insteadOfValues) {
await this.git.submoduleForeach(`git config --local --add '${this.insteadOfKey}' '${insteadOfValue}'`, this.settings.nestedSubmodules);
await this.git.submoduleForeach(`git config --local --add '${escapeSingleQuote(this.insteadOfKey)}' '${escapeSingleQuote(insteadOfValue)}'`, this.settings.nestedSubmodules);
}
}
}
@ -35414,7 +35435,7 @@ class GitAuthHelper {
const pattern = regexp_helper_escape(configKey);
await this.git.submoduleForeach(
// Wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline.
`sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`, true);
`sh -c "git config --local --name-only --get-regexp '${escapeSingleQuote(pattern)}' && git config --local --unset-all '${escapeSingleQuote(configKey)}' || :"`, true);
}
/**
* Removes includeIf entries that point to git-credentials-*.config files.