0
0
mirror of https://github.com/renovatebot/renovate.git synced 2024-12-22 13:38:32 +00:00
renovatebot_renovate/lib/util/git/private-key.spec.ts
Ben Foster 9b45b6b152
fix(git): fix gpg commit signing (#32483) (#32543)
Co-authored-by: Rhys Arkins <rhys@arkins.net>
2024-11-16 10:37:42 +00:00

140 lines
4.9 KiB
TypeScript

import os from 'node:os';
import fs from 'fs-extra';
import { any, mockDeep } from 'jest-mock-extended';
import upath from 'upath';
import { Fixtures } from '../../../test/fixtures';
import { mockedExtended } from '../../../test/util';
import * as exec_ from '../exec';
import { configSigningKey, writePrivateKey } from './private-key';
import { setPrivateKey } from '.';
jest.mock('fs-extra', () =>
jest
.requireActual<
typeof import('../../../test/fixtures')
>('../../../test/fixtures')
.fsExtra(),
);
jest.mock('../exec', () => mockDeep());
const exec = mockedExtended(exec_);
describe('util/git/private-key', () => {
describe('writePrivateKey()', () => {
beforeEach(() => {
Fixtures.reset();
});
it('returns if no private key', async () => {
await expect(writePrivateKey()).resolves.not.toThrow();
await expect(configSigningKey('/tmp/some-repo')).resolves.not.toThrow();
});
it('throws error if failing', async () => {
setPrivateKey('some-key');
exec.exec.calledWith(any()).mockResolvedValue({ stdout: '', stderr: '' });
exec.exec
.calledWith(
`gpg --batch --no-tty --import ${upath.join(os.tmpdir() + '/git-private-gpg.key')}`,
)
.mockRejectedValueOnce({
stderr: `something wrong`,
stdout: '',
});
await expect(writePrivateKey()).rejects.toThrow();
});
it('imports the private GPG key', async () => {
const publicKey = 'BADC0FFEE';
const repoDir = '/tmp/some-repo';
exec.exec.calledWith(any()).mockResolvedValue({ stdout: '', stderr: '' });
exec.exec
.calledWith(
`gpg --batch --no-tty --import ${upath.join(os.tmpdir() + '/git-private-gpg.key')}`,
)
.mockResolvedValueOnce({
stderr: `gpg: key ${publicKey}: secret key imported\nfoo\n`,
stdout: '',
});
setPrivateKey('some-key');
await expect(writePrivateKey()).resolves.not.toThrow();
await expect(configSigningKey(repoDir)).resolves.not.toThrow();
expect(exec.exec).toHaveBeenCalledWith(
`git config user.signingkey ${publicKey}`,
{ cwd: repoDir },
);
expect(exec.exec).toHaveBeenCalledWith('git config commit.gpgsign true', {
cwd: repoDir,
});
expect(exec.exec).toHaveBeenCalledWith('git config gpg.format openpgp', {
cwd: repoDir,
});
});
it('does not import the key again', async () => {
await expect(writePrivateKey()).resolves.not.toThrow();
await expect(configSigningKey('/tmp/some-repo')).resolves.not.toThrow();
});
it('throws error if the private SSH key has a passphrase', async () => {
const privateKeyFile = upath.join(os.tmpdir() + '/git-private-ssh.key');
exec.exec.calledWith(any()).mockResolvedValue({ stdout: '', stderr: '' });
exec.exec
.calledWith(`ssh-keygen -y -P "" -f ${privateKeyFile}`)
.mockRejectedValueOnce({
stderr: `Load key "${privateKeyFile}": incorrect passphrase supplied to decrypt private key`,
stdout: '',
});
setPrivateKey(`\
-----BEGIN OPENSSH PRIVATE KEY-----
some-private-key with-passphrase
some-private-key with-passphrase
-----END OPENSSH PRIVATE KEY-----
`);
await expect(writePrivateKey()).rejects.toThrow();
});
it('imports the private SSH key', async () => {
const privateKey = `\
-----BEGIN OPENSSH PRIVATE KEY-----
some-private-key
some-private-key
-----END OPENSSH PRIVATE KEY-----
`;
const privateKeyFile = upath.join(os.tmpdir() + '/git-private-ssh.key');
const publicKeyFile = `${privateKeyFile}.pub`;
const publicKey = 'some-public-key';
const repoDir = '/tmp/some-repo';
exec.exec.calledWith(any()).mockResolvedValue({ stdout: '', stderr: '' });
exec.exec
.calledWith(`ssh-keygen -y -P "" -f ${privateKeyFile}`)
.mockResolvedValue({
stderr: '',
stdout: publicKey,
});
setPrivateKey(privateKey);
await expect(writePrivateKey()).resolves.not.toThrow();
await expect(configSigningKey(repoDir)).resolves.not.toThrow();
expect(exec.exec).toHaveBeenCalledWith(
`git config user.signingkey ${privateKeyFile}`,
{ cwd: repoDir },
);
const privateKeyFileMode = (await fs.stat(privateKeyFile)).mode;
expect((privateKeyFileMode & 0o777).toString(8)).toBe('600');
expect((await fs.readFile(privateKeyFile)).toString()).toEqual(
privateKey,
);
expect((await fs.readFile(publicKeyFile)).toString()).toEqual(publicKey);
expect(exec.exec).toHaveBeenCalledWith('git config commit.gpgsign true', {
cwd: repoDir,
});
expect(exec.exec).toHaveBeenCalledWith('git config gpg.format ssh', {
cwd: repoDir,
});
process.emit('exit', 0);
expect(fs.existsSync(privateKeyFile)).toBeFalse();
expect(fs.existsSync(publicKeyFile)).toBeFalse();
});
});
});