From 0459c8418bc1db2094dbdf5167705c6c6a1e8e47 Mon Sep 17 00:00:00 2001 From: SRnChito <95471816+ICEY16360@users.noreply.github.com> Date: Tue, 8 Sep 2026 01:26:38 +0800 Subject: [PATCH] fix(git): pass allowUnsafeCredentialHelper for token identity switching (#3383) simple-git 3.35/3.36 moved its unsafe-config blocklist into @simple-git/argv-parser and expanded it to include credential.helper. setLocalIdentity already opted in for the SSH branch (core.sshCommand) via createGit({ allowUnsafeSshCommand: true }), but the token branch (addConfig("credential.helper", "store")) was left without the matching opt-in, so switching to a token-auth identity throws: Configuring credential.helper is not permitted without enabling allowUnsafeCredentialHelper Forward a new allowUnsafeCredentialHelper option through createGit and enable it in setLocalIdentity alongside the existing SSH opt-in. Cover the token branch (and the token -> SSH cleanup) with tests mirroring the existing SSH case. --- packages/web/server/lib/git/service.js | 9 +++--- packages/web/server/lib/git/service.test.js | 35 +++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/packages/web/server/lib/git/service.js b/packages/web/server/lib/git/service.js index e676b9eb..1f6b80c3 100644 --- a/packages/web/server/lib/git/service.js +++ b/packages/web/server/lib/git/service.js @@ -352,7 +352,7 @@ const buildGitEnv = async () => { return env; }; -const createGit = async (directory, { allowUnsafeSshCommand = false } = {}) => { +const createGit = async (directory, { allowUnsafeSshCommand = false, allowUnsafeCredentialHelper = false } = {}) => { const env = await buildGitEnv(); const spawnOptions = { windowsHide: true }; const binary = getGitBinary(); @@ -360,8 +360,9 @@ const createGit = async (directory, { allowUnsafeSshCommand = false } = {}) => { const unsafe = hasCustomBinary || allowUnsafeSshCommand ? { ...(hasCustomBinary && { allowUnsafeCustomBinary: true }), - ...(allowUnsafeSshCommand && { allowUnsafeSshCommand: true }), - } + ...(allowUnsafeSshCommand && { allowUnsafeSshCommand: true }), + ...(allowUnsafeCredentialHelper && { allowUnsafeCredentialHelper: true }), + } : undefined; // Always pin simple-git to an explicit working directory. Omitting baseDir // makes simple-git use process.cwd(), which breaks when the OpenChamber @@ -2146,7 +2147,7 @@ export async function hasLocalIdentity(directory) { } export async function setLocalIdentity(directory, profile) { - const git = await createGit(directory, { allowUnsafeSshCommand: true }); + const git = await createGit(directory, { allowUnsafeSshCommand: true, allowUnsafeCredentialHelper: true }); try { diff --git a/packages/web/server/lib/git/service.test.js b/packages/web/server/lib/git/service.test.js index 1b742704..db2cc418 100644 --- a/packages/web/server/lib/git/service.test.js +++ b/packages/web/server/lib/git/service.test.js @@ -183,6 +183,41 @@ describe.runIf(canRunGit())('setLocalIdentity', () => { "ssh -i '/tmp/test key' -o IdentitiesOnly=yes" ); }); + + it('configures the stored credential helper for token auth with the targeted simple-git opt-in', async () => { + const { tmpDir } = await createTempRepo(); + + await setLocalIdentity(tmpDir, { + userName: 'Token User', + userEmail: 'token@example.com', + authType: 'token', + host: 'github.com', + }); + + expect(runGit(tmpDir, ['config', '--local', '--get', 'credential.helper']).trim()).toBe('store'); + }); + + it('clears the stored credential helper when switching to SSH auth', async () => { + const { tmpDir } = await createTempRepo(); + + await setLocalIdentity(tmpDir, { + userName: 'Token User', + userEmail: 'token@example.com', + authType: 'token', + host: 'github.com', + }); + await setLocalIdentity(tmpDir, { + userName: 'SSH User', + userEmail: 'ssh@example.com', + authType: 'ssh', + sshKey: '/tmp/test key', + }); + + expect(runGit(tmpDir, ['config', '--local', '--get', 'core.sshCommand']).trim()).toBe( + "ssh -i '/tmp/test key' -o IdentitiesOnly=yes" + ); + expect(() => runGit(tmpDir, ['config', '--local', '--get', 'credential.helper'])).toThrow(); + }); }); // ---------------------------------------------------------------------------