import * as core from '@actions/core' import * as fs from 'fs' import {GitCommandManager} from './git-command-manager' import * as path from 'path' import {URL} from 'url' import * as utils from './utils' export class GitAuthHelper { private git: GitCommandManager private gitConfigPath = '' private workingDirectory: string private safeDirectoryConfigKey = 'safe.directory' private safeDirectoryAdded = false private extraheaderConfigKey: string private extraheaderConfigPlaceholderValue = 'AUTHORIZATION: basic ***' private extraheaderConfigValueRegex = '^AUTHORIZATION:' private persistedExtraheaderConfigValue = '' constructor(git: GitCommandManager) { this.git = git this.workingDirectory = this.git.getWorkingDirectory() const serverUrl = this.getServerUrl() this.extraheaderConfigKey = `http.${serverUrl.origin}/.extraheader` } async addSafeDirectory(): Promise { const exists = await this.git.configExists( this.safeDirectoryConfigKey, this.workingDirectory, true ) if (!exists) { await this.git.config( this.safeDirectoryConfigKey, this.workingDirectory, true, true ) this.safeDirectoryAdded = true } } async removeSafeDirectory(): Promise { if (this.safeDirectoryAdded) { await this.git.tryConfigUnset( this.safeDirectoryConfigKey, this.workingDirectory, true ) } } async savePersistedAuth(): Promise { // Save and unset persisted extraheader credential in git config if it exists this.persistedExtraheaderConfigValue = await this.getAndUnset() } async restorePersistedAuth(): Promise { if (this.persistedExtraheaderConfigValue) { try { await this.setExtraheaderConfig(this.persistedExtraheaderConfigValue) core.info('Persisted git credentials restored') } catch (e) { core.warning(utils.getErrorMessage(e)) } } } async configureToken(token: string): Promise { // Encode and configure the basic credential for HTTPS access const basicCredential = Buffer.from( `x-access-token:${token}`, 'utf8' ).toString('base64') core.setSecret(basicCredential) const extraheaderConfigValue = `AUTHORIZATION: basic ${basicCredential}` await this.setExtraheaderConfig(extraheaderConfigValue) } async removeAuth(): Promise { await this.getAndUnset() } private async setExtraheaderConfig( extraheaderConfigValue: string ): Promise { // Configure a placeholder value. 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 // See https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts#L267-L274 await this.git.config( this.extraheaderConfigKey, this.extraheaderConfigPlaceholderValue ) // Replace the placeholder await this.gitConfigStringReplace( this.extraheaderConfigPlaceholderValue, extraheaderConfigValue ) } private async getAndUnset(): Promise { let configValue = '' // Save and unset persisted extraheader credential in git config if it exists if ( await this.git.configExists( this.extraheaderConfigKey, this.extraheaderConfigValueRegex ) ) { configValue = await this.git.getConfigValue( this.extraheaderConfigKey, this.extraheaderConfigValueRegex ) if ( await this.git.tryConfigUnset( this.extraheaderConfigKey, this.extraheaderConfigValueRegex ) ) { core.info(`Unset config key '${this.extraheaderConfigKey}'`) } else { core.warning( `Failed to unset config key '${this.extraheaderConfigKey}'` ) } } return configValue } private async gitConfigStringReplace( find: string, replace: string ): Promise { if (this.gitConfigPath.length === 0) { const gitDir = await this.git.getGitDirectory() this.gitConfigPath = path.join(this.workingDirectory, gitDir, 'config') } let content = (await fs.promises.readFile(this.gitConfigPath)).toString() const index = content.indexOf(find) if (index < 0 || index != content.lastIndexOf(find)) { throw new Error(`Unable to replace '${find}' in ${this.gitConfigPath}`) } content = content.replace(find, replace) await fs.promises.writeFile(this.gitConfigPath, content) } private getServerUrl(): URL { return new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com') } }