|
| 1 | +import fs from 'node:fs/promises'; |
| 2 | +import os from 'node:os'; |
| 3 | +import path from 'node:path'; |
1 | 4 | import { beforeEach, describe, expect, test, vi } from 'vitest'; |
2 | 5 | import { |
3 | 6 | execGitDiff, |
@@ -126,6 +129,28 @@ file2.ts |
126 | 129 | }); |
127 | 130 |
|
128 | 131 | describe('execGitShallowClone', () => { |
| 132 | + // The .git removal is load-bearing for safety, not just tidiness: packing runs |
| 133 | + // `git log` inside the clone by default and git honors that repository's own |
| 134 | + // .git/config, so a retained .git would be a host command-execution vector. |
| 135 | + // Unlike the sibling tests this one touches a real directory, because the |
| 136 | + // guarantee is about what is left on disk rather than which git args ran. |
| 137 | + test('removes the clone .git so a cloned repository cannot supply git config', async () => { |
| 138 | + const mockFileExecAsync = vi.fn().mockResolvedValue({ stdout: '', stderr: '' }); |
| 139 | + const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'repomix-clone-git-')); |
| 140 | + await fs.mkdir(path.join(directory, '.git'), { recursive: true }); |
| 141 | + await fs.writeFile(path.join(directory, '.git', 'config'), '[log]\n\tshowSignature = true\n'); |
| 142 | + |
| 143 | + try { |
| 144 | + await execGitShallowClone('https://github.com/user/repo.git', directory, undefined, { |
| 145 | + execFileAsync: mockFileExecAsync, |
| 146 | + }); |
| 147 | + |
| 148 | + await expect(fs.access(path.join(directory, '.git'))).rejects.toThrow(); |
| 149 | + } finally { |
| 150 | + await fs.rm(directory, { recursive: true, force: true }); |
| 151 | + } |
| 152 | + }); |
| 153 | + |
129 | 154 | test('should execute without branch option if not specified by user', async () => { |
130 | 155 | const mockFileExecAsync = vi.fn().mockResolvedValue({ stdout: '', stderr: '' }); |
131 | 156 | const url = 'https://github.com/user/repo.git'; |
|
0 commit comments