renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
123 lines • 5.22 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.setPrivateKey = setPrivateKey;
exports.writePrivateKey = writePrivateKey;
exports.configSigningKey = configSigningKey;
const tslib_1 = require("tslib");
const node_os_1 = tslib_1.__importDefault(require("node:os"));
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const upath_1 = tslib_1.__importDefault(require("upath"));
const error_messages_1 = require("../../constants/error-messages");
const logger_1 = require("../../logger");
const exec_1 = require("../exec");
const regex_1 = require("../regex");
const sanitize_1 = require("../sanitize");
const sshKeyRegex = (0, regex_1.regEx)(/-----BEGIN ([A-Z ]+ )?PRIVATE KEY-----.*?-----END ([A-Z]+ )?PRIVATE KEY-----/, 's');
let gitPrivateKey;
class PrivateKey {
key;
keyId;
constructor(key) {
this.key = key;
(0, sanitize_1.addSecretForSanitizing)(this.key, 'global');
logger_1.logger.debug('gitPrivateKey: successfully set (but not yet written/configured)');
}
async writeKey() {
try {
this.keyId ??= await this.importKey();
logger_1.logger.debug('gitPrivateKey: imported');
}
catch (err) {
logger_1.logger.warn({ err }, 'gitPrivateKey: error importing');
throw new Error(error_messages_1.PLATFORM_GPG_FAILED);
}
}
async configSigningKey(cwd) {
logger_1.logger.debug('gitPrivateKey: configuring commit signing');
// TODO: types (#22198)
await (0, exec_1.exec)(`git config user.signingkey ${this.keyId}`, { cwd });
await (0, exec_1.exec)(`git config commit.gpgsign true`, { cwd });
await (0, exec_1.exec)(`git config gpg.format ${this.gpgFormat}`, { cwd });
}
}
class GPGKey extends PrivateKey {
gpgFormat = 'openpgp';
constructor(key) {
super(key.trim());
}
async importKey() {
const keyFileName = upath_1.default.join(node_os_1.default.tmpdir() + '/git-private-gpg.key');
await fs_extra_1.default.outputFile(keyFileName, this.key);
const { stdout, stderr } = await (0, exec_1.exec)(
// --batch --no-tty flags allow Renovate to skip warnings about unsupported algorithms in the key
`gpg --batch --no-tty --import ${keyFileName}`);
logger_1.logger.debug({ stdout, stderr }, 'Private key import result');
await fs_extra_1.default.remove(keyFileName);
return `${stdout}${stderr}`
.split(regex_1.newlineRegex)
.find((line) => line.includes('secret key imported'))
?.replace('gpg: key ', '')
.split(':')
.shift();
}
}
class SSHKey extends PrivateKey {
gpgFormat = 'ssh';
async importKey() {
const keyFileName = upath_1.default.join(node_os_1.default.tmpdir() + '/git-private-ssh.key');
if (await this.hasPassphrase(keyFileName)) {
throw new Error('SSH key must have an empty passhprase');
}
await fs_extra_1.default.outputFile(keyFileName, this.key.replace(/\n?$/, '\n'));
process.on('exit', () => fs_extra_1.default.removeSync(keyFileName));
await fs_extra_1.default.chmod(keyFileName, 0o600);
// HACK: `git` calls `ssh-keygen -Y sign ...` internally for SSH-based
// commit signing. Technically, only the private key is needed for signing,
// but `ssh-keygen` has an implementation quirk which requires also the
// public key file to exist. Therefore, we derive the public key from the
// private key just to satisfy `ssh-keygen` until the problem has been
// resolved.
// https://github.com/renovatebot/renovate/issues/18197#issuecomment-2152333710
const { stdout } = await (0, exec_1.exec)(`ssh-keygen -y -P "" -f ${keyFileName}`);
const pubFileName = `${keyFileName}.pub`;
await fs_extra_1.default.outputFile(pubFileName, stdout);
process.on('exit', () => fs_extra_1.default.removeSync(pubFileName));
return keyFileName;
}
async hasPassphrase(keyFileName) {
try {
await (0, exec_1.exec)(`ssh-keygen -y -P "" -f ${keyFileName}`);
}
catch (err) {
return err.stderr.includes('incorrect passphrase supplied to decrypt private key');
}
return false;
}
}
function getPrivateKeyFormat(key) {
return sshKeyRegex.test(key) ? 'ssh' : 'gpg';
}
function createPrivateKey(key) {
switch (getPrivateKeyFormat(key)) {
case 'gpg':
logger_1.logger.debug('gitPrivateKey: GPG key detected');
return new GPGKey(key);
case 'ssh':
logger_1.logger.debug('gitPrivateKey: SSH key detected');
return new SSHKey(key);
}
}
function setPrivateKey(key) {
if (!is_1.default.nonEmptyStringAndNotWhitespace(key)) {
return;
}
gitPrivateKey = createPrivateKey(key);
}
async function writePrivateKey() {
await gitPrivateKey?.writeKey();
}
async function configSigningKey(cwd) {
await gitPrivateKey?.configSigningKey(cwd);
}
//# sourceMappingURL=private-key.js.map