mirror of
				https://github.com/actions/checkout.git
				synced 2025-11-01 02:28:40 +08:00 
			
		
		
		
	Use git-credential-store instead of .extraheader
This commit is contained in:
		
							parent
							
								
									b80ff79f17
								
							
						
					
					
						commit
						6606fcd2c4
					
				|  | @ -71,6 +71,9 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ | ||||||
|     # Default: true |     # Default: true | ||||||
|     persist-credentials: '' |     persist-credentials: '' | ||||||
| 
 | 
 | ||||||
|  |     # Custom git credential helper | ||||||
|  |     custom-credential-helper: '' | ||||||
|  | 
 | ||||||
|     # Relative path under $GITHUB_WORKSPACE to place the repository |     # Relative path under $GITHUB_WORKSPACE to place the repository | ||||||
|     path: '' |     path: '' | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -52,6 +52,8 @@ inputs: | ||||||
|   persist-credentials: |   persist-credentials: | ||||||
|     description: 'Whether to configure the token or SSH key with the local git config' |     description: 'Whether to configure the token or SSH key with the local git config' | ||||||
|     default: true |     default: true | ||||||
|  |   custom-credential-helper: | ||||||
|  |     description: 'Custom git credential helper' | ||||||
|   path: |   path: | ||||||
|     description: 'Relative path under $GITHUB_WORKSPACE to place the repository' |     description: 'Relative path under $GITHUB_WORKSPACE to place the repository' | ||||||
|   clean: |   clean: | ||||||
|  |  | ||||||
							
								
								
									
										90
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										90
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							|  | @ -166,13 +166,16 @@ class GitAuthHelper { | ||||||
|         this.temporaryHomePath = ''; |         this.temporaryHomePath = ''; | ||||||
|         this.git = gitCommandManager; |         this.git = gitCommandManager; | ||||||
|         this.settings = gitSourceSettings || {}; |         this.settings = gitSourceSettings || {}; | ||||||
|         // Token auth header
 |         this.credentialConfigKey = `credential.helper`; | ||||||
|  |         const runnerTemp = process.env['RUNNER_TEMP'] || ''; | ||||||
|  |         assert.ok(runnerTemp, 'RUNNER_TEMP is not defined'); | ||||||
|  |         const uniqueId = (0, uuid_1.v4)(); | ||||||
|  |         this.credentialStorePath = path.join(runnerTemp, `${uniqueId}_credential_store`); | ||||||
|  |         this.credentialConfigValue = `store --file ${this.credentialStorePath}`; | ||||||
|         const serverUrl = urlHelper.getServerUrl(this.settings.githubServerUrl); |         const serverUrl = urlHelper.getServerUrl(this.settings.githubServerUrl); | ||||||
|         this.tokenConfigKey = `http.${serverUrl.origin}/.extraheader`; // "origin" is SCHEME://HOSTNAME[:PORT]
 |         serverUrl.username = `x-access-token`; | ||||||
|         const basicCredential = Buffer.from(`x-access-token:${this.settings.authToken}`, 'utf8').toString('base64'); |         serverUrl.password = this.settings.authToken; | ||||||
|         core.setSecret(basicCredential); |         this.tokenCredential = serverUrl.href; | ||||||
|         this.tokenPlaceholderConfigValue = `AUTHORIZATION: basic ***`; |  | ||||||
|         this.tokenConfigValue = `AUTHORIZATION: basic ${basicCredential}`; |  | ||||||
|         // Instead of SSH URL
 |         // Instead of SSH URL
 | ||||||
|         this.insteadOfKey = `url.${serverUrl.origin}/.insteadOf`; // "origin" is SCHEME://HOSTNAME[:PORT]
 |         this.insteadOfKey = `url.${serverUrl.origin}/.insteadOf`; // "origin" is SCHEME://HOSTNAME[:PORT]
 | ||||||
|         this.insteadOfValues.push(`git@${serverUrl.hostname}:`); |         this.insteadOfValues.push(`git@${serverUrl.hostname}:`); | ||||||
|  | @ -246,7 +249,7 @@ class GitAuthHelper { | ||||||
|             catch (err) { |             catch (err) { | ||||||
|                 // Unset in case somehow written to the real global config
 |                 // Unset in case somehow written to the real global config
 | ||||||
|                 core.info('Encountered an error when attempting to configure token. Attempting unconfigure.'); |                 core.info('Encountered an error when attempting to configure token. Attempting unconfigure.'); | ||||||
|                 yield this.git.tryConfigUnset(this.tokenConfigKey, true); |                 yield this.git.tryConfigUnset(this.credentialConfigKey, true); | ||||||
|                 throw err; |                 throw err; | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  | @ -256,18 +259,12 @@ class GitAuthHelper { | ||||||
|             // Remove possible previous HTTPS instead of SSH
 |             // Remove possible previous HTTPS instead of SSH
 | ||||||
|             yield this.removeGitConfig(this.insteadOfKey, true); |             yield this.removeGitConfig(this.insteadOfKey, true); | ||||||
|             if (this.settings.persistCredentials) { |             if (this.settings.persistCredentials) { | ||||||
|                 // Configure a placeholder value. This approach avoids the credential being captured
 |                 if (this.settings.customCredentialHelper) { | ||||||
|                 // by process creation audit events, which are commonly logged. For more information,
 |                     yield this.git.submoduleForeach(`sh -c "git config --local --add '${this.credentialConfigKey}' '${this.settings.customCredentialHelper}' && git config --local 'credential.useHttpPath' 'true'"`, this.settings.nestedSubmodules); | ||||||
|                 // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 |  | ||||||
|                 const output = yield this.git.submoduleForeach( |  | ||||||
|                 // wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline
 |  | ||||||
|                 `sh -c "git config --local '${this.tokenConfigKey}' '${this.tokenPlaceholderConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url"`, this.settings.nestedSubmodules); |  | ||||||
|                 // Replace the placeholder
 |  | ||||||
|                 const configPaths = output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || []; |  | ||||||
|                 for (const configPath of configPaths) { |  | ||||||
|                     core.debug(`Replacing token placeholder in '${configPath}'`); |  | ||||||
|                     yield this.replaceTokenPlaceholder(configPath); |  | ||||||
|                 } |                 } | ||||||
|  |                 yield this.git.submoduleForeach( | ||||||
|  |                 // wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline
 | ||||||
|  |                 `sh -c "git config --local --add '${this.credentialConfigKey}' '${this.credentialConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url"`, this.settings.nestedSubmodules); | ||||||
|                 if (this.settings.sshKey) { |                 if (this.settings.sshKey) { | ||||||
|                     // Configure core.sshCommand
 |                     // Configure core.sshCommand
 | ||||||
|                     yield this.git.submoduleForeach(`git config --local '${SSH_COMMAND_KEY}' '${this.sshCommand}'`, this.settings.nestedSubmodules); |                     yield this.git.submoduleForeach(`git config --local '${SSH_COMMAND_KEY}' '${this.sshCommand}'`, this.settings.nestedSubmodules); | ||||||
|  | @ -306,7 +303,7 @@ class GitAuthHelper { | ||||||
|             const runnerTemp = process.env['RUNNER_TEMP'] || ''; |             const runnerTemp = process.env['RUNNER_TEMP'] || ''; | ||||||
|             assert.ok(runnerTemp, 'RUNNER_TEMP is not defined'); |             assert.ok(runnerTemp, 'RUNNER_TEMP is not defined'); | ||||||
|             const uniqueId = (0, uuid_1.v4)(); |             const uniqueId = (0, uuid_1.v4)(); | ||||||
|             this.sshKeyPath = path.join(runnerTemp, uniqueId); |             this.sshKeyPath = path.join(runnerTemp, `${uniqueId}_ssh_key`); | ||||||
|             stateHelper.setSshKeyPath(this.sshKeyPath); |             stateHelper.setSshKeyPath(this.sshKeyPath); | ||||||
|             yield fs.promises.mkdir(runnerTemp, { recursive: true }); |             yield fs.promises.mkdir(runnerTemp, { recursive: true }); | ||||||
|             yield fs.promises.writeFile(this.sshKeyPath, this.settings.sshKey.trim() + '\n', { mode: 0o600 }); |             yield fs.promises.writeFile(this.sshKeyPath, this.settings.sshKey.trim() + '\n', { mode: 0o600 }); | ||||||
|  | @ -357,30 +354,17 @@ class GitAuthHelper { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             // Validate args
 |             // Validate args
 | ||||||
|             assert.ok((configPath && globalConfig) || (!configPath && !globalConfig), 'Unexpected configureToken parameter combinations'); |             assert.ok((configPath && globalConfig) || (!configPath && !globalConfig), 'Unexpected configureToken parameter combinations'); | ||||||
|  |             stateHelper.setCredentialStorePath(this.credentialStorePath); | ||||||
|  |             yield fs.promises.writeFile(this.credentialStorePath, this.tokenCredential); | ||||||
|             // Default config path
 |             // Default config path
 | ||||||
|             if (!configPath && !globalConfig) { |             if (!configPath && !globalConfig) { | ||||||
|                 configPath = path.join(this.git.getWorkingDirectory(), '.git', 'config'); |                 configPath = path.join(this.git.getWorkingDirectory(), '.git', 'config'); | ||||||
|             } |             } | ||||||
|             // Configure a placeholder value. This approach avoids the credential being captured
 |             if (this.settings.customCredentialHelper) { | ||||||
|             // by process creation audit events, which are commonly logged. For more information,
 |                 yield this.git.config(this.credentialConfigKey, this.settings.customCredentialHelper, globalConfig, true); | ||||||
|             // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 |                 yield this.git.config('credential.useHttpPath', 'true', globalConfig); | ||||||
|             yield this.git.config(this.tokenConfigKey, this.tokenPlaceholderConfigValue, globalConfig); |  | ||||||
|             // Replace the placeholder
 |  | ||||||
|             yield this.replaceTokenPlaceholder(configPath || ''); |  | ||||||
|         }); |  | ||||||
|             } |             } | ||||||
|     replaceTokenPlaceholder(configPath) { |             yield this.git.config(this.credentialConfigKey, this.credentialConfigValue, globalConfig, true); | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |  | ||||||
|             assert.ok(configPath, 'configPath is not defined'); |  | ||||||
|             let content = (yield fs.promises.readFile(configPath)).toString(); |  | ||||||
|             const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue); |  | ||||||
|             if (placeholderIndex < 0 || |  | ||||||
|                 placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue)) { |  | ||||||
|                 throw new Error(`Unable to replace auth placeholder in ${configPath}`); |  | ||||||
|             } |  | ||||||
|             assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined'); |  | ||||||
|             content = content.replace(this.tokenPlaceholderConfigValue, this.tokenConfigValue); |  | ||||||
|             yield fs.promises.writeFile(configPath, content); |  | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|     removeSsh() { |     removeSsh() { | ||||||
|  | @ -413,8 +397,19 @@ class GitAuthHelper { | ||||||
|     } |     } | ||||||
|     removeToken() { |     removeToken() { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             // HTTP extra header
 |             var _a; | ||||||
|             yield this.removeGitConfig(this.tokenConfigKey); |             // Credential Helper
 | ||||||
|  |             const credentialStorePath = this.credentialStorePath || stateHelper.CredentialStorePath; | ||||||
|  |             if (credentialStorePath) { | ||||||
|  |                 try { | ||||||
|  |                     yield io.rmRF(credentialStorePath); | ||||||
|  |                 } | ||||||
|  |                 catch (err) { | ||||||
|  |                     core.debug(`${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`); | ||||||
|  |                     core.warning(`Failed to remove credential store '${credentialStorePath}'`); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             yield this.removeGitConfig(this.credentialConfigKey); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|     removeGitConfig(configKey_1) { |     removeGitConfig(configKey_1) { | ||||||
|  | @ -1826,6 +1821,8 @@ function getInputs() { | ||||||
|         // Persist credentials
 |         // Persist credentials
 | ||||||
|         result.persistCredentials = |         result.persistCredentials = | ||||||
|             (core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE'; |             (core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE'; | ||||||
|  |         // Custom credential helper
 | ||||||
|  |         result.customCredentialHelper = core.getInput('custom-credential-helper'); | ||||||
|         // Workflow organization ID
 |         // Workflow organization ID
 | ||||||
|         result.workflowOrganizationId = |         result.workflowOrganizationId = | ||||||
|             yield workflowContextHelper.getOrganizationId(); |             yield workflowContextHelper.getOrganizationId(); | ||||||
|  | @ -2347,7 +2344,7 @@ var __importStar = (this && this.__importStar) || function (mod) { | ||||||
|     return result; |     return result; | ||||||
| }; | }; | ||||||
| Object.defineProperty(exports, "__esModule", ({ value: true })); | Object.defineProperty(exports, "__esModule", ({ value: true })); | ||||||
| exports.setSafeDirectory = exports.setSshKnownHostsPath = exports.setSshKeyPath = exports.setRepositoryPath = exports.SshKnownHostsPath = exports.SshKeyPath = exports.PostSetSafeDirectory = exports.RepositoryPath = exports.IsPost = void 0; | exports.setCredentialStorePath = exports.setSafeDirectory = exports.setSshKnownHostsPath = exports.setSshKeyPath = exports.setRepositoryPath = exports.CredentialStorePath = exports.SshKnownHostsPath = exports.SshKeyPath = exports.PostSetSafeDirectory = exports.RepositoryPath = exports.IsPost = void 0; | ||||||
| const core = __importStar(__nccwpck_require__(2186)); | const core = __importStar(__nccwpck_require__(2186)); | ||||||
| /** | /** | ||||||
|  * Indicates whether the POST action is running |  * Indicates whether the POST action is running | ||||||
|  | @ -2369,6 +2366,10 @@ exports.SshKeyPath = core.getState('sshKeyPath'); | ||||||
|  * The SSH known hosts path for the POST action. The value is empty during the MAIN action. |  * The SSH known hosts path for the POST action. The value is empty during the MAIN action. | ||||||
|  */ |  */ | ||||||
| exports.SshKnownHostsPath = core.getState('sshKnownHostsPath'); | exports.SshKnownHostsPath = core.getState('sshKnownHostsPath'); | ||||||
|  | /** | ||||||
|  |  * The credential store path for git-credential-store | ||||||
|  |  */ | ||||||
|  | exports.CredentialStorePath = core.getState('credentialStorePath'); | ||||||
| /** | /** | ||||||
|  * Save the repository path so the POST action can retrieve the value. |  * Save the repository path so the POST action can retrieve the value. | ||||||
|  */ |  */ | ||||||
|  | @ -2402,6 +2403,13 @@ exports.setSafeDirectory = setSafeDirectory; | ||||||
| if (!exports.IsPost) { | if (!exports.IsPost) { | ||||||
|     core.saveState('isPost', 'true'); |     core.saveState('isPost', 'true'); | ||||||
| } | } | ||||||
|  | /** | ||||||
|  |  * Save the credential store path so the POST action can retrieve the value. | ||||||
|  |  */ | ||||||
|  | function setCredentialStorePath(credentialStorePath) { | ||||||
|  |     core.saveState('credentialStorePath', credentialStorePath); | ||||||
|  | } | ||||||
|  | exports.setCredentialStorePath = setCredentialStorePath; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /***/ }), | /***/ }), | ||||||
|  |  | ||||||
|  | @ -34,9 +34,10 @@ export function createAuthHelper( | ||||||
| class GitAuthHelper { | class GitAuthHelper { | ||||||
|   private readonly git: IGitCommandManager |   private readonly git: IGitCommandManager | ||||||
|   private readonly settings: IGitSourceSettings |   private readonly settings: IGitSourceSettings | ||||||
|   private readonly tokenConfigKey: string |   private readonly credentialConfigKey: string | ||||||
|   private readonly tokenConfigValue: string |   private readonly credentialConfigValue: string | ||||||
|   private readonly tokenPlaceholderConfigValue: string |   private readonly tokenCredential: string | ||||||
|  |   private readonly credentialStorePath: string | ||||||
|   private readonly insteadOfKey: string |   private readonly insteadOfKey: string | ||||||
|   private readonly insteadOfValues: string[] = [] |   private readonly insteadOfValues: string[] = [] | ||||||
|   private sshCommand = '' |   private sshCommand = '' | ||||||
|  | @ -51,16 +52,20 @@ class GitAuthHelper { | ||||||
|     this.git = gitCommandManager |     this.git = gitCommandManager | ||||||
|     this.settings = gitSourceSettings || ({} as unknown as IGitSourceSettings) |     this.settings = gitSourceSettings || ({} as unknown as IGitSourceSettings) | ||||||
| 
 | 
 | ||||||
|     // Token auth header
 |     this.credentialConfigKey = `credential.helper` | ||||||
|  |     const runnerTemp = process.env['RUNNER_TEMP'] || '' | ||||||
|  |     assert.ok(runnerTemp, 'RUNNER_TEMP is not defined') | ||||||
|  |     const uniqueId = uuid() | ||||||
|  |     this.credentialStorePath = path.join( | ||||||
|  |       runnerTemp, | ||||||
|  |       `${uniqueId}_credential_store` | ||||||
|  |     ) | ||||||
|  |     this.credentialConfigValue = `store --file ${this.credentialStorePath}` | ||||||
|  | 
 | ||||||
|     const serverUrl = urlHelper.getServerUrl(this.settings.githubServerUrl) |     const serverUrl = urlHelper.getServerUrl(this.settings.githubServerUrl) | ||||||
|     this.tokenConfigKey = `http.${serverUrl.origin}/.extraheader` // "origin" is SCHEME://HOSTNAME[:PORT]
 |     serverUrl.username = `x-access-token` | ||||||
|     const basicCredential = Buffer.from( |     serverUrl.password = this.settings.authToken | ||||||
|       `x-access-token:${this.settings.authToken}`, |     this.tokenCredential = serverUrl.href | ||||||
|       'utf8' |  | ||||||
|     ).toString('base64') |  | ||||||
|     core.setSecret(basicCredential) |  | ||||||
|     this.tokenPlaceholderConfigValue = `AUTHORIZATION: basic ***` |  | ||||||
|     this.tokenConfigValue = `AUTHORIZATION: basic ${basicCredential}` |  | ||||||
| 
 | 
 | ||||||
|     // Instead of SSH URL
 |     // Instead of SSH URL
 | ||||||
|     this.insteadOfKey = `url.${serverUrl.origin}/.insteadOf` // "origin" is SCHEME://HOSTNAME[:PORT]
 |     this.insteadOfKey = `url.${serverUrl.origin}/.insteadOf` // "origin" is SCHEME://HOSTNAME[:PORT]
 | ||||||
|  | @ -143,7 +148,7 @@ class GitAuthHelper { | ||||||
|       core.info( |       core.info( | ||||||
|         'Encountered an error when attempting to configure token. Attempting unconfigure.' |         'Encountered an error when attempting to configure token. Attempting unconfigure.' | ||||||
|       ) |       ) | ||||||
|       await this.git.tryConfigUnset(this.tokenConfigKey, true) |       await this.git.tryConfigUnset(this.credentialConfigKey, true) | ||||||
|       throw err |       throw err | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | @ -153,23 +158,19 @@ class GitAuthHelper { | ||||||
|     await this.removeGitConfig(this.insteadOfKey, true) |     await this.removeGitConfig(this.insteadOfKey, true) | ||||||
| 
 | 
 | ||||||
|     if (this.settings.persistCredentials) { |     if (this.settings.persistCredentials) { | ||||||
|       // Configure a placeholder value. This approach avoids the credential being captured
 |       if (this.settings.customCredentialHelper) { | ||||||
|       // by process creation audit events, which are commonly logged. For more information,
 |         await this.git.submoduleForeach( | ||||||
|       // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 |           `sh -c "git config --local --add '${this.credentialConfigKey}' '${this.settings.customCredentialHelper}' && git config --local 'credential.useHttpPath' 'true'"`, | ||||||
|       const output = await this.git.submoduleForeach( |  | ||||||
|         // wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline
 |  | ||||||
|         `sh -c "git config --local '${this.tokenConfigKey}' '${this.tokenPlaceholderConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url"`, |  | ||||||
|           this.settings.nestedSubmodules |           this.settings.nestedSubmodules | ||||||
|         ) |         ) | ||||||
| 
 |  | ||||||
|       // Replace the placeholder
 |  | ||||||
|       const configPaths: string[] = |  | ||||||
|         output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || [] |  | ||||||
|       for (const configPath of configPaths) { |  | ||||||
|         core.debug(`Replacing token placeholder in '${configPath}'`) |  | ||||||
|         await this.replaceTokenPlaceholder(configPath) |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  |       await this.git.submoduleForeach( | ||||||
|  |         // wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline
 | ||||||
|  |         `sh -c "git config --local --add '${this.credentialConfigKey}' '${this.credentialConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url"`, | ||||||
|  |         this.settings.nestedSubmodules | ||||||
|  |       ) | ||||||
|  | 
 | ||||||
|       if (this.settings.sshKey) { |       if (this.settings.sshKey) { | ||||||
|         // Configure core.sshCommand
 |         // Configure core.sshCommand
 | ||||||
|         await this.git.submoduleForeach( |         await this.git.submoduleForeach( | ||||||
|  | @ -210,7 +211,7 @@ class GitAuthHelper { | ||||||
|     const runnerTemp = process.env['RUNNER_TEMP'] || '' |     const runnerTemp = process.env['RUNNER_TEMP'] || '' | ||||||
|     assert.ok(runnerTemp, 'RUNNER_TEMP is not defined') |     assert.ok(runnerTemp, 'RUNNER_TEMP is not defined') | ||||||
|     const uniqueId = uuid() |     const uniqueId = uuid() | ||||||
|     this.sshKeyPath = path.join(runnerTemp, uniqueId) |     this.sshKeyPath = path.join(runnerTemp, `${uniqueId}_ssh_key`) | ||||||
|     stateHelper.setSshKeyPath(this.sshKeyPath) |     stateHelper.setSshKeyPath(this.sshKeyPath) | ||||||
|     await fs.promises.mkdir(runnerTemp, {recursive: true}) |     await fs.promises.mkdir(runnerTemp, {recursive: true}) | ||||||
|     await fs.promises.writeFile( |     await fs.promises.writeFile( | ||||||
|  | @ -282,40 +283,31 @@ class GitAuthHelper { | ||||||
|       'Unexpected configureToken parameter combinations' |       'Unexpected configureToken parameter combinations' | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|  |     stateHelper.setCredentialStorePath(this.credentialStorePath) | ||||||
|  |     await fs.promises.writeFile(this.credentialStorePath, this.tokenCredential) | ||||||
|  | 
 | ||||||
|     // Default config path
 |     // Default config path
 | ||||||
|     if (!configPath && !globalConfig) { |     if (!configPath && !globalConfig) { | ||||||
|       configPath = path.join(this.git.getWorkingDirectory(), '.git', 'config') |       configPath = path.join(this.git.getWorkingDirectory(), '.git', 'config') | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Configure a placeholder value. This approach avoids the credential being captured
 |     if (this.settings.customCredentialHelper) { | ||||||
|     // 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
 |  | ||||||
|       await this.git.config( |       await this.git.config( | ||||||
|       this.tokenConfigKey, |         this.credentialConfigKey, | ||||||
|       this.tokenPlaceholderConfigValue, |         this.settings.customCredentialHelper, | ||||||
|       globalConfig |         globalConfig, | ||||||
|  |         true | ||||||
|       ) |       ) | ||||||
| 
 | 
 | ||||||
|     // Replace the placeholder
 |       await this.git.config('credential.useHttpPath', 'true', globalConfig) | ||||||
|     await this.replaceTokenPlaceholder(configPath || '') |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|   private async replaceTokenPlaceholder(configPath: string): Promise<void> { |     await this.git.config( | ||||||
|     assert.ok(configPath, 'configPath is not defined') |       this.credentialConfigKey, | ||||||
|     let content = (await fs.promises.readFile(configPath)).toString() |       this.credentialConfigValue, | ||||||
|     const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue) |       globalConfig, | ||||||
|     if ( |       true | ||||||
|       placeholderIndex < 0 || |  | ||||||
|       placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue) |  | ||||||
|     ) { |  | ||||||
|       throw new Error(`Unable to replace auth placeholder in ${configPath}`) |  | ||||||
|     } |  | ||||||
|     assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined') |  | ||||||
|     content = content.replace( |  | ||||||
|       this.tokenPlaceholderConfigValue, |  | ||||||
|       this.tokenConfigValue |  | ||||||
|     ) |     ) | ||||||
|     await fs.promises.writeFile(configPath, content) |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async removeSsh(): Promise<void> { |   private async removeSsh(): Promise<void> { | ||||||
|  | @ -346,8 +338,20 @@ class GitAuthHelper { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async removeToken(): Promise<void> { |   private async removeToken(): Promise<void> { | ||||||
|     // HTTP extra header
 |     // Credential Helper
 | ||||||
|     await this.removeGitConfig(this.tokenConfigKey) |     const credentialStorePath = | ||||||
|  |       this.credentialStorePath || stateHelper.CredentialStorePath | ||||||
|  |     if (credentialStorePath) { | ||||||
|  |       try { | ||||||
|  |         await io.rmRF(credentialStorePath) | ||||||
|  |       } catch (err) { | ||||||
|  |         core.debug(`${(err as any)?.message ?? err}`) | ||||||
|  |         core.warning( | ||||||
|  |           `Failed to remove credential store '${credentialStorePath}'` | ||||||
|  |         ) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     await this.removeGitConfig(this.credentialConfigKey) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async removeGitConfig( |   private async removeGitConfig( | ||||||
|  |  | ||||||
|  | @ -104,6 +104,11 @@ export interface IGitSourceSettings { | ||||||
|    */ |    */ | ||||||
|   persistCredentials: boolean |   persistCredentials: boolean | ||||||
| 
 | 
 | ||||||
|  |   /** | ||||||
|  |    * Use following command/script as value for "credential.<URL>.helper" | ||||||
|  |    */ | ||||||
|  |   customCredentialHelper: string | undefined | ||||||
|  | 
 | ||||||
|   /** |   /** | ||||||
|    * Organization ID for the currently running workflow (used for auth settings) |    * Organization ID for the currently running workflow (used for auth settings) | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|  | @ -149,6 +149,9 @@ export async function getInputs(): Promise<IGitSourceSettings> { | ||||||
|   result.persistCredentials = |   result.persistCredentials = | ||||||
|     (core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE' |     (core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE' | ||||||
| 
 | 
 | ||||||
|  |   // Custom credential helper
 | ||||||
|  |   result.customCredentialHelper = core.getInput('custom-credential-helper') | ||||||
|  | 
 | ||||||
|   // Workflow organization ID
 |   // Workflow organization ID
 | ||||||
|   result.workflowOrganizationId = |   result.workflowOrganizationId = | ||||||
|     await workflowContextHelper.getOrganizationId() |     await workflowContextHelper.getOrganizationId() | ||||||
|  |  | ||||||
|  | @ -25,6 +25,11 @@ export const SshKeyPath = core.getState('sshKeyPath') | ||||||
|  */ |  */ | ||||||
| export const SshKnownHostsPath = core.getState('sshKnownHostsPath') | export const SshKnownHostsPath = core.getState('sshKnownHostsPath') | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * The credential store path for git-credential-store | ||||||
|  |  */ | ||||||
|  | export const CredentialStorePath = core.getState('credentialStorePath') | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Save the repository path so the POST action can retrieve the value. |  * Save the repository path so the POST action can retrieve the value. | ||||||
|  */ |  */ | ||||||
|  | @ -58,3 +63,10 @@ export function setSafeDirectory() { | ||||||
| if (!IsPost) { | if (!IsPost) { | ||||||
|   core.saveState('isPost', 'true') |   core.saveState('isPost', 'true') | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Save the credential store path so the POST action can retrieve the value. | ||||||
|  |  */ | ||||||
|  | export function setCredentialStorePath(credentialStorePath: string) { | ||||||
|  |   core.saveState('credentialStorePath', credentialStorePath) | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Julius Lehmann
						Julius Lehmann