Skip to content

Commit fcbdbe4

Browse files
committed
https: distinguish PFX object-array agent keys
Signed-off-by: RafaelGSS <rafael.nunu@hotmail.com> PR-URL: nodejs-private/node-private#930 Refs: https://hackerone.com/reports/3816840 CVE-ID: CVE-2026-56850
1 parent a77c7f7 commit fcbdbe4

3 files changed

Lines changed: 119 additions & 1 deletion

File tree

lib/https.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'use strict';
2323

2424
const {
25+
ArrayIsArray,
2526
ArrayPrototypeIndexOf,
2627
ArrayPrototypePush,
2728
ArrayPrototypeShift,
@@ -471,6 +472,20 @@ ObjectSetPrototypeOf(Agent.prototype, HttpAgent.prototype);
471472
ObjectSetPrototypeOf(Agent, HttpAgent);
472473
Agent.prototype.createConnection = createConnection;
473474

475+
function getPfxAgentKey(pfx, passphrase) {
476+
if (!ArrayIsArray(pfx))
477+
return pfx;
478+
479+
let key = '';
480+
for (let i = 0; i < pfx.length; i++) {
481+
const value = pfx[i];
482+
const raw = value?.buf || value;
483+
const pass = value?.passphrase || passphrase;
484+
key += `:${raw}:${pass}`;
485+
}
486+
return key;
487+
}
488+
474489
/**
475490
* Gets a unique name for a set of options.
476491
* @param {{
@@ -506,7 +521,7 @@ Agent.prototype.getName = function getName(options = kEmptyObject) {
506521

507522
name += ':';
508523
if (options.pfx)
509-
name += options.pfx;
524+
name += getPfxAgentKey(options.pfx, options.passphrase);
510525

511526
name += ':';
512527
if (options.rejectUnauthorized !== undefined)

test/parallel/test-https-agent-getname.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ if (!common.hasCrypto)
66

77
const assert = require('assert');
88
const https = require('https');
9+
const fixtures = require('../common/fixtures');
910

1011
const agent = new https.Agent();
1112

@@ -52,3 +53,46 @@ assert.strictEqual(
5253
'::secureProtocol:c,r,l:false:ecdhCurve:dhparam:0:sessionIdContext:' +
5354
'"sigalgs":privateKeyIdentifier:privateKeyEngine'
5455
);
56+
57+
{
58+
const baseOptions = {
59+
host: '0.0.0.0',
60+
port: 443,
61+
};
62+
63+
const agent1 = fixtures.readKey('agent1.pfx');
64+
const agent6 = fixtures.readKey('agent6.pfx');
65+
66+
assert.notStrictEqual(
67+
agent.getName({
68+
...baseOptions,
69+
pfx: [{ buf: agent1, passphrase: 'sample' }],
70+
}),
71+
agent.getName({
72+
...baseOptions,
73+
pfx: [{ buf: agent6, passphrase: 'sample' }],
74+
})
75+
);
76+
77+
assert.notStrictEqual(
78+
agent.getName({
79+
...baseOptions,
80+
pfx: [{ buf: agent1, passphrase: 'sample' }],
81+
}),
82+
agent.getName({
83+
...baseOptions,
84+
pfx: [{ buf: agent1, passphrase: 'different' }],
85+
})
86+
);
87+
88+
assert.notStrictEqual(
89+
agent.getName({
90+
...baseOptions,
91+
pfx: [{ __proto__: { buf: agent1, passphrase: 'sample' } }],
92+
}),
93+
agent.getName({
94+
...baseOptions,
95+
pfx: [{ __proto__: { buf: agent6, passphrase: 'sample' } }],
96+
})
97+
);
98+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const https = require('https');
9+
const fixtures = require('../common/fixtures');
10+
11+
const server = https.createServer({
12+
key: fixtures.readKey('agent2-key.pem'),
13+
cert: fixtures.readKey('agent2-cert.pem'),
14+
requestCert: true,
15+
rejectUnauthorized: false,
16+
}, common.mustCall((req, res) => {
17+
res.end(req.socket.getPeerCertificate().subject.CN);
18+
}, 2));
19+
20+
server.listen(0, common.mustCall(async () => {
21+
const agent = new https.Agent({ keepAlive: true, maxSockets: 1 });
22+
const port = server.address().port;
23+
24+
const first = await request({
25+
agent,
26+
port,
27+
pfx: [{ buf: fixtures.readKey('agent1.pfx'), passphrase: 'sample' }],
28+
});
29+
assert.strictEqual(first.body, 'agent1');
30+
assert.strictEqual(first.reusedSocket, false);
31+
32+
const second = await request({
33+
agent,
34+
port,
35+
pfx: [{ buf: fixtures.readKey('agent10.pfx'), passphrase: 'sample' }],
36+
});
37+
assert.strictEqual(second.body, 'agent10.example.com');
38+
assert.strictEqual(second.reusedSocket, false);
39+
40+
agent.destroy();
41+
server.close();
42+
}));
43+
44+
function request(options) {
45+
return new Promise((resolve, reject) => {
46+
const req = https.get({
47+
...options,
48+
rejectUnauthorized: false,
49+
}, common.mustCall((res) => {
50+
let body = '';
51+
res.setEncoding('utf8');
52+
res.on('data', (chunk) => body += chunk);
53+
res.on('end', common.mustCall(() => {
54+
resolve({ body, reusedSocket: req.reusedSocket });
55+
}));
56+
}));
57+
req.on('error', reject);
58+
});
59+
}

0 commit comments

Comments
 (0)