fix(HTTP Request Node): Keep SSL passphrases with whitespace intact (#37020)

This commit is contained in:
Bernhard Wittmann 2026-08-27 15:38:15 +00:00 committed by GitHub
parent e2afe16634
commit bcd240f589
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 55 additions and 3 deletions

View File

@ -34,6 +34,12 @@ ABC
expect(formatPemBlock('')).toBe('');
});
it('should return non-PEM input unchanged', () => {
expect(formatPemBlock('my secret key')).toBe('my secret key');
expect(formatPemBlock('pass\\nphrase')).toBe('pass\\nphrase');
expect(formatPemBlock('-----END CERTIFICATE-----')).toBe('-----END CERTIFICATE-----');
});
it('should format compact RSA PRIVATE KEY block', () => {
const compactKey = `-----BEGIN RSA PRIVATE KEY-----${'C'.repeat(64)}-----END RSA PRIVATE KEY-----`;

View File

@ -25,6 +25,9 @@ function formatCompactPem(pem: string, isPublic: boolean): string | undefined {
* by collapsing whitespace and wrapping the body at 64 chars. Multi-block PEM
* chains are returned unchanged.
*
* Input that is not a single-line PEM block is returned unchanged, so a plain
* secret (e.g. a key passphrase) passed here by mistake is not corrupted.
*
* @param pem - The PEM-encoded block to format.
* @param isPublic - When true, match `PUBLIC KEY` labels instead of the default `PRIVATE KEY` / `CERTIFICATE`.
* @returns The formatted PEM block.
@ -34,7 +37,9 @@ export function formatPemBlock(pem: string, isPublic = false): string {
if (isPublic) {
regex = /(PUBLIC KEY)/;
}
if (!pem || /\n/.test(pem)) {
// The fallback formatter below would collapse a non-PEM value's whitespace
// into newlines, corrupting plain secrets such as key passphrases.
if (!pem || /\n/.test(pem) || !pem.includes('-----BEGIN ')) {
return pem;
}
const compactPem = formatCompactPem(pem, isPublic);

View File

@ -312,8 +312,7 @@ export const setAgentOptions = (
if (sslCertificates.ca) agentOptions.ca = formatPemBlock(sslCertificates.ca);
if (sslCertificates.cert) agentOptions.cert = formatPemBlock(sslCertificates.cert);
if (sslCertificates.key) agentOptions.key = formatPemBlock(sslCertificates.key);
if (sslCertificates.passphrase)
agentOptions.passphrase = formatPemBlock(sslCertificates.passphrase);
if (sslCertificates.passphrase) agentOptions.passphrase = sslCertificates.passphrase;
requestOptions.agentOptions = agentOptions;
}
};

View File

@ -131,6 +131,48 @@ describe('HTTP Node Utils', () => {
},
});
});
it('should keep a passphrase containing whitespace unchanged', async () => {
const requestOptions: IRequestOptions = {
method: 'GET',
uri: 'https://example.com',
};
const sslCertificates = {
passphrase: 'my secret key',
};
setAgentOptions(requestOptions, sslCertificates);
expect(requestOptions).toStrictEqual({
method: 'GET',
uri: 'https://example.com',
agentOptions: {
passphrase: 'my secret key',
},
});
});
it('should wrap compact PEM certificates but not the passphrase', async () => {
const requestOptions: IRequestOptions = {
method: 'GET',
uri: 'https://example.com',
};
const sslCertificates = {
cert: `-----BEGIN CERTIFICATE-----${'A'.repeat(70)}-----END CERTIFICATE-----`,
key: `-----BEGIN PRIVATE KEY-----${'B'.repeat(70)}-----END PRIVATE KEY-----`,
passphrase: 'my secret key',
};
setAgentOptions(requestOptions, sslCertificates);
expect(requestOptions.agentOptions).toStrictEqual({
cert: `-----BEGIN CERTIFICATE-----\n${'A'.repeat(64)}\n${'A'.repeat(6)}\n-----END CERTIFICATE-----`,
key: `-----BEGIN PRIVATE KEY-----\n${'B'.repeat(64)}\n${'B'.repeat(6)}\n-----END PRIVATE KEY-----`,
passphrase: 'my secret key',
});
});
});
describe('sanitizeUiMessage', () => {