Skip to content
Merged
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
2 changes: 1 addition & 1 deletion lib/index.js

Large diffs are not rendered by default.

20 changes: 15 additions & 5 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import * as core from '@actions/core';
import * as path from 'path';
import simpleGit, {Response} from 'simple-git';
import {checkInputs, getInput, logOutputs, setOutput} from './io';
import {log, matchGitArgs, parseInputArray} from './util';
import {
log,
matchGitArgs,
parseInputArray,
pickGitIdentityConfig,
} from './util';

const baseDir = path.join(process.cwd(), getInput('cwd') || '');
const git = simpleGit({baseDir});
Expand Down Expand Up @@ -50,10 +55,15 @@ core.info(`Running in ${baseDir}`);
.addConfig('author.name', getInput('author_name'), undefined, log)
.addConfig('committer.email', getInput('committer_email'), undefined, log)
.addConfig('committer.name', getInput('committer_name'), undefined, log);
core.debug(
'> Current git config\n' +
JSON.stringify((await git.listConfig()).all, null, 2),
);
if (core.isDebug()) {
const identity = pickGitIdentityConfig((await git.listConfig()).all);
core.debug(
Object.keys(identity).length
? '> Current git identity config\n' +
JSON.stringify(identity, null, 2)
: '> Git identity config set (no identity keys present in listConfig)',
);
}

let fetchOption: string | boolean;
try {
Expand Down
26 changes: 26 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,32 @@ export function log(err: any, data?: any) {
if (err) core.error(err);
}

/** Git identity keys this action sets; safe to log (no credentials). */
const GIT_IDENTITY_CONFIG_KEYS = [
'user.name',
'user.email',
'author.name',
'author.email',
'committer.name',
'committer.email',
] as const;

/**
* Picks only git identity config entries for logging.
* Never includes credential-bearing keys (extraheader, remote URLs, etc.).
*/
export function pickGitIdentityConfig(
config: Record<string, unknown>,
): Record<string, unknown> {
const result: Record<string, unknown> = {};
for (const key of GIT_IDENTITY_CONFIG_KEYS) {
if (Object.prototype.hasOwnProperty.call(config, key)) {
result[key] = config[key];
}
}
return result;
}

/**
* Ensures `name` is safe to pass as a single git branch/ref positional argument.
* Rejects empty values, leading hyphens (git option injection), whitespace/control
Expand Down
42 changes: 42 additions & 0 deletions test/util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
assertValidBranchName,
matchGitArgs,
parseInputArray,
pickGitIdentityConfig,
} from '../src/util';

describe('parseInputArray', () => {
Expand Down Expand Up @@ -170,3 +171,44 @@ describe('matchGitArgs', () => {
expect(() => matchGitArgs('--exe=evil')).toThrow(/not allowed/);
});
});

describe('pickGitIdentityConfig', () => {
it('keeps only identity keys', () => {
const picked = pickGitIdentityConfig({
'user.name': 'Alice',
'user.email': 'alice@example.com',
'author.name': 'Alice',
'author.email': 'alice@example.com',
'committer.name': 'Bot',
'committer.email': 'bot@example.com',
'http.https://github.com/.extraheader':
'AUTHORIZATION: basic dGVzdDp0b2tlbg==',
'credential.helper': 'store',
'remote.origin.url':
'https://x-access-token:ghp_secret@github.com/o/r.git',
'core.sshCommand': 'ssh -i /secrets/id_rsa',
});

expect(picked).toStrictEqual({
'user.name': 'Alice',
'user.email': 'alice@example.com',
'author.name': 'Alice',
'author.email': 'alice@example.com',
'committer.name': 'Bot',
'committer.email': 'bot@example.com',
});
expect(picked).not.toHaveProperty('http.https://github.com/.extraheader');
expect(picked).not.toHaveProperty('credential.helper');
expect(picked).not.toHaveProperty('remote.origin.url');
expect(picked).not.toHaveProperty('core.sshCommand');
});

it('returns an empty object when no identity keys are present', () => {
expect(
pickGitIdentityConfig({
'http.https://github.com/.extraheader':
'AUTHORIZATION: basic dGVzdDp0b2tlbg==',
}),
).toStrictEqual({});
});
});