Skip to content

Commit 96f9ad5

Browse files
authored
refactor(config): split decryption functions (#28571)
1 parent c8702b9 commit 96f9ad5

File tree

7 files changed

+99
-115
lines changed

7 files changed

+99
-115
lines changed

.github/workflows/build.yml

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ env:
3232
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
3333
NODE_VERSION: 18
3434
DRY_RUN: true
35+
TEST_LEGACY_DECRYPTION: true
3536
SPARSE_CHECKOUT: |-
3637
.github/actions/
3738
data/

jest.config.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import crypto from 'node:crypto';
22
import os from 'node:os';
3+
import { env } from 'node:process';
34
import v8 from 'node:v8';
45
import { minimatch } from 'minimatch';
56
import type { JestConfigWithTsJest } from 'ts-jest';
@@ -205,11 +206,7 @@ const config: JestConfig = {
205206
'!lib/**/{__fixtures__,__mocks__,__testutil__,test}/**/*.{js,ts}',
206207
'!lib/**/types.ts',
207208
],
208-
coveragePathIgnorePatterns: [
209-
'/node_modules/',
210-
'<rootDir>/test/',
211-
'<rootDir>/tools/',
212-
],
209+
coveragePathIgnorePatterns: getCoverageIgnorePatterns(),
213210
cacheDirectory: '.cache/jest',
214211
collectCoverage: true,
215212
coverageReporters: ci
@@ -450,3 +447,12 @@ process.stderr.write(`Host stats:
450447
Memory: ${(mem / 1024 / 1024 / 1024).toFixed(2)} GB
451448
HeapLimit: ${(stats.heap_size_limit / 1024 / 1024 / 1024).toFixed(2)} GB
452449
`);
450+
function getCoverageIgnorePatterns(): string[] | undefined {
451+
const patterns = ['/node_modules/', '<rootDir>/test/', '<rootDir>/tools/'];
452+
453+
if (env.TEST_LEGACY_DECRYPTION !== 'true') {
454+
patterns.push('<rootDir>/lib/config/decrypt/legacy.ts');
455+
}
456+
457+
return patterns;
458+
}

lib/config/decrypt.ts

+6-77
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,18 @@
1-
import crypto from 'node:crypto';
21
import is from '@sindresorhus/is';
3-
import * as openpgp from 'openpgp';
42
import { logger } from '../logger';
53
import { maskToken } from '../util/mask';
64
import { regEx } from '../util/regex';
75
import { addSecretForSanitizing } from '../util/sanitize';
86
import { ensureTrailingSlash } from '../util/url';
7+
import {
8+
tryDecryptPublicKeyDefault,
9+
tryDecryptPublicKeyPKCS1,
10+
} from './decrypt/legacy';
11+
import { tryDecryptOpenPgp } from './decrypt/openpgp';
912
import { GlobalConfig } from './global';
1013
import { DecryptedObject } from './schema';
1114
import type { RenovateConfig } from './types';
1215

13-
export async function tryDecryptPgp(
14-
privateKey: string,
15-
encryptedStr: string,
16-
): Promise<string | null> {
17-
if (encryptedStr.length < 500) {
18-
// optimization during transition of public key -> pgp
19-
return null;
20-
}
21-
try {
22-
const pk = await openpgp.readPrivateKey({
23-
// prettier-ignore
24-
armoredKey: privateKey.replace(regEx(/\n[ \t]+/g), '\n'), // little massage to help a common problem
25-
});
26-
const startBlock = '-----BEGIN PGP MESSAGE-----\n\n';
27-
const endBlock = '\n-----END PGP MESSAGE-----';
28-
let armoredMessage = encryptedStr.trim();
29-
if (!armoredMessage.startsWith(startBlock)) {
30-
armoredMessage = `${startBlock}${armoredMessage}`;
31-
}
32-
if (!armoredMessage.endsWith(endBlock)) {
33-
armoredMessage = `${armoredMessage}${endBlock}`;
34-
}
35-
const message = await openpgp.readMessage({
36-
armoredMessage,
37-
});
38-
const { data } = await openpgp.decrypt({
39-
message,
40-
decryptionKeys: pk,
41-
});
42-
logger.debug('Decrypted config using openpgp');
43-
return data;
44-
} catch (err) {
45-
logger.debug({ err }, 'Could not decrypt using openpgp');
46-
return null;
47-
}
48-
}
49-
50-
export function tryDecryptPublicKeyDefault(
51-
privateKey: string,
52-
encryptedStr: string,
53-
): string | null {
54-
let decryptedStr: string | null = null;
55-
try {
56-
decryptedStr = crypto
57-
.privateDecrypt(privateKey, Buffer.from(encryptedStr, 'base64'))
58-
.toString();
59-
logger.debug('Decrypted config using default padding');
60-
} catch (err) {
61-
logger.debug('Could not decrypt using default padding');
62-
}
63-
return decryptedStr;
64-
}
65-
66-
export function tryDecryptPublicKeyPKCS1(
67-
privateKey: string,
68-
encryptedStr: string,
69-
): string | null {
70-
let decryptedStr: string | null = null;
71-
try {
72-
decryptedStr = crypto
73-
.privateDecrypt(
74-
{
75-
key: privateKey,
76-
padding: crypto.constants.RSA_PKCS1_PADDING,
77-
},
78-
Buffer.from(encryptedStr, 'base64'),
79-
)
80-
.toString();
81-
} catch (err) {
82-
logger.debug('Could not decrypt using PKCS1 padding');
83-
}
84-
return decryptedStr;
85-
}
86-
8716
export async function tryDecrypt(
8817
privateKey: string,
8918
encryptedStr: string,
@@ -92,7 +21,7 @@ export async function tryDecrypt(
9221
): Promise<string | null> {
9322
let decryptedStr: string | null = null;
9423
if (privateKey?.startsWith('-----BEGIN PGP PRIVATE KEY BLOCK-----')) {
95-
const decryptedObjStr = await tryDecryptPgp(privateKey, encryptedStr);
24+
const decryptedObjStr = await tryDecryptOpenPgp(privateKey, encryptedStr);
9625
if (decryptedObjStr) {
9726
decryptedStr = validateDecryptedValue(decryptedObjStr, repository);
9827
}

lib/config/decrypt/legacy.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/** istanbul ignore file */
2+
import crypto from 'node:crypto';
3+
import { logger } from '../../logger';
4+
5+
export function tryDecryptPublicKeyPKCS1(
6+
privateKey: string,
7+
encryptedStr: string,
8+
): string | null {
9+
let decryptedStr: string | null = null;
10+
try {
11+
decryptedStr = crypto
12+
.privateDecrypt(
13+
{
14+
key: privateKey,
15+
padding: crypto.constants.RSA_PKCS1_PADDING,
16+
},
17+
Buffer.from(encryptedStr, 'base64'),
18+
)
19+
.toString();
20+
} catch (err) {
21+
logger.debug('Could not decrypt using PKCS1 padding');
22+
}
23+
return decryptedStr;
24+
}
25+
26+
export function tryDecryptPublicKeyDefault(
27+
privateKey: string,
28+
encryptedStr: string,
29+
): string | null {
30+
let decryptedStr: string | null = null;
31+
try {
32+
decryptedStr = crypto
33+
.privateDecrypt(privateKey, Buffer.from(encryptedStr, 'base64'))
34+
.toString();
35+
logger.debug('Decrypted config using default padding');
36+
} catch (err) {
37+
logger.debug('Could not decrypt using default padding');
38+
}
39+
return decryptedStr;
40+
}

lib/config/decrypt/openpgp.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as openpgp from 'openpgp';
2+
import { logger } from '../../logger';
3+
import { regEx } from '../../util/regex';
4+
5+
export async function tryDecryptOpenPgp(
6+
privateKey: string,
7+
encryptedStr: string,
8+
): Promise<string | null> {
9+
if (encryptedStr.length < 500) {
10+
// optimization during transition of public key -> pgp
11+
return null;
12+
}
13+
try {
14+
const pk = await openpgp.readPrivateKey({
15+
// prettier-ignore
16+
armoredKey: privateKey.replace(regEx(/\n[ \t]+/g), '\n'), // little massage to help a common problem
17+
});
18+
const startBlock = '-----BEGIN PGP MESSAGE-----\n\n';
19+
const endBlock = '\n-----END PGP MESSAGE-----';
20+
let armoredMessage = encryptedStr.trim();
21+
if (!armoredMessage.startsWith(startBlock)) {
22+
armoredMessage = `${startBlock}${armoredMessage}`;
23+
}
24+
if (!armoredMessage.endsWith(endBlock)) {
25+
armoredMessage = `${armoredMessage}${endBlock}`;
26+
}
27+
const message = await openpgp.readMessage({
28+
armoredMessage,
29+
});
30+
const { data } = await openpgp.decrypt({
31+
message,
32+
decryptionKeys: pk,
33+
});
34+
logger.debug('Decrypted config using openpgp');
35+
return data;
36+
} catch (err) {
37+
logger.debug({ err }, 'Could not decrypt using openpgp');
38+
return null;
39+
}
40+
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"generate": "run-s 'generate:*'",
2828
"generate:imports": "node tools/generate-imports.mjs",
2929
"git-check": "node tools/check-git-version.mjs",
30-
"jest": "node tools/jest.mjs",
30+
"jest": "GIT_ALLOW_PROTOCOL=file LOG_LEVEL=fatal node --experimental-vm-modules node_modules/jest/bin/jest.js --logHeapUsage",
3131
"lint": "run-s ls-lint type-check eslint prettier markdown-lint git-check doc-fence-check",
3232
"lint-fix": "run-s eslint-fix prettier-fix markdown-lint-fix",
3333
"ls-lint": "ls-lint",

tools/jest.mjs

-32
This file was deleted.

0 commit comments

Comments
 (0)