fix!: drop legacy rsa encryption (#39111)

This commit is contained in:
Michael Kriese
2025-11-06 08:41:53 +01:00
committed by Jamie Tanna
parent 0d5d7a8669
commit f1eefcf393
4 changed files with 8 additions and 197 deletions
-27
View File
@@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAzVLc1KmhWxcLnoPTPpQwxVHySFx4vY+0Sk+2AdnlJvrTFlNR
8XgBPdgU9SDwHFFTYyXRQ/msm0YMOBRoIdz4/psz6IzRV80aOtDIkUPAU+cdqnje
3rusrPnqCykDuIHGoBZ8zlt57t/8OVNOduUflrQRqMFors8iLWcNjuGJfZAusI5B
M8KYYDPL2oUo/8AV0bWN8MdNnxDSwGtan4bbzrtG6dpNzyntsG5DkL1h8OZRki4S
kHf+71ZzJFz1KcyrzBVJ9DBxdiJdmLLkv9caLLc2QS0VFmtfLnlrga3Sahy6YNwc
rN33aaNOXwFPSErsKWIgIf16eOaSUQCFusXY9QIDAQABAoIBAQCkltMM6nm9Ikkf
JX9V/8bkth7o4K+tDSAyHZnB/CBUUeaaU+oxDci5AZkzMtcnbA3TQcJxohg6VDmB
TuJ2msNCnblLpm492t023pyYzd3DpFXEjKXjmEAAXUm+7n7cDbPiKoSbivrAgO6Y
KW6RonPjA6/QPlIjJ0m3aY+VxLfJXTYVfFBk+0HGEAvmrSSSEezXej8Qzs9CRKtz
cQkR5Bs749SS509MHxaslP7n366EvJBJkqrjUxA6kbxOxUkuuOWF9jfduDAG+NYc
pe4IXFOYMpK/w29wqvkNulKYs+FHXr1sGjkztGjyNnjP0NnX+r0EYU07xLJ26krD
KMWQG8sBAoGBAO93iFrPnEBnnKojI8b+u3YowVCz0HVuHHIStHd9uUgfsn/Bxfls
HRIlg8l1MJd69TT0knOhkJxBCP+b/qBvid7YLbTxIVELCAfLAzsmGgY+DEN/TTUb
FDvHGa/drCnkSR/O/RbtHvMQISzTja1siYrdwwY+wpwzR2tB7ZH/9iO1AoGBANt/
3Srpv4BbP7JZ+cNte4JfI2gZq41mo+DF5ryuFZzsP3R5SZ1BOapQGyLEATQaoxsJ
QZsI73KPBzab0/+E75qJuXckIGmFftHXgoQpClGvPaEDF9M21QW5Y3vKVa/45qhy
3wpb8gEYqrt1x0rmzyumCtCD8J470Er64gE1eKhBAoGBALW6ScFYwqRhvROkvTb8
A7mE7kfXXgBwAqhTJ59yytRAMc8gd6R0do9Z5uxQwgKDLmj0ndugpcTe2fxZHuAU
JVX3SqCBSZ5eN8bqOtZ9cMyB8/6ZMjd2CGHhE85R9KCJ/TBlfc4TPySIfhStq1wL
/UlkR+eKY1f01mNAUhE1ZU7tAoGBAKwS/B51KsSERFYcRToYbRfSX55vaVarnVNL
scw+qQDhEAnOP5CBHqTOscc6YzsmmrFKO10/zv8+80ezN6n73B6JU5T8BFDU74uv
6EiVJ9rLh4PfOeFB/hPDtyLHhw8yEBkEHKgxVnHXlZjqBzdH5Cdyvs2icZKKj4sI
TP7nnVRBAoGAPZBjjTn9HgB9et5Kdovp+7nr842WboxwklVyTpbNGAjIxNXIXex+
TST21UbquTIpIrpYRk1WMIu9T5PndAagDWVEsmUYQZeuhmuY64K+iviRsfdGthws
nbbR8sMEkn4XEtPZfrEBq27g01RNmIIMW5Es2O5N2AjLlyeBOh099Fw=
-----END RSA PRIVATE KEY-----
+8 -43
View File
@@ -6,10 +6,6 @@ import { regEx } from '../util/regex';
import { addSecretForSanitizing } from '../util/sanitize';
import { ensureTrailingSlash, parseUrl, trimSlashes } from '../util/url';
import { tryDecryptKbPgp } from './decrypt/kbpgp';
import {
tryDecryptPublicKeyDefault,
tryDecryptPublicKeyPKCS1,
} from './decrypt/legacy';
import { tryDecryptOpenPgp } from './decrypt/openpgp';
import { GlobalConfig } from './global';
import { DecryptedObject } from './schema';
@@ -30,35 +26,14 @@ export async function tryDecrypt(
key: string,
encryptedStr: string,
repository: string,
keyName: string,
): Promise<string | null> {
let decryptedStr: string | null = null;
if (key?.startsWith('-----BEGIN PGP PRIVATE KEY BLOCK-----')) {
const decryptedObjStr =
getEnv().RENOVATE_X_USE_OPENPGP === 'true'
? await tryDecryptOpenPgp(key, encryptedStr)
: await tryDecryptKbPgp(key, encryptedStr);
if (decryptedObjStr) {
decryptedStr = validateDecryptedValue(decryptedObjStr, repository);
}
} else {
decryptedStr = tryDecryptPublicKeyDefault(key, encryptedStr);
if (is.string(decryptedStr)) {
logger.warn(
{ keyName },
'Encrypted value is using deprecated default padding, please change to using PGP encryption.',
);
} else {
decryptedStr = tryDecryptPublicKeyPKCS1(key, encryptedStr);
/* v8 ignore start -- not testable */
if (is.string(decryptedStr)) {
logger.warn(
{ keyName },
'Encrypted value is using deprecated PKCS1 padding, please change to using PGP encryption.',
);
}
/* v8 ignore stop */
}
const decryptedObjStr =
getEnv().RENOVATE_X_USE_OPENPGP === 'true'
? await tryDecryptOpenPgp(key, encryptedStr)
: await tryDecryptKbPgp(key, encryptedStr);
if (decryptedObjStr) {
decryptedStr = validateDecryptedValue(decryptedObjStr, repository);
}
return decryptedStr;
}
@@ -170,20 +145,10 @@ export async function decryptConfig(
if (privateKey) {
for (const [eKey, eVal] of Object.entries(val)) {
logger.debug(`Trying to decrypt ${eKey} in ${path}`);
let decryptedStr = await tryDecrypt(
privateKey,
eVal,
repository,
eKey,
);
let decryptedStr = await tryDecrypt(privateKey, eVal, repository);
if (privateKeyOld && !is.nonEmptyString(decryptedStr)) {
logger.debug(`Trying to decrypt with old private key`);
decryptedStr = await tryDecrypt(
privateKeyOld,
eVal,
repository,
eKey,
);
decryptedStr = await tryDecrypt(privateKeyOld, eVal, repository);
}
if (!is.nonEmptyString(decryptedStr)) {
const error = new Error('config-validation');
-88
View File
@@ -1,88 +0,0 @@
import { CONFIG_VALIDATION } from '../../constants/error-messages';
import { decryptConfig, setPrivateKeys } from '../decrypt';
import { GlobalConfig } from '../global';
import type { RenovateConfig } from '../types';
import { Fixtures } from '~test/fixtures';
const privateKey = Fixtures.get('private.pem', '..');
const repository = 'abc/def';
describe('config/decrypt/legacy', () => {
describe('decryptConfig()', () => {
let config: RenovateConfig;
beforeEach(() => {
config = {};
GlobalConfig.reset();
setPrivateKeys(undefined, undefined);
});
it('handles invalid encrypted type', async () => {
config.encrypted = 1;
setPrivateKeys(privateKey, undefined);
const res = await decryptConfig(config, repository);
expect(res.encrypted).toBeUndefined();
});
it('handles invalid encrypted value', async () => {
config.encrypted = { a: 1 };
setPrivateKeys(privateKey, 'invalid-key');
await expect(decryptConfig(config, repository)).rejects.toThrow(
CONFIG_VALIDATION,
);
});
it('replaces npm token placeholder in npmrc', async () => {
setPrivateKeys('invalid-key', privateKey); // test old key failover
config.npmrc =
'//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n';
config.encrypted = {
npmToken:
'FLA9YHIzpE7YetAg/P0X46npGRCMqn7hgyzwX5ZQ9wYgu9BRRbTiBVsUIFTyM5BuP1Q22slT2GkWvFvum7GU236Y6QiT7Nr8SLvtsJn2XUuq8H7REFKzdy3+wqyyWbCErYTFyY1dcPM7Ht+CaGDWdd8u/FsoX7AdMRs/X1jNUo6iSmlUiyGlYDKF+QMnCJom1VPVgZXWsGKdjI2MLny991QMaiv0VajmFIh4ENv4CtXOl/1twvIl/6XTXAaqpJJKDTPZEuydi+PHDZmal2RAOfrkH4m0UURa7SlfpUlIg+EaqbNGp85hCYXLwRcEET1OnYr3rH1oYkcYJ40any1tvQ==',
};
const res = await decryptConfig(config, repository);
expect(res.encrypted).toBeUndefined();
expect(res.npmToken).toBe('abcdef-ghijklm-nopqf-stuvwxyz');
});
it('appends npm token in npmrc', async () => {
setPrivateKeys(privateKey, undefined);
config.npmrc = 'foo=bar\n';
config.encrypted = {
npmToken:
'FLA9YHIzpE7YetAg/P0X46npGRCMqn7hgyzwX5ZQ9wYgu9BRRbTiBVsUIFTyM5BuP1Q22slT2GkWvFvum7GU236Y6QiT7Nr8SLvtsJn2XUuq8H7REFKzdy3+wqyyWbCErYTFyY1dcPM7Ht+CaGDWdd8u/FsoX7AdMRs/X1jNUo6iSmlUiyGlYDKF+QMnCJom1VPVgZXWsGKdjI2MLny991QMaiv0VajmFIh4ENv4CtXOl/1twvIl/6XTXAaqpJJKDTPZEuydi+PHDZmal2RAOfrkH4m0UURa7SlfpUlIg+EaqbNGp85hCYXLwRcEET1OnYr3rH1oYkcYJ40any1tvQ==',
};
const res = await decryptConfig(config, repository);
expect(res.encrypted).toBeUndefined();
expect(res.npmToken).toBe('abcdef-ghijklm-nopqf-stuvwxyz');
});
it('decrypts nested', async () => {
setPrivateKeys(privateKey, undefined);
config.packageFiles = [
{
packageFile: 'package.json',
devDependencies: {
encrypted: {
branchPrefix:
'FLA9YHIzpE7YetAg/P0X46npGRCMqn7hgyzwX5ZQ9wYgu9BRRbTiBVsUIFTyM5BuP1Q22slT2GkWvFvum7GU236Y6QiT7Nr8SLvtsJn2XUuq8H7REFKzdy3+wqyyWbCErYTFyY1dcPM7Ht+CaGDWdd8u/FsoX7AdMRs/X1jNUo6iSmlUiyGlYDKF+QMnCJom1VPVgZXWsGKdjI2MLny991QMaiv0VajmFIh4ENv4CtXOl/1twvIl/6XTXAaqpJJKDTPZEuydi+PHDZmal2RAOfrkH4m0UURa7SlfpUlIg+EaqbNGp85hCYXLwRcEET1OnYr3rH1oYkcYJ40any1tvQ==',
npmToken:
'FLA9YHIzpE7YetAg/P0X46npGRCMqn7hgyzwX5ZQ9wYgu9BRRbTiBVsUIFTyM5BuP1Q22slT2GkWvFvum7GU236Y6QiT7Nr8SLvtsJn2XUuq8H7REFKzdy3+wqyyWbCErYTFyY1dcPM7Ht+CaGDWdd8u/FsoX7AdMRs/X1jNUo6iSmlUiyGlYDKF+QMnCJom1VPVgZXWsGKdjI2MLny991QMaiv0VajmFIh4ENv4CtXOl/1twvIl/6XTXAaqpJJKDTPZEuydi+PHDZmal2RAOfrkH4m0UURa7SlfpUlIg+EaqbNGp85hCYXLwRcEET1OnYr3rH1oYkcYJ40any1tvQ==',
},
},
},
'backend/package.json',
];
// TODO: fix types #22198
const res = (await decryptConfig(config, repository)) as any;
expect(res.encrypted).toBeUndefined();
expect(res.packageFiles[0].devDependencies.encrypted).toBeUndefined();
expect(res.packageFiles[0].devDependencies.branchPrefix).toBe(
'abcdef-ghijklm-nopqf-stuvwxyz',
);
expect(res.packageFiles[0].devDependencies.npmToken).toBe(
'abcdef-ghijklm-nopqf-stuvwxyz',
);
});
});
});
-39
View File
@@ -1,39 +0,0 @@
import crypto from 'node:crypto';
import { logger } from '../../logger';
export function tryDecryptPublicKeyPKCS1(
privateKey: string,
encryptedStr: string,
): string | null {
let decryptedStr: string | null = null;
try {
decryptedStr = crypto
.privateDecrypt(
{
key: privateKey,
padding: crypto.constants.RSA_PKCS1_PADDING,
},
Buffer.from(encryptedStr, 'base64'),
)
.toString();
} catch {
logger.debug('Could not decrypt using PKCS1 padding');
}
return decryptedStr;
}
export function tryDecryptPublicKeyDefault(
privateKey: string,
encryptedStr: string,
): string | null {
let decryptedStr: string | null = null;
try {
decryptedStr = crypto
.privateDecrypt(privateKey, Buffer.from(encryptedStr, 'base64'))
.toString();
logger.debug('Decrypted config using default padding');
} catch {
logger.debug('Could not decrypt using default padding');
}
return decryptedStr;
}