Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export type Scalars = {
ActionAuditID: { input: any; output: any; }
/** The ID for a Address. */
AddressID: { input: any; output: any; }
/** The ID for a Attestation. */
AttestationID: { input: any; output: any; }
/** The ID for a BulkDataOperation. */
BulkDataOperationID: { input: any; output: any; }
/** The ID for a BusinessUser. */
Expand Down
74 changes: 69 additions & 5 deletions packages/cli-kit/src/public/node/git.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('downloadRepository()', async () => {

await git.downloadGitRepository({repoUrl, destination})

expect(mockedExeca).toHaveBeenCalledWith('git', ['clone', '--recurse-submodules', repoUrl, destination])
expect(mockedExeca).toHaveBeenCalledWith('git', ['clone', '--recurse-submodules', '--', repoUrl, destination])
})

test('calls git clone with branch', async () => {
Expand All @@ -50,11 +50,21 @@ describe('downloadRepository()', async () => {
'--recurse-submodules',
'--branch',
'my-branch',
'--',
'http://repoUrl',
destination,
])
})

test('calls git clone with flag-like repository URL', async () => {
const repoUrl = '-repoUrl'
const destination = 'destination'

await git.downloadGitRepository({repoUrl, destination})

expect(mockedExeca).toHaveBeenCalledWith('git', ['clone', '--recurse-submodules', '--', repoUrl, destination])
})

test('fails when the shallow and latestTag properties are passed', async () => {
await expect(async () => {
const repoUrl = 'http://repoUrl'
Expand Down Expand Up @@ -109,8 +119,27 @@ describe('downloadRepository()', async () => {

await git.downloadGitRepository({repoUrl, destination, latestTag})

expect(mockedExeca).toHaveBeenCalledWith('git', ['clone', '--recurse-submodules', repoUrl, destination])
expect(mockedExeca).toHaveBeenCalledWith('git', ['checkout', '1.2.3'], {cwd: destination})
expect(mockedExeca).toHaveBeenCalledWith('git', ['clone', '--recurse-submodules', '--', repoUrl, destination])
expect(mockedExeca).toHaveBeenCalledWith('git', ['checkout', 'refs/tags/1.2.3'], {cwd: destination})
})

test('clones and checks out a flag-like tag', async () => {
mockGitCommandSequence([
// clone
{stdout: ''},
// describe --tags --abbrev=0
{stdout: '--flag-like-tag'},
// checkout
{stdout: ''},
])

const repoUrl = 'http://repoUrl'
const destination = 'destination'
const latestTag = true

await git.downloadGitRepository({repoUrl, destination, latestTag})

expect(mockedExeca).toHaveBeenCalledWith('git', ['checkout', 'refs/tags/--flag-like-tag'], {cwd: destination})
})

test('throws when destination exists as a file', async () => {
Expand Down Expand Up @@ -159,7 +188,7 @@ describe('downloadRepository()', async () => {

await git.downloadGitRepository({repoUrl, destination})

expect(mockedExeca).toHaveBeenCalledWith('git', ['clone', '--recurse-submodules', repoUrl, destination])
expect(mockedExeca).toHaveBeenCalledWith('git', ['clone', '--recurse-submodules', '--', repoUrl, destination])
})
})

Expand All @@ -170,7 +199,7 @@ describe('downloadRepository()', async () => {

await git.downloadGitRepository({repoUrl, destination})

expect(mockedExeca).toHaveBeenCalledWith('git', ['clone', '--recurse-submodules', repoUrl, destination])
expect(mockedExeca).toHaveBeenCalledWith('git', ['clone', '--recurse-submodules', '--', repoUrl, destination])
})
})
})
Expand All @@ -186,6 +215,41 @@ describe('initializeRepository()', () => {
})
})

describe('checkIfIgnoredInGitRepository()', () => {
test('calls git check-ignore with -- and the list of files', async () => {
const directory = '/tmp/git-repo'
const files = ['file1', 'file2']
mockGitCommand('file1\n')

const result = await git.checkIfIgnoredInGitRepository(directory, files)

expect(mockedExeca).toHaveBeenCalledWith('git', ['check-ignore', '--', 'file1', 'file2'], {cwd: directory})
expect(result).toEqual(['file1'])
})

test('calls git check-ignore with flag-like file paths', async () => {
const directory = '/tmp/git-repo'
const files = ['-file1', '--file2']
mockGitCommand('-file1\n')

const result = await git.checkIfIgnoredInGitRepository(directory, files)

expect(mockedExeca).toHaveBeenCalledWith('git', ['check-ignore', '--', '-file1', '--file2'], {cwd: directory})
expect(result).toEqual(['-file1'])
})

test('returns an empty array when no files are ignored', async () => {
const directory = '/tmp/git-repo'
const files = ['file1']
const error = Object.assign(new Error('not ignored'), {exitCode: 1})
mockedExeca.mockRejectedValue(error)

const result = await git.checkIfIgnoredInGitRepository(directory, files)

expect(result).toEqual([])
})
})

describe('createGitIgnore()', () => {
test('writes to a file in the provided directory', async () => {
await inTemporaryDirectory(async (tmpDir) => {
Expand Down
9 changes: 6 additions & 3 deletions packages/cli-kit/src/public/node/git.ts
Comment thread
gonzaloriestra marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ export async function initializeGitRepository(directory: string, initialBranch =
*/
export async function checkIfIgnoredInGitRepository(directory: string, files: string[]): Promise<string[]> {
try {
const stdout = await gitCommand(['check-ignore', ...files], directory)
// Security: Use -- to separate flags from positional arguments (file paths)
const stdout = await gitCommand(['check-ignore', '--', ...files], directory)
return stdout.split('\n').filter(Boolean)
} catch (error) {
// git check-ignore exits with code 1 when no files are ignored
Expand Down Expand Up @@ -209,14 +210,16 @@ export async function downloadGitRepository(cloneOptions: GitCloneOptions): Prom
if (!isTerminalInteractive()) {
args.push('-c', 'core.askpass=true')
}
args.push(repository!, destination)
// Security: Use -- to separate flags from positional arguments (repository URL and destination)
args.push('--', repository!, destination)

try {
await execa('git', args)

if (latestTag) {
const tag = await getLatestTagFromDirectory(destination, repoUrl)
await gitCommand(['checkout', tag], destination)
// Security: Use refs/tags/ prefix to ensure the tag is treated as a revision and not a flag
await gitCommand(['checkout', `refs/tags/${tag}`], destination)
}
} catch (err) {
if (err instanceof AbortError) {
Expand Down
Loading