fix(git): support secure SSH config
This commit is contained in:
@@ -55,7 +55,7 @@
|
|||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
"simple-git": "^3.28.0",
|
"simple-git": "^3.36.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"yaml": "^2.8.1",
|
"yaml": "^2.8.1",
|
||||||
@@ -200,7 +200,7 @@
|
|||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"remend": "^1.2.1",
|
"remend": "^1.2.1",
|
||||||
"shiki": "^3.23.0",
|
"shiki": "^3.23.0",
|
||||||
"simple-git": "^3.28.0",
|
"simple-git": "^3.36.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"strip-json-comments": "^5.0.3",
|
"strip-json-comments": "^5.0.3",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
@@ -284,7 +284,7 @@
|
|||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"sherpa-onnx-node": "1.12.28",
|
"sherpa-onnx-node": "1.12.28",
|
||||||
"simple-git": "^3.28.0",
|
"simple-git": "^3.36.0",
|
||||||
"web-push": "^3.6.7",
|
"web-push": "^3.6.7",
|
||||||
"ws": "^8.18.3",
|
"ws": "^8.18.3",
|
||||||
"yaml": "^2.8.1",
|
"yaml": "^2.8.1",
|
||||||
@@ -1258,6 +1258,10 @@
|
|||||||
|
|
||||||
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||||
|
|
||||||
|
"@simple-git/args-pathspec": ["@simple-git/args-pathspec@1.0.3", "", {}, "sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA=="],
|
||||||
|
|
||||||
|
"@simple-git/argv-parser": ["@simple-git/argv-parser@1.1.1", "", { "dependencies": { "@simple-git/args-pathspec": "^1.0.3" } }, "sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw=="],
|
||||||
|
|
||||||
"@simplewebauthn/browser": ["@simplewebauthn/browser@13.3.0", "", {}, "sha512-BE/UWv6FOToAdVk0EokzkqQQDOWtNydYlY6+OrmiZ5SCNmb41VehttboTetUM3T/fr6EAFYVXjz4My2wg230rQ=="],
|
"@simplewebauthn/browser": ["@simplewebauthn/browser@13.3.0", "", {}, "sha512-BE/UWv6FOToAdVk0EokzkqQQDOWtNydYlY6+OrmiZ5SCNmb41VehttboTetUM3T/fr6EAFYVXjz4My2wg230rQ=="],
|
||||||
|
|
||||||
"@simplewebauthn/server": ["@simplewebauthn/server@13.3.1", "", { "dependencies": { "@hexagon/base64": "^1.1.27", "@levischuck/tiny-cbor": "^0.2.2", "@peculiar/asn1-android": "^2.6.0", "@peculiar/asn1-ecc": "^2.6.1", "@peculiar/asn1-rsa": "^2.6.1", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.1", "@peculiar/x509": "^1.14.3" } }, "sha512-GV/oM/qeycWn8p42JZIMJBsXWQcNFg+nJFzeQTnMA4gN8mXg0+HZFWJerHg8ZN/zlveMS3iV1wzuFpOVWS/46w=="],
|
"@simplewebauthn/server": ["@simplewebauthn/server@13.3.1", "", { "dependencies": { "@hexagon/base64": "^1.1.27", "@levischuck/tiny-cbor": "^0.2.2", "@peculiar/asn1-android": "^2.6.0", "@peculiar/asn1-ecc": "^2.6.1", "@peculiar/asn1-rsa": "^2.6.1", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.1", "@peculiar/x509": "^1.14.3" } }, "sha512-GV/oM/qeycWn8p42JZIMJBsXWQcNFg+nJFzeQTnMA4gN8mXg0+HZFWJerHg8ZN/zlveMS3iV1wzuFpOVWS/46w=="],
|
||||||
@@ -3000,7 +3004,7 @@
|
|||||||
|
|
||||||
"simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
|
"simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
|
||||||
|
|
||||||
"simple-git": ["simple-git@3.32.3", "", { "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", "debug": "^4.4.0" } }, "sha512-56a5oxFdWlsGygOXHWrG+xjj5w9ZIt2uQbzqiIGdR/6i5iococ7WQ/bNPzWxCJdEUGUCmyMH0t9zMpRJTaKxmw=="],
|
"simple-git": ["simple-git@3.36.0", "", { "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", "@simple-git/args-pathspec": "^1.0.3", "@simple-git/argv-parser": "^1.1.0", "debug": "^4.4.0" } }, "sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q=="],
|
||||||
|
|
||||||
"simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="],
|
"simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="],
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -134,7 +134,7 @@
|
|||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
"simple-git": "^3.28.0",
|
"simple-git": "^3.36.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"yaml": "^2.8.1",
|
"yaml": "^2.8.1",
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"remend": "^1.2.1",
|
"remend": "^1.2.1",
|
||||||
"shiki": "^3.23.0",
|
"shiki": "^3.23.0",
|
||||||
"simple-git": "^3.28.0",
|
"simple-git": "^3.36.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"strip-json-comments": "^5.0.3",
|
"strip-json-comments": "^5.0.3",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"sherpa-onnx-node": "1.12.28",
|
"sherpa-onnx-node": "1.12.28",
|
||||||
"simple-git": "^3.28.0",
|
"simple-git": "^3.36.0",
|
||||||
"web-push": "^3.6.7",
|
"web-push": "^3.6.7",
|
||||||
"ws": "^8.18.3",
|
"ws": "^8.18.3",
|
||||||
"yaml": "^2.8.1"
|
"yaml": "^2.8.1"
|
||||||
|
|||||||
@@ -351,12 +351,17 @@ const buildGitEnv = async () => {
|
|||||||
return env;
|
return env;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createGit = async (directory) => {
|
const createGit = async (directory, { allowUnsafeSshCommand = false } = {}) => {
|
||||||
const env = await buildGitEnv();
|
const env = await buildGitEnv();
|
||||||
const spawnOptions = { windowsHide: true };
|
const spawnOptions = { windowsHide: true };
|
||||||
const binary = getGitBinary();
|
const binary = getGitBinary();
|
||||||
const hasCustomBinary = typeof binary === 'string' && binary.trim() && binary !== 'git' && binary !== 'git.exe';
|
const hasCustomBinary = typeof binary === 'string' && binary.trim() && binary !== 'git' && binary !== 'git.exe';
|
||||||
const unsafe = hasCustomBinary ? { allowUnsafeCustomBinary: true } : undefined;
|
const unsafe = hasCustomBinary || allowUnsafeSshCommand
|
||||||
|
? {
|
||||||
|
...(hasCustomBinary && { allowUnsafeCustomBinary: true }),
|
||||||
|
...(allowUnsafeSshCommand && { allowUnsafeSshCommand: true }),
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
// Always pin simple-git to an explicit working directory. Omitting baseDir
|
// Always pin simple-git to an explicit working directory. Omitting baseDir
|
||||||
// makes simple-git use process.cwd(), which breaks when the OpenChamber
|
// makes simple-git use process.cwd(), which breaks when the OpenChamber
|
||||||
// server was launched from a neutral directory (e.g. $HOME) and the opened
|
// server was launched from a neutral directory (e.g. $HOME) and the opened
|
||||||
@@ -2047,7 +2052,7 @@ export async function hasLocalIdentity(directory) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function setLocalIdentity(directory, profile) {
|
export async function setLocalIdentity(directory, profile) {
|
||||||
const git = await createGit(directory);
|
const git = await createGit(directory, { allowUnsafeSshCommand: true });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@@ -2057,12 +2062,12 @@ export async function setLocalIdentity(directory, profile) {
|
|||||||
const authType = profile.authType || 'ssh';
|
const authType = profile.authType || 'ssh';
|
||||||
|
|
||||||
if (authType === 'ssh' && profile.sshKey) {
|
if (authType === 'ssh' && profile.sshKey) {
|
||||||
await git.addConfig(
|
await git.raw([
|
||||||
|
'config',
|
||||||
|
'--local',
|
||||||
'core.sshCommand',
|
'core.sshCommand',
|
||||||
buildSshCommand(profile.sshKey),
|
buildSshCommand(profile.sshKey)
|
||||||
false,
|
]);
|
||||||
'local'
|
|
||||||
);
|
|
||||||
await git.raw(['config', '--local', '--unset', 'credential.helper']).catch(() => {});
|
await git.raw(['config', '--local', '--unset', 'credential.helper']).catch(() => {});
|
||||||
} else if (authType === 'token' && profile.host) {
|
} else if (authType === 'token' && profile.host) {
|
||||||
await git.addConfig(
|
await git.addConfig(
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
resetToCommit,
|
resetToCommit,
|
||||||
resolveBaseRefForLog,
|
resolveBaseRefForLog,
|
||||||
revertCommit,
|
revertCommit,
|
||||||
|
setLocalIdentity,
|
||||||
stageFiles,
|
stageFiles,
|
||||||
unstageFiles,
|
unstageFiles,
|
||||||
applyHunk,
|
applyHunk,
|
||||||
@@ -129,6 +130,23 @@ describe('git index path validation', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe.runIf(canRunGit())('setLocalIdentity', () => {
|
||||||
|
it('configures the local SSH command with the targeted simple-git opt-in', async () => {
|
||||||
|
const { tmpDir } = await createTempRepo();
|
||||||
|
|
||||||
|
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"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// applyHunk (per-hunk stage / unstage / discard)
|
// applyHunk (per-hunk stage / unstage / discard)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user