1
0
mirror of https://gitea.com/actions/checkout.git synced 2025-04-30 20:36:11 +08:00
This commit is contained in:
lehmanju 2024-05-30 08:35:45 +00:00 committed by GitHub
commit 3bc2bad45e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 47 additions and 93 deletions

View File

@ -33,6 +33,10 @@ jobs:
matrix: matrix:
runs-on: [ubuntu-latest, macos-latest, windows-latest] runs-on: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }}
env:
GIT_TRACE: 1
GIT_TRANSFER_TRACE: 1
GIT_CURL_VERBOSE: 1
steps: steps:
# Clone this repo # Clone this repo

60
dist/index.js vendored
View File

@ -164,15 +164,15 @@ class GitAuthHelper {
this.sshKeyPath = ''; this.sshKeyPath = '';
this.sshKnownHostsPath = ''; this.sshKnownHostsPath = '';
this.temporaryHomePath = ''; this.temporaryHomePath = '';
this.credentialStorePath = '';
this.git = gitCommandManager; this.git = gitCommandManager;
this.settings = gitSourceSettings || {}; this.settings = gitSourceSettings || {};
// Token auth header // Token auth header
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] this.tokenConfigKey = `credential.${serverUrl.origin}/.helper`; // "origin" is SCHEME://HOSTNAME[:PORT]
const basicCredential = Buffer.from(`x-access-token:${this.settings.authToken}`, 'utf8').toString('base64'); serverUrl.username = `x-access-token`;
core.setSecret(basicCredential); serverUrl.password = this.settings.authToken;
this.tokenPlaceholderConfigValue = `AUTHORIZATION: basic ***`; this.tokenCredential = serverUrl.href;
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}:`);
@ -186,6 +186,7 @@ class GitAuthHelper {
yield this.removeAuth(); yield this.removeAuth();
// Configure new values // Configure new values
yield this.configureSsh(); yield this.configureSsh();
yield this.writeTokenCredential();
yield this.configureToken(); yield this.configureToken();
}); });
} }
@ -261,13 +262,7 @@ class GitAuthHelper {
// refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing // 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( 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 // 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); `sh -c "git config --local '${this.tokenConfigKey}' '"store --file ${this.credentialStorePath}"' && 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);
}
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);
@ -361,26 +356,16 @@ class GitAuthHelper {
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 yield this.git.config(this.tokenConfigKey, `"store --file ${this.credentialStorePath}"`, globalConfig);
// 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, globalConfig);
// Replace the placeholder
yield this.replaceTokenPlaceholder(configPath || '');
}); });
} }
replaceTokenPlaceholder(configPath) { writeTokenCredential() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
assert.ok(configPath, 'configPath is not defined'); const runnerTemp = process.env['RUNNER_TEMP'] || '';
let content = (yield fs.promises.readFile(configPath)).toString(); assert.ok(runnerTemp, 'RUNNER_TEMP is not defined');
const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue); const uniqueId = (0, uuid_1.v4)();
if (placeholderIndex < 0 || this.credentialStorePath = path.join(runnerTemp, uniqueId);
placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue)) { yield fs.promises.writeFile(this.credentialStorePath, this.tokenCredential);
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() {
@ -886,24 +871,20 @@ class GitCommandManager {
for (const key of Object.keys(this.gitEnv)) { for (const key of Object.keys(this.gitEnv)) {
env[key] = this.gitEnv[key]; env[key] = this.gitEnv[key];
} }
const defaultListener = {
stdout: (data) => {
stdout.push(data.toString());
}
};
const mergedListeners = Object.assign(Object.assign({}, defaultListener), customListeners);
const stdout = [];
const options = { const options = {
cwd: this.workingDirectory, cwd: this.workingDirectory,
env, env,
silent, silent,
ignoreReturnCode: allowAllExitCodes, ignoreReturnCode: allowAllExitCodes,
listeners: mergedListeners listeners: customListeners
}; };
result.exitCode = yield exec.exec(`"${this.gitPath}"`, args, options); let execOutput = yield exec.getExecOutput(`"${this.gitPath}"`, args, options);
result.stdout = stdout.join(''); result.exitCode = execOutput.exitCode;
result.stdout = execOutput.stdout;
result.stderr = execOutput.stderr;
core.debug(result.exitCode.toString()); core.debug(result.exitCode.toString());
core.debug(result.stdout); core.debug(result.stdout);
core.debug(result.stderr);
return result; return result;
}); });
} }
@ -975,6 +956,7 @@ class GitCommandManager {
class GitOutput { class GitOutput {
constructor() { constructor() {
this.stdout = ''; this.stdout = '';
this.stderr = '';
this.exitCode = 0; this.exitCode = 0;
} }
} }

View File

@ -35,14 +35,14 @@ class GitAuthHelper {
private readonly git: IGitCommandManager private readonly git: IGitCommandManager
private readonly settings: IGitSourceSettings private readonly settings: IGitSourceSettings
private readonly tokenConfigKey: string private readonly tokenConfigKey: string
private readonly tokenConfigValue: string private readonly tokenCredential: string
private readonly tokenPlaceholderConfigValue: string
private readonly insteadOfKey: string private readonly insteadOfKey: string
private readonly insteadOfValues: string[] = [] private readonly insteadOfValues: string[] = []
private sshCommand = '' private sshCommand = ''
private sshKeyPath = '' private sshKeyPath = ''
private sshKnownHostsPath = '' private sshKnownHostsPath = ''
private temporaryHomePath = '' private temporaryHomePath = ''
private credentialStorePath = ''
constructor( constructor(
gitCommandManager: IGitCommandManager, gitCommandManager: IGitCommandManager,
@ -53,14 +53,10 @@ class GitAuthHelper {
// Token auth header // Token auth header
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] this.tokenConfigKey = `credential.${serverUrl.origin}/.helper` // "origin" is SCHEME://HOSTNAME[:PORT]
const basicCredential = Buffer.from( serverUrl.username = `x-access-token`
`x-access-token:${this.settings.authToken}`, serverUrl.password = this.settings.authToken
'utf8' this.tokenCredential = serverUrl.href
).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]
@ -78,6 +74,7 @@ class GitAuthHelper {
// Configure new values // Configure new values
await this.configureSsh() await this.configureSsh()
await this.writeTokenCredential()
await this.configureToken() await this.configureToken()
} }
@ -158,18 +155,10 @@ class GitAuthHelper {
// refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
const output = await this.git.submoduleForeach( 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 // 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"`, `sh -c "git config --local '${this.tokenConfigKey}' '"store --file ${this.credentialStorePath}"' && 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)
}
if (this.settings.sshKey) { if (this.settings.sshKey) {
// Configure core.sshCommand // Configure core.sshCommand
await this.git.submoduleForeach( await this.git.submoduleForeach(
@ -287,35 +276,19 @@ class GitAuthHelper {
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
// 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.tokenConfigKey,
this.tokenPlaceholderConfigValue, `"store --file ${this.credentialStorePath}"`,
globalConfig globalConfig
) )
// Replace the placeholder
await this.replaceTokenPlaceholder(configPath || '')
} }
private async replaceTokenPlaceholder(configPath: string): Promise<void> { private async writeTokenCredential(): Promise<void> {
assert.ok(configPath, 'configPath is not defined') const runnerTemp = process.env['RUNNER_TEMP'] || ''
let content = (await fs.promises.readFile(configPath)).toString() assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue) const uniqueId = uuid()
if ( this.credentialStorePath = path.join(runnerTemp, uniqueId)
placeholderIndex < 0 || await fs.promises.writeFile(this.credentialStorePath, this.tokenCredential)
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> {

View File

@ -522,28 +522,22 @@ class GitCommandManager {
env[key] = this.gitEnv[key] env[key] = this.gitEnv[key]
} }
const defaultListener = {
stdout: (data: Buffer) => {
stdout.push(data.toString())
}
}
const mergedListeners = {...defaultListener, ...customListeners}
const stdout: string[] = []
const options = { const options = {
cwd: this.workingDirectory, cwd: this.workingDirectory,
env, env,
silent, silent,
ignoreReturnCode: allowAllExitCodes, ignoreReturnCode: allowAllExitCodes,
listeners: mergedListeners listeners: customListeners
} }
result.exitCode = await exec.exec(`"${this.gitPath}"`, args, options) let execOutput = await exec.getExecOutput(`"${this.gitPath}"`, args, options)
result.stdout = stdout.join('') result.exitCode = execOutput.exitCode
result.stdout = execOutput.stdout
result.stderr = execOutput.stderr
core.debug(result.exitCode.toString()) core.debug(result.exitCode.toString())
core.debug(result.stdout) core.debug(result.stdout)
core.debug(result.stderr)
return result return result
} }
@ -631,5 +625,6 @@ class GitCommandManager {
class GitOutput { class GitOutput {
stdout = '' stdout = ''
stderr = ''
exitCode = 0 exitCode = 0
} }