Skip to content

Commit acaf426

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 daa6d25 commit acaf426

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,
@@ -467,6 +468,20 @@ ObjectSetPrototypeOf(Agent.prototype, HttpAgent.prototype);
467468
ObjectSetPrototypeOf(Agent,HttpAgent);
468469
Agent.prototype.createConnection=createConnection;
469470

471+
functiongetPfxAgentKey(pfx,passphrase){
472+
if(!ArrayIsArray(pfx))
473+
returnpfx;
474+
475+
letkey='';
476+
for(leti=0;i<pfx.length;i++){
477+
constvalue=pfx[i];
478+
constraw=value?.buf||value;
479+
constpass=value?.passphrase||passphrase;
480+
key+=`:${raw}:${pass}`;
481+
}
482+
returnkey;
483+
}
484+
470485
/**
471486
* Gets a unique name for a set of options.
472487
* @param {{
@@ -502,7 +517,7 @@ Agent.prototype.getName = function getName(options = kEmptyObject) {
502517

503518
name+=':';
504519
if(options.pfx)
505-
name+=options.pfx;
520+
name+=getPfxAgentKey(options.pfx,options.passphrase);
506521

507522
name+=':';
508523
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
constassert=require('assert');
88
consthttps=require('https');
9+
constfixtures=require('../common/fixtures');
910

1011
constagent=newhttps.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+
constbaseOptions={
59+
host: '0.0.0.0',
60+
port: 443,
61+
};
62+
63+
constagent1=fixtures.readKey('agent1.pfx');
64+
constagent6=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+
constcommon=require('../common');
4+
if(!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
constassert=require('assert');
8+
consthttps=require('https');
9+
constfixtures=require('../common/fixtures');
10+
11+
constserver=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+
constagent=newhttps.Agent({keepAlive: true,maxSockets: 1});
22+
constport=server.address().port;
23+
24+
constfirst=awaitrequest({
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+
constsecond=awaitrequest({
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+
functionrequest(options){
45+
returnnewPromise((resolve,reject)=>{
46+
constreq=https.get({
47+
...options,
48+
rejectUnauthorized: false,
49+
},common.mustCall((res)=>{
50+
letbody='';
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)