Skip to content

Commit ab9adfc

Browse files
aduh95anonrig
andcommitted
tls: remove prototype primordials
Co-authored-by: Yagiz Nizipli <yagiz@nizipli.com> PR-URL: #53699 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 6237172 commit ab9adfc

5 files changed

Lines changed: 68 additions & 96 deletions

File tree

‎doc/contributing/primordials.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ important than reliability against prototype pollution:
99

1010
*`node:http`
1111
*`node:http2`
12+
*`node:tls`
1213

1314
Usage of primordials should be preferred for new code in other areas, but
1415
replacing current code with primordials should be

‎lib/_tls_common.js‎

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@
2222
'use strict';
2323

2424
const{
25-
ArrayPrototypePush,
2625
JSONParse,
27-
RegExpPrototypeSymbolReplace,
2826
}=primordials;
2927

3028
consttls=require('tls');
@@ -133,21 +131,21 @@ function translatePeerCertificate(c) {
133131
c.infoAccess={__proto__: null};
134132

135133
// XXX: More key validation?
136-
RegExpPrototypeSymbolReplace(/([^\n:]*):([^\n]*)(?:\n|$)/g,info,
137-
(all,key,val)=>{
138-
if(val.charCodeAt(0)===0x22){
139-
// The translatePeerCertificate function is only
140-
// used on internally created legacy certificate
141-
// objects, and any value that contains a quote
142-
// will always be a valid JSON string literal,
143-
// so this should never throw.
144-
val=JSONParse(val);
145-
}
146-
if(keyinc.infoAccess)
147-
ArrayPrototypePush(c.infoAccess[key],val);
148-
else
149-
c.infoAccess[key]=[val];
150-
});
134+
info.replace(/([^\n:]*):([^\n]*)(?:\n|$)/g,
135+
(all,key,val)=>{
136+
if(val.charCodeAt(0)===0x22){
137+
// The translatePeerCertificate function is only
138+
// used on internally created legacy certificate
139+
// objects, and any value that contains a quote
140+
// will always be a valid JSON string literal,
141+
// so this should never throw.
142+
val=JSONParse(val);
143+
}
144+
if(keyinc.infoAccess)
145+
c.infoAccess[key].push(val);
146+
else
147+
c.infoAccess[key]=[val];
148+
});
151149
}
152150
returnc;
153151
}

‎lib/_tls_wrap.js‎

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,11 @@
2222
'use strict';
2323

2424
const{
25-
ArrayPrototypeForEach,
26-
ArrayPrototypeJoin,
27-
ArrayPrototypePush,
28-
FunctionPrototype,
2925
ObjectAssign,
3026
ObjectDefineProperty,
3127
ObjectSetPrototypeOf,
3228
ReflectApply,
3329
RegExp,
34-
RegExpPrototypeExec,
35-
RegExpPrototypeSymbolReplace,
36-
StringPrototypeReplaceAll,
37-
StringPrototypeSlice,
3830
Symbol,
3931
SymbolFor,
4032
}=primordials;
@@ -119,7 +111,7 @@ const kPskIdentityHint = Symbol('pskidentityhint');
119111
constkPendingSession=Symbol('pendingSession');
120112
constkIsVerified=Symbol('verified');
121113

122-
constnoop=FunctionPrototype;
114+
constnoop=()=>{};
123115

124116
letipServernameWarned=false;
125117
lettlsTracingWarned=false;
@@ -475,8 +467,7 @@ function onerror(err) {
475467
owner.destroy(err);
476468
}elseif(owner._tlsOptions?.isServer&&
477469
owner._rejectUnauthorized&&
478-
RegExpPrototypeExec(/peerdidnotreturnacertificate/,
479-
err.message)!==null){
470+
/peerdidnotreturnacertificate/.test(err.message)){
480471
// Ignore server's authorization errors
481472
owner.destroy();
482473
}else{
@@ -1162,7 +1153,7 @@ function makeSocketMethodProxy(name) {
11621153
};
11631154
}
11641155

