mirror of
				https://github.com/actions/checkout.git
				synced 2025-10-31 05:25:55 +08:00 
			
		
		
		
	Compare commits
	
		
			22 Commits
		
	
	
		
			users/eric
			...
			317d15a1e8
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 317d15a1e8 | ||
|  | 1929737c8e | ||
|  | dc519229d3 | ||
|  | c9518fb408 | ||
|  | f8060825ea | ||
|  | 3292e202f3 | ||
|  | e4894fca20 | ||
|  | 2bcd7c6585 | ||
|  | 857facff5c | ||
|  | ff9f98e487 | ||
|  | aa7e6581cb | ||
|  | 6397f22a4f | ||
|  | 762bf756aa | ||
|  | 96c6589494 | ||
|  | 0f2eb6b146 | ||
|  | a60fb6cabe | ||
|  | 8e4be9ae12 | ||
|  | 74fe54f098 | ||
|  | b13eccf351 | ||
|  | 82257b56c2 | ||
|  | d9b320ec70 | ||
|  | bcc5319a0b | 
							
								
								
									
										2
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							| @@ -299,7 +299,7 @@ jobs: | ||||
|   test-output: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       # Clone this repo | ||||
|       # Download the action at the current ref | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v4.1.6 | ||||
|         with: | ||||
|   | ||||
| @@ -706,7 +706,7 @@ describe('git-auth-helper tests', () => { | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|     await authHelper.configureAuth() | ||||
|  | ||||
|     // Verify includeIf entries exist in local config | ||||
|     // Sanity check - verify includeIf entries exist in local config | ||||
|     let localConfigContent = ( | ||||
|       await fs.promises.readFile(localGitConfigPath) | ||||
|     ).toString() | ||||
| @@ -714,192 +714,26 @@ describe('git-auth-helper tests', () => { | ||||
|       localConfigContent.indexOf('includeIf.gitdir:') | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|  | ||||
|     // Verify both host and container includeIf entries are present | ||||
|     const hostGitDir = path.join(workspace, '.git').replace(/\\/g, '/') | ||||
|     expect( | ||||
|       localConfigContent.indexOf(`includeIf.gitdir:${hostGitDir}.path`) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|     expect( | ||||
|       localConfigContent.indexOf('includeIf.gitdir:/github/workspace/.git.path') | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|  | ||||
|     // Verify credentials file exists | ||||
|     // Sanity check - verify credentials file exists | ||||
|     let credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||
|       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||
|     ) | ||||
|     expect(credentialsFiles.length).toBe(1) | ||||
|     const credentialsFilePath = path.join(runnerTemp, credentialsFiles[0]) | ||||
|  | ||||
|     // Verify credentials file contains the auth token | ||||
|     let credentialsContent = ( | ||||
|       await fs.promises.readFile(credentialsFilePath) | ||||
|     ).toString() | ||||
|     const basicCredential = Buffer.from( | ||||
|       `x-access-token:${settings.authToken}`, | ||||
|       'utf8' | ||||
|     ).toString('base64') | ||||
|     expect( | ||||
|       credentialsContent.indexOf( | ||||
|         `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}` | ||||
|       ) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|  | ||||
|     // Verify the includeIf entries point to the credentials file | ||||
|     const containerCredentialsPath = path.posix.join( | ||||
|       '/github/runner_temp', | ||||
|       path.basename(credentialsFilePath) | ||||
|     ) | ||||
|     expect( | ||||
|       localConfigContent.indexOf(credentialsFilePath) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|     expect( | ||||
|       localConfigContent.indexOf(containerCredentialsPath) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|  | ||||
|     // Act | ||||
|     await authHelper.removeAuth() | ||||
|  | ||||
|     // Assert all includeIf entries removed from local git config | ||||
|     // Assert includeIf entries removed from local git config | ||||
|     localConfigContent = ( | ||||
|       await fs.promises.readFile(localGitConfigPath) | ||||
|     ).toString() | ||||
|     expect(localConfigContent.indexOf('includeIf.gitdir:')).toBeLessThan(0) | ||||
|     expect( | ||||
|       localConfigContent.indexOf(`includeIf.gitdir:${hostGitDir}.path`) | ||||
|     ).toBeLessThan(0) | ||||
|     expect( | ||||
|       localConfigContent.indexOf('includeIf.gitdir:/github/workspace/.git.path') | ||||
|     ).toBeLessThan(0) | ||||
|     expect(localConfigContent.indexOf(credentialsFilePath)).toBeLessThan(0) | ||||
|     expect(localConfigContent.indexOf(containerCredentialsPath)).toBeLessThan(0) | ||||
|  | ||||
|     // Assert credentials config file deleted | ||||
|     credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||
|       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||
|     ) | ||||
|     expect(credentialsFiles.length).toBe(0) | ||||
|  | ||||
|     // Verify credentials file no longer exists on disk | ||||
|     try { | ||||
|       await fs.promises.stat(credentialsFilePath) | ||||
|       throw new Error('Credentials file should have been deleted') | ||||
|     } catch (err) { | ||||
|       if ((err as any)?.code !== 'ENOENT') { | ||||
|         throw err | ||||
|       } | ||||
|     } | ||||
|   }) | ||||
|  | ||||
|   const removeAuth_removesTokenFromSubmodules = | ||||
|     'removeAuth removes token from submodules' | ||||
|   it(removeAuth_removesTokenFromSubmodules, async () => { | ||||
|     // Arrange | ||||
|     await setup(removeAuth_removesTokenFromSubmodules) | ||||
|  | ||||
|     // Create fake submodule config paths | ||||
|     const submodule1Dir = path.join(workspace, '.git', 'modules', 'submodule-1') | ||||
|     const submodule2Dir = path.join(workspace, '.git', 'modules', 'submodule-2') | ||||
|     const submodule1ConfigPath = path.join(submodule1Dir, 'config') | ||||
|     const submodule2ConfigPath = path.join(submodule2Dir, 'config') | ||||
|  | ||||
|     await fs.promises.mkdir(submodule1Dir, {recursive: true}) | ||||
|     await fs.promises.mkdir(submodule2Dir, {recursive: true}) | ||||
|     await fs.promises.writeFile(submodule1ConfigPath, '') | ||||
|     await fs.promises.writeFile(submodule2ConfigPath, '') | ||||
|  | ||||
|     // Mock getSubmoduleConfigPaths to return our fake submodules (for both configure and remove) | ||||
|     const mockGetSubmoduleConfigPaths = | ||||
|       git.getSubmoduleConfigPaths as jest.Mock<any, any> | ||||
|     mockGetSubmoduleConfigPaths.mockResolvedValue([ | ||||
|       submodule1ConfigPath, | ||||
|       submodule2ConfigPath | ||||
|     ]) | ||||
|  | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|     await authHelper.configureAuth() | ||||
|     await authHelper.configureSubmoduleAuth() | ||||
|  | ||||
|     // Verify credentials file exists | ||||
|     let credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||
|       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||
|     ) | ||||
|     expect(credentialsFiles.length).toBe(1) | ||||
|     const credentialsFilePath = path.join(runnerTemp, credentialsFiles[0]) | ||||
|  | ||||
|     // Verify submodule 1 config has includeIf entries | ||||
|     let submodule1Content = ( | ||||
|       await fs.promises.readFile(submodule1ConfigPath) | ||||
|     ).toString() | ||||
|     const submodule1GitDir = submodule1Dir.replace(/\\/g, '/') | ||||
|     expect( | ||||
|       submodule1Content.indexOf(`includeIf.gitdir:${submodule1GitDir}.path`) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|     expect( | ||||
|       submodule1Content.indexOf(credentialsFilePath) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|  | ||||
|     // Verify submodule 2 config has includeIf entries | ||||
|     let submodule2Content = ( | ||||
|       await fs.promises.readFile(submodule2ConfigPath) | ||||
|     ).toString() | ||||
|     const submodule2GitDir = submodule2Dir.replace(/\\/g, '/') | ||||
|     expect( | ||||
|       submodule2Content.indexOf(`includeIf.gitdir:${submodule2GitDir}.path`) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|     expect( | ||||
|       submodule2Content.indexOf(credentialsFilePath) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|  | ||||
|     // Verify both host and container paths are in each submodule config | ||||
|     const containerCredentialsPath = path.posix.join( | ||||
|       '/github/runner_temp', | ||||
|       path.basename(credentialsFilePath) | ||||
|     ) | ||||
|     expect( | ||||
|       submodule1Content.indexOf(containerCredentialsPath) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|     expect( | ||||
|       submodule2Content.indexOf(containerCredentialsPath) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|  | ||||
|     // Act - ensure mock persists for removeAuth | ||||
|     mockGetSubmoduleConfigPaths.mockResolvedValue([ | ||||
|       submodule1ConfigPath, | ||||
|       submodule2ConfigPath | ||||
|     ]) | ||||
|     await authHelper.removeAuth() | ||||
|  | ||||
|     // Assert submodule 1 includeIf entries removed | ||||
|     submodule1Content = ( | ||||
|       await fs.promises.readFile(submodule1ConfigPath) | ||||
|     ).toString() | ||||
|     expect(submodule1Content.indexOf('includeIf.gitdir:')).toBeLessThan(0) | ||||
|     expect(submodule1Content.indexOf(credentialsFilePath)).toBeLessThan(0) | ||||
|     expect(submodule1Content.indexOf(containerCredentialsPath)).toBeLessThan(0) | ||||
|  | ||||
|     // Assert submodule 2 includeIf entries removed | ||||
|     submodule2Content = ( | ||||
|       await fs.promises.readFile(submodule2ConfigPath) | ||||
|     ).toString() | ||||
|     expect(submodule2Content.indexOf('includeIf.gitdir:')).toBeLessThan(0) | ||||
|     expect(submodule2Content.indexOf(credentialsFilePath)).toBeLessThan(0) | ||||
|     expect(submodule2Content.indexOf(containerCredentialsPath)).toBeLessThan(0) | ||||
|  | ||||
|     // Assert credentials config file deleted | ||||
|     credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||
|       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||
|     ) | ||||
|     expect(credentialsFiles.length).toBe(0) | ||||
|  | ||||
|     // Verify credentials file no longer exists on disk | ||||
|     try { | ||||
|       await fs.promises.stat(credentialsFilePath) | ||||
|       throw new Error('Credentials file should have been deleted') | ||||
|     } catch (err) { | ||||
|       if ((err as any)?.code !== 'ENOENT') { | ||||
|         throw err | ||||
|       } | ||||
|     } | ||||
|   }) | ||||
|  | ||||
|   const removeGlobalConfig_removesOverride = | ||||
| @@ -937,41 +771,17 @@ describe('git-auth-helper tests', () => { | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|      | ||||
|     // Get a real credentials config path | ||||
|     const credentialsConfigPath = await ( | ||||
|       authHelper as any | ||||
|     ).getCredentialsConfigPath() | ||||
|     const credentialsConfigPath = await (authHelper as any).getCredentialsConfigPath() | ||||
|      | ||||
|     // Act & Assert | ||||
|     expect( | ||||
|       (authHelper as any).testCredentialsConfigPath(credentialsConfigPath) | ||||
|     ).toBe(true) | ||||
|     expect( | ||||
|       (authHelper as any).testCredentialsConfigPath( | ||||
|         '/some/path/git-credentials-12345678-abcd-1234-5678-123456789012.config' | ||||
|       ) | ||||
|     ).toBe(true) | ||||
|     expect( | ||||
|       (authHelper as any).testCredentialsConfigPath( | ||||
|         '/some/path/git-credentials-abcdef12-3456-7890-abcd-ef1234567890.config' | ||||
|       ) | ||||
|     ).toBe(true) | ||||
|     expect((authHelper as any).testCredentialsConfigPath(credentialsConfigPath)).toBe(true) | ||||
|     expect((authHelper as any).testCredentialsConfigPath('/some/path/git-credentials-12345678-abcd-1234-5678-123456789012.config')).toBe(true) | ||||
|     expect((authHelper as any).testCredentialsConfigPath('/some/path/git-credentials-abcdef12-3456-7890-abcd-ef1234567890.config')).toBe(true) | ||||
|      | ||||
|     // Test invalid paths | ||||
|     expect( | ||||
|       (authHelper as any).testCredentialsConfigPath( | ||||
|         '/some/path/other-config.config' | ||||
|       ) | ||||
|     ).toBe(false) | ||||
|     expect( | ||||
|       (authHelper as any).testCredentialsConfigPath( | ||||
|         '/some/path/git-credentials-invalid.config' | ||||
|       ) | ||||
|     ).toBe(false) | ||||
|     expect( | ||||
|       (authHelper as any).testCredentialsConfigPath( | ||||
|         '/some/path/git-credentials-.config' | ||||
|       ) | ||||
|     ).toBe(false) | ||||
|     expect((authHelper as any).testCredentialsConfigPath('/some/path/other-config.config')).toBe(false) | ||||
|     expect((authHelper as any).testCredentialsConfigPath('/some/path/git-credentials-invalid.config')).toBe(false) | ||||
|     expect((authHelper as any).testCredentialsConfigPath('/some/path/git-credentials-.config')).toBe(false) | ||||
|     expect((authHelper as any).testCredentialsConfigPath('')).toBe(false) | ||||
|   }) | ||||
| }) | ||||
| @@ -1080,41 +890,28 @@ async function setup(testName: string): Promise<void> { | ||||
|       } | ||||
|     ), | ||||
|     tryConfigUnsetValue: jest.fn( | ||||
|       async ( | ||||
|         key: string, | ||||
|         value: string, | ||||
|         globalConfig?: boolean, | ||||
|         configPath?: string | ||||
|       ): Promise<boolean> => { | ||||
|         const targetConfigPath = | ||||
|           configPath || | ||||
|           (globalConfig | ||||
|       async (key: string, value: string, globalConfig?: boolean): Promise<boolean> => { | ||||
|         const configPath = globalConfig | ||||
|           ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||
|             : localGitConfigPath) | ||||
|         let content = await fs.promises.readFile(targetConfigPath) | ||||
|           : localGitConfigPath | ||||
|         let content = await fs.promises.readFile(configPath) | ||||
|         let lines = content | ||||
|           .toString() | ||||
|           .split('\n') | ||||
|           .filter(x => x) | ||||
|           .filter(x => !(x.startsWith(key) && x.includes(value))) | ||||
|         await fs.promises.writeFile(targetConfigPath, lines.join('\n')) | ||||
|         await fs.promises.writeFile(configPath, lines.join('\n')) | ||||
|         return true | ||||
|       } | ||||
|     ), | ||||
|     tryDisableAutomaticGarbageCollection: jest.fn(), | ||||
|     tryGetFetchUrl: jest.fn(), | ||||
|     tryGetConfigValues: jest.fn( | ||||
|       async ( | ||||
|         key: string, | ||||
|         globalConfig?: boolean, | ||||
|         configPath?: string | ||||
|       ): Promise<string[]> => { | ||||
|         const targetConfigPath = | ||||
|           configPath || | ||||
|           (globalConfig | ||||
|       async (key: string, globalConfig?: boolean): Promise<string[]> => { | ||||
|         const configPath = globalConfig | ||||
|           ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||
|             : localGitConfigPath) | ||||
|         const content = await fs.promises.readFile(targetConfigPath) | ||||
|           : localGitConfigPath | ||||
|         const content = await fs.promises.readFile(configPath) | ||||
|         const lines = content | ||||
|           .toString() | ||||
|           .split('\n') | ||||
| @@ -1124,17 +921,11 @@ async function setup(testName: string): Promise<void> { | ||||
|       } | ||||
|     ), | ||||
|     tryGetConfigKeys: jest.fn( | ||||
|       async ( | ||||
|         pattern: string, | ||||
|         globalConfig?: boolean, | ||||
|         configPath?: string | ||||
|       ): Promise<string[]> => { | ||||
|         const targetConfigPath = | ||||
|           configPath || | ||||
|           (globalConfig | ||||
|       async (pattern: string, globalConfig?: boolean): Promise<string[]> => { | ||||
|         const configPath = globalConfig | ||||
|           ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||
|             : localGitConfigPath) | ||||
|         const content = await fs.promises.readFile(targetConfigPath) | ||||
|           : localGitConfigPath | ||||
|         const content = await fs.promises.readFile(configPath) | ||||
|         const lines = content | ||||
|           .toString() | ||||
|           .split('\n') | ||||
|   | ||||
| @@ -17,7 +17,7 @@ fi | ||||
|  | ||||
| echo "Testing persisted credential" | ||||
| pushd ./submodules-recursive/submodule-level-1/submodule-level-2 | ||||
| git config --local --includes --name-only --get-regexp http.+extraheader && git fetch | ||||
| git config --local --name-only --get-regexp http.+extraheader && git fetch | ||||
| if [ "$?" != "0" ]; then | ||||
|     echo "Failed to validate persisted credential" | ||||
|     popd | ||||
|   | ||||
| @@ -17,7 +17,7 @@ fi | ||||
|  | ||||
| echo "Testing persisted credential" | ||||
| pushd ./submodules-true/submodule-level-1 | ||||
| git config --local --includes --name-only --get-regexp http.+extraheader && git fetch | ||||
| git config --local --name-only --get-regexp http.+extraheader && git fetch | ||||
| if [ "$?" != "0" ]; then | ||||
|     echo "Failed to validate persisted credential" | ||||
|     popd | ||||
|   | ||||
							
								
								
									
										63
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							| @@ -238,9 +238,7 @@ class GitAuthHelper { | ||||
|                 yield this.git.tryConfigUnset(this.insteadOfKey, true); | ||||
|                 if (!this.settings.sshKey) { | ||||
|                     for (const insteadOfValue of this.insteadOfValues) { | ||||
|                         yield this.git.config(this.insteadOfKey, insteadOfValue, true, // globalConfig?
 | ||||
|                         true // add?
 | ||||
|                         ); | ||||
|                         yield this.git.config(this.insteadOfKey, insteadOfValue, true, true); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @@ -257,10 +255,17 @@ class GitAuthHelper { | ||||
|             // Remove possible previous HTTPS instead of SSH
 | ||||
|             yield this.removeSubmoduleGitConfig(this.insteadOfKey); | ||||
|             if (this.settings.persistCredentials) { | ||||
|                 // Get the credentials config file path in RUNNER_TEMP
 | ||||
|                 const credentialsConfigPath = this.getCredentialsConfigPath(); | ||||
|                 // Credentials config path
 | ||||
|                 const credentialsConfigPath = yield this.getCredentialsConfigPath(); | ||||
|                 // Container credentials config path
 | ||||
|                 const containerCredentialsPath = path.posix.join('/github/runner_temp', path.basename(credentialsConfigPath)); | ||||
|                 // Container repo path
 | ||||
|                 const workingDirectory = this.git.getWorkingDirectory(); | ||||
|                 const githubWorkspace = process.env['GITHUB_WORKSPACE']; | ||||
|                 assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined'); | ||||
|                 let relativePath = path.relative(githubWorkspace, workingDirectory); | ||||
|                 relativePath = relativePath.replace(/\\/g, '/'); | ||||
|                 const containerRepoPath = path.posix.join('/github/workspace', relativePath); | ||||
|                 // Get submodule config file paths.
 | ||||
|                 const configPaths = yield this.git.getSubmoduleConfigPaths(this.settings.nestedSubmodules); | ||||
|                 // For each submodule, configure includeIf entries pointing to the shared credentials file.
 | ||||
| @@ -270,19 +275,12 @@ class GitAuthHelper { | ||||
|                     let submoduleGitDir = path.dirname(configPath); // The config file is at .git/modules/submodule-name/config
 | ||||
|                     submoduleGitDir = submoduleGitDir.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | ||||
|                     // Configure host includeIf
 | ||||
|                     yield this.git.config(`includeIf.gitdir:${submoduleGitDir}.path`, credentialsConfigPath, false, // globalConfig?
 | ||||
|                     false, // add?
 | ||||
|                     configPath); | ||||
|                     // Container submodule git directory
 | ||||
|                     const githubWorkspace = process.env['GITHUB_WORKSPACE']; | ||||
|                     assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined'); | ||||
|                     yield this.git.config(`includeIf.gitdir:${submoduleGitDir}.path`, credentialsConfigPath, false, false, configPath); | ||||
|                     // Configure container includeIf
 | ||||
|                     let relativeSubmoduleGitDir = path.relative(githubWorkspace, submoduleGitDir); | ||||
|                     relativeSubmoduleGitDir = relativeSubmoduleGitDir.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | ||||
|                     const containerSubmoduleGitDir = path.posix.join('/github/workspace', relativeSubmoduleGitDir); | ||||
|                     // Configure container includeIf
 | ||||
|                     yield this.git.config(`includeIf.gitdir:${containerSubmoduleGitDir}.path`, containerCredentialsPath, false, // globalConfig?
 | ||||
|                     false, // add?
 | ||||
|                     configPath); | ||||
|                     yield this.git.config(`includeIf.gitdir:${containerSubmoduleGitDir}.path`, containerCredentialsPath, false, false, configPath); | ||||
|                 } | ||||
|                 if (this.settings.sshKey) { | ||||
|                     // Configure core.sshCommand
 | ||||
| @@ -381,14 +379,12 @@ class GitAuthHelper { | ||||
|     configureToken(globalConfig) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             // Get the credentials config file path in RUNNER_TEMP
 | ||||
|             const credentialsConfigPath = this.getCredentialsConfigPath(); | ||||
|             const credentialsConfigPath = yield this.getCredentialsConfigPath(); | ||||
|             // Write placeholder to the separate credentials config file using git config.
 | ||||
|             // This approach avoids the credential being captured by process creation audit events,
 | ||||
|             // which are commonly logged. For more information, refer to
 | ||||
|             // https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 | ||||
|             yield this.git.config(this.tokenConfigKey, this.tokenPlaceholderConfigValue, false, // globalConfig?
 | ||||
|             false, // add?
 | ||||
|             credentialsConfigPath); | ||||
|             yield this.git.config(this.tokenConfigKey, this.tokenPlaceholderConfigValue, false, false, credentialsConfigPath); | ||||
|             // Replace the placeholder in the credentials config file
 | ||||
|             let content = (yield fs.promises.readFile(credentialsConfigPath)).toString(); | ||||
|             const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue); | ||||
| @@ -402,8 +398,7 @@ class GitAuthHelper { | ||||
|             // Add include or includeIf to reference the credentials config
 | ||||
|             if (globalConfig) { | ||||
|                 // Global config file is temporary
 | ||||
|                 yield this.git.config('include.path', credentialsConfigPath, true // globalConfig?
 | ||||
|                 ); | ||||
|                 yield this.git.config('include.path', credentialsConfigPath, true); | ||||
|             } | ||||
|             else { | ||||
|                 // Host git directory
 | ||||
| @@ -413,9 +408,9 @@ class GitAuthHelper { | ||||
|                 const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`; | ||||
|                 yield this.git.config(hostIncludeKey, credentialsConfigPath); | ||||
|                 // Container git directory
 | ||||
|                 const workingDirectory = this.git.getWorkingDirectory(); | ||||
|                 const githubWorkspace = process.env['GITHUB_WORKSPACE']; | ||||
|                 assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined'); | ||||
|                 const workingDirectory = this.git.getWorkingDirectory(); | ||||
|                 let relativePath = path.relative(githubWorkspace, workingDirectory); | ||||
|                 relativePath = relativePath.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | ||||
|                 const containerGitDir = path.posix.join('/github/workspace', relativePath, '.git'); | ||||
| @@ -432,6 +427,7 @@ class GitAuthHelper { | ||||
|      * @returns The absolute path to the credentials config file | ||||
|      */ | ||||
|     getCredentialsConfigPath() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             if (this.credentialsConfigPath) { | ||||
|                 return this.credentialsConfigPath; | ||||
|             } | ||||
| @@ -442,6 +438,7 @@ class GitAuthHelper { | ||||
|             this.credentialsConfigPath = path.join(runnerTemp, configFileName); | ||||
|             core.debug(`Credentials config path: ${this.credentialsConfigPath}`); | ||||
|             return this.credentialsConfigPath; | ||||
|         }); | ||||
|     } | ||||
|     /** | ||||
|      * Removes SSH authentication configuration by cleaning up SSH keys, | ||||
| @@ -475,7 +472,7 @@ class GitAuthHelper { | ||||
|                 } | ||||
|             } | ||||
|             // SSH command
 | ||||
|             core.info('Removing SSH command configuration'); | ||||
|             core.info("Removing SSH command configuration"); | ||||
|             yield this.removeGitConfig(SSH_COMMAND_KEY); | ||||
|             yield this.removeSubmoduleGitConfig(SSH_COMMAND_KEY); | ||||
|         }); | ||||
| @@ -488,13 +485,13 @@ class GitAuthHelper { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             var _a; | ||||
|             // Remove HTTP extra header
 | ||||
|             core.info('Removing HTTP extra header'); | ||||
|             core.info("Removing HTTP extra header"); | ||||
|             yield this.removeGitConfig(this.tokenConfigKey); | ||||
|             yield this.removeSubmoduleGitConfig(this.tokenConfigKey); | ||||
|             // Collect credentials config paths that need to be removed
 | ||||
|             const credentialsPaths = new Set(); | ||||
|             // Remove includeIf entries that point to git-credentials-*.config files
 | ||||
|             core.info('Removing includeIf entries pointing to credentials config files'); | ||||
|             core.info("Removing includeIf entries pointing to credentials config files"); | ||||
|             const mainCredentialsPaths = yield this.removeIncludeIfCredentials(); | ||||
|             mainCredentialsPaths.forEach(path => credentialsPaths.add(path)); | ||||
|             // Remove submodule includeIf entries that point to git-credentials-*.config files
 | ||||
| @@ -559,12 +556,10 @@ class GitAuthHelper { | ||||
|             const credentialsPaths = new Set(); | ||||
|             try { | ||||
|                 // Get all includeIf.gitdir keys
 | ||||
|                 const keys = yield this.git.tryGetConfigKeys('^includeIf\\.gitdir:', false, // globalConfig?
 | ||||
|                 configPath); | ||||
|                 const keys = yield this.git.tryGetConfigKeys('^includeIf\\.gitdir:', false, configPath); | ||||
|                 for (const key of keys) { | ||||
|                     // Get all values for this key
 | ||||
|                     const values = yield this.git.tryGetConfigValues(key, false, // globalConfig?
 | ||||
|                     configPath); | ||||
|                     const values = yield this.git.tryGetConfigValues(key, false, configPath); | ||||
|                     if (values.length > 0) { | ||||
|                         // Remove only values that match git-credentials-<uuid>.config pattern
 | ||||
|                         for (const value of values) { | ||||
| @@ -1065,10 +1060,7 @@ class GitCommandManager { | ||||
|             if (output.exitCode !== 0) { | ||||
|                 return []; | ||||
|             } | ||||
|             return output.stdout | ||||
|                 .trim() | ||||
|                 .split('\n') | ||||
|                 .filter(value => value.trim()); | ||||
|             return output.stdout.trim().split('\n').filter(value => value.trim()); | ||||
|         }); | ||||
|     } | ||||
|     tryGetConfigKeys(pattern, globalConfig, configFile) { | ||||
| @@ -1085,10 +1077,7 @@ class GitCommandManager { | ||||
|             if (output.exitCode !== 0) { | ||||
|                 return []; | ||||
|             } | ||||
|             return output.stdout | ||||
|                 .trim() | ||||
|                 .split('\n') | ||||
|                 .filter(key => key.trim()); | ||||
|             return output.stdout.trim().split('\n').filter(key => key.trim()); | ||||
|         }); | ||||
|     } | ||||
|     tryReset() { | ||||
|   | ||||
| @@ -136,12 +136,7 @@ class GitAuthHelper { | ||||
|       await this.git.tryConfigUnset(this.insteadOfKey, true) | ||||
|       if (!this.settings.sshKey) { | ||||
|         for (const insteadOfValue of this.insteadOfValues) { | ||||
|           await this.git.config( | ||||
|             this.insteadOfKey, | ||||
|             insteadOfValue, | ||||
|             true, // globalConfig? | ||||
|             true // add? | ||||
|           ) | ||||
|           await this.git.config(this.insteadOfKey, insteadOfValue, true, true) | ||||
|         } | ||||
|       } | ||||
|     } catch (err) { | ||||
| @@ -159,8 +154,8 @@ class GitAuthHelper { | ||||
|     await this.removeSubmoduleGitConfig(this.insteadOfKey) | ||||
|  | ||||
|     if (this.settings.persistCredentials) { | ||||
|       // Get the credentials config file path in RUNNER_TEMP | ||||
|       const credentialsConfigPath = this.getCredentialsConfigPath() | ||||
|       // Credentials config path | ||||
|       const credentialsConfigPath = await this.getCredentialsConfigPath() | ||||
|  | ||||
|       // Container credentials config path | ||||
|       const containerCredentialsPath = path.posix.join( | ||||
| @@ -168,6 +163,17 @@ class GitAuthHelper { | ||||
|         path.basename(credentialsConfigPath) | ||||
|       ) | ||||
|  | ||||
|       // Container repo path | ||||
|       const workingDirectory = this.git.getWorkingDirectory() | ||||
|       const githubWorkspace = process.env['GITHUB_WORKSPACE'] | ||||
|       assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined') | ||||
|       let relativePath = path.relative(githubWorkspace, workingDirectory) | ||||
|       relativePath = relativePath.replace(/\\/g, '/') | ||||
|       const containerRepoPath = path.posix.join( | ||||
|         '/github/workspace', | ||||
|         relativePath | ||||
|       ) | ||||
|  | ||||
|       // Get submodule config file paths. | ||||
|       const configPaths = await this.git.getSubmoduleConfigPaths( | ||||
|         this.settings.nestedSubmodules | ||||
| @@ -184,14 +190,12 @@ class GitAuthHelper { | ||||
|         await this.git.config( | ||||
|           `includeIf.gitdir:${submoduleGitDir}.path`, | ||||
|           credentialsConfigPath, | ||||
|           false, // globalConfig? | ||||
|           false, // add? | ||||
|           false, | ||||
|           false, | ||||
|           configPath | ||||
|         ) | ||||
|  | ||||
|         // Container submodule git directory | ||||
|         const githubWorkspace = process.env['GITHUB_WORKSPACE'] | ||||
|         assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined') | ||||
|         // Configure container includeIf | ||||
|         let relativeSubmoduleGitDir = path.relative( | ||||
|           githubWorkspace, | ||||
|           submoduleGitDir | ||||
| @@ -201,13 +205,11 @@ class GitAuthHelper { | ||||
|           '/github/workspace', | ||||
|           relativeSubmoduleGitDir | ||||
|         ) | ||||
|  | ||||
|         // Configure container includeIf | ||||
|         await this.git.config( | ||||
|           `includeIf.gitdir:${containerSubmoduleGitDir}.path`, | ||||
|           containerCredentialsPath, | ||||
|           false, // globalConfig? | ||||
|           false, // add? | ||||
|           false, | ||||
|           false, | ||||
|           configPath | ||||
|         ) | ||||
|       } | ||||
| @@ -325,7 +327,7 @@ class GitAuthHelper { | ||||
|    */ | ||||
|   private async configureToken(globalConfig?: boolean): Promise<void> { | ||||
|     // Get the credentials config file path in RUNNER_TEMP | ||||
|     const credentialsConfigPath = this.getCredentialsConfigPath() | ||||
|     const credentialsConfigPath = await this.getCredentialsConfigPath() | ||||
|  | ||||
|     // Write placeholder to the separate credentials config file using git config. | ||||
|     // This approach avoids the credential being captured by process creation audit events, | ||||
| @@ -334,8 +336,8 @@ class GitAuthHelper { | ||||
|     await this.git.config( | ||||
|       this.tokenConfigKey, | ||||
|       this.tokenPlaceholderConfigValue, | ||||
|       false, // globalConfig? | ||||
|       false, // add? | ||||
|       false, | ||||
|       false, | ||||
|       credentialsConfigPath | ||||
|     ) | ||||
|  | ||||
| @@ -346,9 +348,7 @@ class GitAuthHelper { | ||||
|       placeholderIndex < 0 || | ||||
|       placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue) | ||||
|     ) { | ||||
|       throw new Error( | ||||
|         `Unable to replace auth placeholder in ${credentialsConfigPath}` | ||||
|       ) | ||||
|       throw new Error(`Unable to replace auth placeholder in ${credentialsConfigPath}`) | ||||
|     } | ||||
|     assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined') | ||||
|     content = content.replace( | ||||
| @@ -360,11 +360,7 @@ class GitAuthHelper { | ||||
|     // Add include or includeIf to reference the credentials config | ||||
|     if (globalConfig) { | ||||
|       // Global config file is temporary | ||||
|       await this.git.config( | ||||
|         'include.path', | ||||
|         credentialsConfigPath, | ||||
|         true // globalConfig? | ||||
|       ) | ||||
|       await this.git.config('include.path', credentialsConfigPath, true) | ||||
|     } else { | ||||
|       // Host git directory | ||||
|       let gitDir = path.join(this.git.getWorkingDirectory(), '.git') | ||||
| @@ -375,9 +371,9 @@ class GitAuthHelper { | ||||
|       await this.git.config(hostIncludeKey, credentialsConfigPath) | ||||
|  | ||||
|       // Container git directory | ||||
|       const workingDirectory = this.git.getWorkingDirectory() | ||||
|       const githubWorkspace = process.env['GITHUB_WORKSPACE'] | ||||
|       assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined') | ||||
|       const workingDirectory = this.git.getWorkingDirectory() | ||||
|       let relativePath = path.relative(githubWorkspace, workingDirectory) | ||||
|       relativePath = relativePath.replace(/\\/g, '/') // Use forward slashes, even on Windows | ||||
|       const containerGitDir = path.posix.join( | ||||
| @@ -402,7 +398,7 @@ class GitAuthHelper { | ||||
|    * Gets or creates the path to the credentials config file in RUNNER_TEMP. | ||||
|    * @returns The absolute path to the credentials config file | ||||
|    */ | ||||
|   private getCredentialsConfigPath(): string { | ||||
|   private async getCredentialsConfigPath(): Promise<string> { | ||||
|     if (this.credentialsConfigPath) { | ||||
|       return this.credentialsConfigPath | ||||
|     } | ||||
| @@ -449,7 +445,7 @@ class GitAuthHelper { | ||||
|     } | ||||
|  | ||||
|     // SSH command | ||||
|     core.info('Removing SSH command configuration') | ||||
|     core.info("Removing SSH command configuration") | ||||
|     await this.removeGitConfig(SSH_COMMAND_KEY) | ||||
|     await this.removeSubmoduleGitConfig(SSH_COMMAND_KEY) | ||||
|   } | ||||
| @@ -460,7 +456,7 @@ class GitAuthHelper { | ||||
|    */ | ||||
|   private async removeToken(): Promise<void> { | ||||
|     // Remove HTTP extra header | ||||
|     core.info('Removing HTTP extra header') | ||||
|     core.info("Removing HTTP extra header") | ||||
|     await this.removeGitConfig(this.tokenConfigKey) | ||||
|     await this.removeSubmoduleGitConfig(this.tokenConfigKey) | ||||
|  | ||||
| @@ -468,15 +464,14 @@ class GitAuthHelper { | ||||
|     const credentialsPaths = new Set<string>() | ||||
|  | ||||
|     // Remove includeIf entries that point to git-credentials-*.config files | ||||
|     core.info('Removing includeIf entries pointing to credentials config files') | ||||
|     core.info("Removing includeIf entries pointing to credentials config files") | ||||
|     const mainCredentialsPaths = await this.removeIncludeIfCredentials() | ||||
|     mainCredentialsPaths.forEach(path => credentialsPaths.add(path)) | ||||
|  | ||||
|     // Remove submodule includeIf entries that point to git-credentials-*.config files | ||||
|     const submoduleConfigPaths = await this.git.getSubmoduleConfigPaths(true) | ||||
|     for (const configPath of submoduleConfigPaths) { | ||||
|       const submoduleCredentialsPaths = | ||||
|         await this.removeIncludeIfCredentials(configPath) | ||||
|       const submoduleCredentialsPaths = await this.removeIncludeIfCredentials(configPath) | ||||
|       submoduleCredentialsPaths.forEach(path => credentialsPaths.add(path)) | ||||
|     } | ||||
|  | ||||
| @@ -496,9 +491,7 @@ class GitAuthHelper { | ||||
|           ) | ||||
|         } | ||||
|       } else { | ||||
|         core.debug( | ||||
|           `Skipping removal of credentials config '${credentialsPath}' - not under RUNNER_TEMP` | ||||
|         ) | ||||
|         core.debug(`Skipping removal of credentials config '${credentialsPath}' - not under RUNNER_TEMP`) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -535,26 +528,16 @@ class GitAuthHelper { | ||||
|    * @param configPath Optional path to a specific git config file to operate on | ||||
|    * @returns Array of unique credentials config file paths that were found and removed | ||||
|    */ | ||||
|   private async removeIncludeIfCredentials( | ||||
|     configPath?: string | ||||
|   ): Promise<string[]> { | ||||
|   private async removeIncludeIfCredentials(configPath?: string): Promise<string[]> { | ||||
|     const credentialsPaths = new Set<string>() | ||||
|      | ||||
|     try { | ||||
|       // Get all includeIf.gitdir keys | ||||
|       const keys = await this.git.tryGetConfigKeys( | ||||
|         '^includeIf\\.gitdir:', | ||||
|         false, // globalConfig? | ||||
|         configPath | ||||
|       ) | ||||
|       const keys = await this.git.tryGetConfigKeys('^includeIf\\.gitdir:', false, configPath) | ||||
|        | ||||
|       for (const key of keys) { | ||||
|         // Get all values for this key | ||||
|         const values = await this.git.tryGetConfigValues( | ||||
|           key, | ||||
|           false, // globalConfig? | ||||
|           configPath | ||||
|         ) | ||||
|         const values = await this.git.tryGetConfigValues(key, false, configPath) | ||||
|         if (values.length > 0) { | ||||
|           // Remove only values that match git-credentials-<uuid>.config pattern | ||||
|           for (const value of values) { | ||||
|   | ||||
| @@ -61,24 +61,11 @@ export interface IGitCommandManager { | ||||
|   tagExists(pattern: string): Promise<boolean> | ||||
|   tryClean(): Promise<boolean> | ||||
|   tryConfigUnset(configKey: string, globalConfig?: boolean): Promise<boolean> | ||||
|   tryConfigUnsetValue( | ||||
|     configKey: string, | ||||
|     configValue: string, | ||||
|     globalConfig?: boolean, | ||||
|     configFile?: string | ||||
|   ): Promise<boolean> | ||||
|   tryConfigUnsetValue(configKey: string, configValue: string, globalConfig?: boolean, configFile?: string): Promise<boolean> | ||||
|   tryDisableAutomaticGarbageCollection(): Promise<boolean> | ||||
|   tryGetFetchUrl(): Promise<string> | ||||
|   tryGetConfigValues( | ||||
|     configKey: string, | ||||
|     globalConfig?: boolean, | ||||
|     configFile?: string | ||||
|   ): Promise<string[]> | ||||
|   tryGetConfigKeys( | ||||
|     pattern: string, | ||||
|     globalConfig?: boolean, | ||||
|     configFile?: string | ||||
|   ): Promise<string[]> | ||||
|   tryGetConfigValues(configKey: string, globalConfig?: boolean, configFile?: string): Promise<string[]> | ||||
|   tryGetConfigKeys(pattern: string, globalConfig?: boolean, configFile?: string): Promise<string[]> | ||||
|   tryReset(): Promise<boolean> | ||||
|   version(): Promise<GitVersion> | ||||
| } | ||||
| @@ -557,10 +544,7 @@ class GitCommandManager { | ||||
|       return [] | ||||
|     } | ||||
|  | ||||
|     return output.stdout | ||||
|       .trim() | ||||
|       .split('\n') | ||||
|       .filter(value => value.trim()) | ||||
|     return output.stdout.trim().split('\n').filter(value => value.trim()) | ||||
|   } | ||||
|  | ||||
|   async tryGetConfigKeys( | ||||
| @@ -582,10 +566,7 @@ class GitCommandManager { | ||||
|       return [] | ||||
|     } | ||||
|  | ||||
|     return output.stdout | ||||
|       .trim() | ||||
|       .split('\n') | ||||
|       .filter(key => key.trim()) | ||||
|     return output.stdout.trim().split('\n').filter(key => key.trim()) | ||||
|   } | ||||
|  | ||||
|   async tryReset(): Promise<boolean> { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user