1165-
ArrayPrototypeForEach([
1156+
[
11661157
'getCipher',
11671158
'getSharedSigalgs',
11681159
'getEphemeralKeyInfo',
@@ -1173,7 +1164,7 @@ ArrayPrototypeForEach([
11731164
'getTLSTicket',
11741165
'isSessionReused',
11751166
'enableTrace',
1176-
],(method)=>{
1167+
].forEach((method)=>{
11771168
TLSSocket.prototype[method]=makeSocketMethodProxy(method);
11781169
});
11791170

@@ -1470,10 +1461,10 @@ Server.prototype.setSecureContext = function(options) {
14701461
if(options.sessionIdContext){
14711462
this.sessionIdContext=options.sessionIdContext;
14721463
}else{
1473-
this.sessionIdContext=StringPrototypeSlice(
1474-
crypto.createHash('sha1')
1475-
.update(ArrayPrototypeJoin(process.argv,' '))
1476-
.digest('hex'),0,32);
1464+
this.sessionIdContext=crypto.createHash('sha1')
1465+
.update(process.argv.join(' '))
1466+
.digest('hex')
1467+
.slice(0,32);
14771468
}
14781469

14791470
if(options.sessionTimeout)
@@ -1568,10 +1559,10 @@ Server.prototype.setOptions = deprecate(function(options) {
15681559
if(options.sessionIdContext){
15691560
this.sessionIdContext=options.sessionIdContext;
15701561
}else{
1571-
this.sessionIdContext=StringPrototypeSlice(
1572-
crypto.createHash('sha1')
1573-
.update(ArrayPrototypeJoin(process.argv,' '))
1574-
.digest('hex'),0,32);
1562+
this.sessionIdContext=crypto.createHash('sha1')
1563+
.update(process.argv.join(' '))
1564+
.digest('hex')
1565+
.slice(0,32);
15751566
}
15761567
if(options.pskCallback)this[kPskCallback]=options.pskCallback;
15771568
if(options.pskIdentityHint)this[kPskIdentityHint]=options.pskIdentityHint;
@@ -1588,14 +1579,15 @@ Server.prototype.addContext = function(servername, context) {
15881579
thrownewERR_TLS_REQUIRED_SERVER_NAME();
15891580
}
15901581

1591-
constre=newRegExp('^'+StringPrototypeReplaceAll(
1592-
RegExpPrototypeSymbolReplace(/([.^$+?\-\\[\]{}])/g,servername,'\\$1'),
1593-
'*','[^.]*',
1594-
)+'$');
1582+
constre=newRegExp(`^${
1583+
servername
1584+
.replace(/([.^$+?\-\\[\]{}])/g,'\\$1')
1585+
.replaceAll('*','[^.]*')
1586+
}$`);
15951587

15961588
constsecureContext=
15971589
contextinstanceofcommon.SecureContext ? context : tls.createSecureContext(context);
1598-
ArrayPrototypePush(this._contexts,[re,secureContext.context]);
1590+
this._contexts.push([re,secureContext.context]);
15991591
};
16001592

16011593
Server.prototype[EE.captureRejectionSymbol]=function(
@@ -1616,7 +1608,7 @@ function SNICallback(servername, callback) {
16161608

16171609
for(leti=contexts.length-1;i>=0;--i){
16181610
constelem=contexts[i];
1619-
if(RegExpPrototypeExec(elem[0],servername)!==null){
1611+
if(elem[0].test(servername)){
16201612
callback(null,elem[1]);
16211613
return;
16221614
}

‎lib/eslint.config_partial.mjs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,16 +488,18 @@ export default [
488488
{
489489
files: [
490490
'lib/_http_*.js',
491+
'lib/_tls_*.js',
491492
'lib/http.js',
492493
'lib/http2.js',
493494
'lib/internal/http.js',
494495
'lib/internal/http2/*.js',
496+
'lib/tls.js',
495497
],
496498
rules: {
497499
'no-restricted-syntax': [
498500
...noRestrictedSyntax,
499501
{
500-
selector: 'VariableDeclarator:has(.init[name="primordials"]) Identifier[name=/Prototype/]:not([name=/^(Object|Reflect)(Get|Set)PrototypeOf$/])',
502+
selector: 'VariableDeclarator:has(.init[name="primordials"]) Identifier[name=/Prototype[A-Z]/]:not([name=/^(Object|Reflect)(Get|Set)PrototypeOf$/])',
501503
message: 'We do not use prototype primordials in this file',
502504
},
503505
],

‎lib/tls.js‎

Lines changed: 30 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,10 @@
2424
const{
2525
Array,
2626
ArrayIsArray,
27-
ArrayPrototypeForEach,
28-
ArrayPrototypeIncludes,
29-
ArrayPrototypeJoin,
30-
ArrayPrototypePush,
31-
ArrayPrototypeReduce,
32-
ArrayPrototypeSome,
3327
JSONParse,
3428
ObjectDefineProperty,
3529
ObjectFreeze,
36-
RegExpPrototypeExec,
37-
RegExpPrototypeSymbolReplace,
3830
StringFromCharCode,
39-
StringPrototypeCharCodeAt,
40-
StringPrototypeEndsWith,
41-
StringPrototypeIncludes,
42-
StringPrototypeIndexOf,
43-
StringPrototypeSlice,
44-
StringPrototypeSplit,
45-
StringPrototypeStartsWith,
46-
StringPrototypeSubstring,
4731
}=primordials;
4832

4933
const{
@@ -122,7 +106,7 @@ ObjectDefineProperty(exports, 'rootCertificates', {
122106
// ("\x06spdy/2\x08http/1.1\x08http/1.0")
123107
functionconvertProtocols(protocols){
124108
constlens=newArray(protocols.length);
125-
constbuff=Buffer.allocUnsafe(ArrayPrototypeReduce(protocols,(p,c,i)=>{
109+
constbuff=Buffer.allocUnsafe(protocols.reduce((p,c,i)=>{
126110
constlen=Buffer.byteLength(c);
127111
if(len>255){
128112
thrownewERR_OUT_OF_RANGE('The byte length of the protocol at index '+
@@ -158,20 +142,17 @@ exports.convertALPNProtocols = function convertALPNProtocols(protocols, out) {
158142
};
159143

160144
functionunfqdn(host){
161-
returnRegExpPrototypeSymbolReplace(/[.]$/,host,'');
145+
returnhost.replace(/[.]$/,'');
162146
}
163147

164148
// String#toLowerCase() is locale-sensitive so we use
165149
// a conservative version that only lowercases A-Z.
166150
functiontoLowerCase(c){
167-
returnStringFromCharCode(32+StringPrototypeCharCodeAt(c,0));
151+
returnStringFromCharCode(32+c.charCodeAt(0));
168152
}
169153

170154
functionsplitHost(host){
171-
returnStringPrototypeSplit(
172-
RegExpPrototypeSymbolReplace(/[A-Z]/g,unfqdn(host),toLowerCase),
173-
'.',
174-
);
155+
returnunfqdn(host).replace(/[A-Z]/g,toLowerCase).split('.');
175156
}
176157

177158
functioncheck(hostParts,pattern,wildcards){
@@ -185,15 +166,15 @@ function check(hostParts, pattern, wildcards) {
185166
returnfalse;
186167

187168
// Pattern has empty components, e.g. "bad..example.com".
188-
if(ArrayPrototypeIncludes(patternParts,''))
169+
if(patternParts.includes(''))
189170
returnfalse;
190171

191172
// RFC 6125 allows IDNA U-labels (Unicode) in names but we have no
192173
// good way to detect their encoding or normalize them so we simply
193174
// reject them. Control characters and blanks are rejected as well
194175
// because nothing good can come from accepting them.
195-
constisBad=(s)=>RegExpPrototypeExec(/[^\u0021-\u007F]/u,s)!==null;
196-
if(ArrayPrototypeSome(patternParts,isBad))
176+
constisBad=(s)=>/[^\u0021-\u007F]/u.test(s);
177+
if(patternParts.some(isBad))
197178
returnfalse;
198179

199180
// Check host parts from right to left first.
@@ -204,13 +185,13 @@ function check(hostParts, pattern, wildcards) {
204185

205186
consthostSubdomain=hostParts[0];
206187
constpatternSubdomain=patternParts[0];
207-
constpatternSubdomainParts=StringPrototypeSplit(patternSubdomain,'*');
188+
constpatternSubdomainParts=patternSubdomain.split('*');
208189

209190
// Short-circuit when the subdomain does not contain a wildcard.
210191
// RFC 6125 does not allow wildcard substitution for components
211192
// containing IDNA A-labels (Punycode) so match those verbatim.
212193
if(patternSubdomainParts.length===1||
213-
StringPrototypeIncludes(patternSubdomain,'xn--'))
194+
patternSubdomain.includes('xn--'))
214195
returnhostSubdomain===patternSubdomain;
215196

216197
if(!wildcards)
@@ -229,10 +210,10 @@ function check(hostParts, pattern, wildcards) {
229210
if(prefix.length+suffix.length>hostSubdomain.length)
230211
returnfalse;
231212

232-
if(!StringPrototypeStartsWith(hostSubdomain,prefix))
213+
if(!hostSubdomain.startsWith(prefix))
233214
returnfalse;
234215

235-
if(!StringPrototypeEndsWith(hostSubdomain,suffix))
216+
if(!hostSubdomain.endsWith(suffix))
236217
returnfalse;
237218

238219
returntrue;
@@ -250,30 +231,29 @@ function splitEscapedAltNames(altNames) {
250231
letcurrentToken='';
251232
letoffset=0;
252233
while(offset!==altNames.length){
253-
constnextSep=StringPrototypeIndexOf(altNames,', ',offset);
254-
constnextQuote=StringPrototypeIndexOf(altNames,'"',offset);
234+
constnextSep=altNames.indexOf(',',offset);
235+
constnextQuote=altNames.indexOf('"',offset);
255236
if(nextQuote!==-1&&(nextSep===-1||nextQuote<nextSep)){
256237
// There is a quote character and there is no separator before the quote.
257-
currentToken+=StringPrototypeSubstring(altNames,offset,nextQuote);
258-
constmatch=RegExpPrototypeExec(
259-
jsonStringPattern,StringPrototypeSubstring(altNames,nextQuote));
238+
currentToken+=altNames.substring(offset,nextQuote);
239+
constmatch=jsonStringPattern.exec(altNames.substring(nextQuote));
260240
if(!match){
261241
thrownewERR_TLS_CERT_ALTNAME_FORMAT();
262242
}
263243
currentToken+=JSONParse(match[0]);
264244
offset=nextQuote+match[0].length;
265245
}elseif(nextSep!==-1){
266246
// There is a separator and no quote before it.
267-
currentToken+=StringPrototypeSubstring(altNames,offset,nextSep);
268-
ArrayPrototypePush(result,currentToken);
247+
currentToken+=altNames.substring(offset,nextSep);
248+
result.push(currentToken);
269249
currentToken='';
270250
offset=nextSep+2;
271251
}else{
272-
currentToken+=StringPrototypeSubstring(altNames,offset);
252+
currentToken+=altNames.substring(offset);
273253
offset=altNames.length;
274254
}
275255
}
276-
ArrayPrototypePush(result,currentToken);
256+
result.push(currentToken);
277257
returnresult;
278258
}
279259

@@ -286,14 +266,14 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) {
286266
hostname=''+hostname;
287267

288268
if(altNames){
289-
constsplitAltNames=StringPrototypeIncludes(altNames,'"') ?
269+
constsplitAltNames=altNames.includes('"') ?
290270
splitEscapedAltNames(altNames) :
291-
StringPrototypeSplit(altNames,', ');
292-
ArrayPrototypeForEach(splitAltNames,(name)=>{
293-
if(StringPrototypeStartsWith(name,'DNS:')){
294-
ArrayPrototypePush(dnsNames,StringPrototypeSlice(name,4));
295-
}elseif(StringPrototypeStartsWith(name,'IP Address:')){
296-
ArrayPrototypePush(ips,canonicalizeIP(StringPrototypeSlice(name,11)));
271+
altNames.split(', ');
272+
splitAltNames.forEach((name)=>{
273+
if(name.startsWith('DNS:')){
274+
dnsNames.push(name.slice(4));
275+
}elseif(name.startsWith('IP Address:')){
276+
ips.push(canonicalizeIP(name.slice(11)));
297277
}
298278
});
299279
}
@@ -304,16 +284,15 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) {
304284
hostname=unfqdn(hostname);// Remove trailing dot for error messages.
305285

306286
if(net.isIP(hostname)){
307-
valid=ArrayPrototypeIncludes(ips,canonicalizeIP(hostname));
287+
valid=ips.includes(canonicalizeIP(hostname));
308288
if(!valid)
309-
reason=`IP: ${hostname} is not in the cert's list: `+
310-
ArrayPrototypeJoin(ips,', ');
289+
reason=`IP: ${hostname} is not in the cert's list: `+ips.join(', ');
311290
}elseif(dnsNames.length>0||subject?.CN){
312291
consthostParts=splitHost(hostname);
313292
constwildcard=(pattern)=>check(hostParts,pattern,true);
314293

315294
if(dnsNames.length>0){
316-
valid=ArrayPrototypeSome(dnsNames,wildcard);
295+
valid=dnsNames.some(wildcard);
317296
if(!valid)
318297
reason=
319298
`Host: ${hostname}. is not in the cert's altnames: ${altNames}`;
@@ -322,7 +301,7 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) {
322301
constcn=subject.CN;
323302

324303
if(ArrayIsArray(cn))
325-
valid=ArrayPrototypeSome(cn,wildcard);
304+
valid=cn.some(wildcard);
326305
elseif(cn)
327306
valid=wildcard(cn);
328307

0 commit comments

Comments
 (0)