GeezerVison is showing github.com — tls: remove prototype primordials · nodejs/node@ab9adfc
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/contributing/primordials.md +1Lines changed: 1 addition & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -9,6 +9,7 @@ important than reliability against prototype pollution: | 9 | 9 |
| 10 | 10 | * `node:http` | 11 | 11 | * `node:http2` | | 12 | +* `node:tls` | 12 | 13 |
| 13 | 14 | Usage of primordials should be preferred for new code in other areas, but | 14 | 15 | replacing current code with primordials should be | |
Collapse file Copy file name to clipboardExpand all lines: lib/_tls_common.js +15-17Lines changed: 15 additions & 17 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 22 | 22 | 'use strict'; | 23 | 23 |
| 24 | 24 | const { | 25 | | - ArrayPrototypePush, | 26 | 25 | JSONParse, | 27 | | - RegExpPrototypeSymbolReplace, | 28 | 26 | } = primordials; | 29 | 27 |
| 30 | 28 | const tls = require('tls'); | @@ -133,21 +131,21 @@ function translatePeerCertificate(c) { | 133 | 131 | c.infoAccess = { __proto__: null }; | 134 | 132 |
| 135 | 133 | // 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 (key in c.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 (key in c.infoAccess) | | 145 | + c.infoAccess[key].push(val); | | 146 | + else | | 147 | + c.infoAccess[key] = [val]; | | 148 | + }); | 151 | 149 | } | 152 | 150 | return c; | 153 | 151 | } | |
Collapse file Copy file name to clipboardExpand all lines: lib/_tls_wrap.js +19-27Lines changed: 19 additions & 27 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 22 | 22 | 'use strict'; | 23 | 23 |
| 24 | 24 | const { | 25 | | - ArrayPrototypeForEach, | 26 | | - ArrayPrototypeJoin, | 27 | | - ArrayPrototypePush, | 28 | | - FunctionPrototype, | 29 | 25 | ObjectAssign, | 30 | 26 | ObjectDefineProperty, | 31 | 27 | ObjectSetPrototypeOf, | 32 | 28 | ReflectApply, | 33 | 29 | RegExp, | 34 | | - RegExpPrototypeExec, | 35 | | - RegExpPrototypeSymbolReplace, | 36 | | - StringPrototypeReplaceAll, | 37 | | - StringPrototypeSlice, | 38 | 30 | Symbol, | 39 | 31 | SymbolFor, | 40 | 32 | } = primordials; | @@ -119,7 +111,7 @@ const kPskIdentityHint = Symbol('pskidentityhint'); | 119 | 111 | const kPendingSession = Symbol('pendingSession'); | 120 | 112 | const kIsVerified = Symbol('verified'); | 121 | 113 |
| 122 | | -const noop = FunctionPrototype; | | 114 | +const noop = () => {}; | 123 | 115 |
| 124 | 116 | let ipServernameWarned = false; | 125 | 117 | let tlsTracingWarned = false; | @@ -475,8 +467,7 @@ function onerror(err) { | 475 | 467 | owner.destroy(err); | 476 | 468 | } else if (owner._tlsOptions?.isServer && | 477 | 469 | owner._rejectUnauthorized && | 478 | | - RegExpPrototypeExec(/peer did not return a certificate/, | 479 | | - err.message) !== null) { | | 470 | + /peer did not return a certificate/.test(err.message)) { | 480 | 471 | // Ignore server's authorization errors | 481 | 472 | owner.destroy(); | 482 | 473 | } else { | @@ -1162,7 +1153,7 @@ function makeSocketMethodProxy(name) { | 1162 | 1153 | }; | 1163 | 1154 | } | 1164 | 1155 |
| 1165 | | -ArrayPrototypeForEach([ | | 1156 | +[ | 1166 | 1157 | 'getCipher', | 1167 | 1158 | 'getSharedSigalgs', | 1168 | 1159 | 'getEphemeralKeyInfo', | @@ -1173,7 +1164,7 @@ ArrayPrototypeForEach([ | 1173 | 1164 | 'getTLSTicket', | 1174 | 1165 | 'isSessionReused', | 1175 | 1166 | 'enableTrace', | 1176 | | -], (method) => { | | 1167 | +].forEach((method) => { | 1177 | 1168 | TLSSocket.prototype[method] = makeSocketMethodProxy(method); | 1178 | 1169 | }); | 1179 | 1170 |
| @@ -1470,10 +1461,10 @@ Server.prototype.setSecureContext = function(options) { | 1470 | 1461 | if (options.sessionIdContext) { | 1471 | 1462 | this.sessionIdContext = options.sessionIdContext; | 1472 | 1463 | } 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); | 1477 | 1468 | } | 1478 | 1469 |
| 1479 | 1470 | if (options.sessionTimeout) | @@ -1568,10 +1559,10 @@ Server.prototype.setOptions = deprecate(function(options) { | 1568 | 1559 | if (options.sessionIdContext) { | 1569 | 1560 | this.sessionIdContext = options.sessionIdContext; | 1570 | 1561 | } 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); | 1575 | 1566 | } | 1576 | 1567 | if (options.pskCallback) this[kPskCallback] = options.pskCallback; | 1577 | 1568 | if (options.pskIdentityHint) this[kPskIdentityHint] = options.pskIdentityHint; | @@ -1588,14 +1579,15 @@ Server.prototype.addContext = function(servername, context) { | 1588 | 1579 | throw new ERR_TLS_REQUIRED_SERVER_NAME(); | 1589 | 1580 | } | 1590 | 1581 |
| 1591 | | - const re = new RegExp('^' + StringPrototypeReplaceAll( | 1592 | | - RegExpPrototypeSymbolReplace(/([.^$+?\-\\[\]{}])/g, servername, '\\$1'), | 1593 | | - '*', '[^.]*', | 1594 | | - ) + '$'); | | 1582 | + const re = new RegExp(`^${ | | 1583 | + servername | | 1584 | + .replace(/([.^$+?\-\\[\]{}])/g, '\\$1') | | 1585 | + .replaceAll('*', '[^.]*') | | 1586 | + }$`); | 1595 | 1587 |
| 1596 | 1588 | const secureContext = | 1597 | 1589 | context instanceof common.SecureContext ? context : tls.createSecureContext(context); | 1598 | | - ArrayPrototypePush(this._contexts, [re, secureContext.context]); | | 1590 | + this._contexts.push([re, secureContext.context]); | 1599 | 1591 | }; | 1600 | 1592 |
| 1601 | 1593 | Server.prototype[EE.captureRejectionSymbol] = function( | @@ -1616,7 +1608,7 @@ function SNICallback(servername, callback) { | 1616 | 1608 |
| 1617 | 1609 | for (let i = contexts.length - 1; i >= 0; --i) { | 1618 | 1610 | const elem = contexts[i]; | 1619 | | - if (RegExpPrototypeExec(elem[0], servername) !== null) { | | 1611 | + if (elem[0].test(servername)) { | 1620 | 1612 | callback(null, elem[1]); | 1621 | 1613 | return; | 1622 | 1614 | } | |
Collapse file Copy file name to clipboardExpand all lines: lib/eslint.config_partial.mjs +3-1Lines changed: 3 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -488,16 +488,18 @@ export default [ | 488 | 488 | { | 489 | 489 | files: [ | 490 | 490 | 'lib/_http_*.js', | | 491 | + 'lib/_tls_*.js', | 491 | 492 | 'lib/http.js', | 492 | 493 | 'lib/http2.js', | 493 | 494 | 'lib/internal/http.js', | 494 | 495 | 'lib/internal/http2/*.js', | | 496 | + 'lib/tls.js', | 495 | 497 | ], | 496 | 498 | rules: { | 497 | 499 | 'no-restricted-syntax': [ | 498 | 500 | ...noRestrictedSyntax, | 499 | 501 | { | 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$/])', | 501 | 503 | message: 'We do not use prototype primordials in this file', | 502 | 504 | }, | 503 | 505 | ], | |
Collapse file Copy file name to clipboardExpand all lines: lib/tls.js +30-51Lines changed: 30 additions & 51 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 24 | 24 | const { | 25 | 25 | Array, | 26 | 26 | ArrayIsArray, | 27 | | - ArrayPrototypeForEach, | 28 | | - ArrayPrototypeIncludes, | 29 | | - ArrayPrototypeJoin, | 30 | | - ArrayPrototypePush, | 31 | | - ArrayPrototypeReduce, | 32 | | - ArrayPrototypeSome, | 33 | 27 | JSONParse, | 34 | 28 | ObjectDefineProperty, | 35 | 29 | ObjectFreeze, | 36 | | - RegExpPrototypeExec, | 37 | | - RegExpPrototypeSymbolReplace, | 38 | 30 | StringFromCharCode, | 39 | | - StringPrototypeCharCodeAt, | 40 | | - StringPrototypeEndsWith, | 41 | | - StringPrototypeIncludes, | 42 | | - StringPrototypeIndexOf, | 43 | | - StringPrototypeSlice, | 44 | | - StringPrototypeSplit, | 45 | | - StringPrototypeStartsWith, | 46 | | - StringPrototypeSubstring, | 47 | 31 | } = primordials; | 48 | 32 |
| 49 | 33 | const { | @@ -122,7 +106,7 @@ ObjectDefineProperty(exports, 'rootCertificates', { | 122 | 106 | // ("\x06spdy/2\x08http/1.1\x08http/1.0") | 123 | 107 | function convertProtocols(protocols) { | 124 | 108 | const lens = new Array(protocols.length); | 125 | | - const buff = Buffer.allocUnsafe(ArrayPrototypeReduce(protocols, (p, c, i) => { | | 109 | + const buff = Buffer.allocUnsafe(protocols.reduce((p, c, i) => { | 126 | 110 | const len = Buffer.byteLength(c); | 127 | 111 | if (len > 255) { | 128 | 112 | throw new ERR_OUT_OF_RANGE('The byte length of the protocol at index ' + | @@ -158,20 +142,17 @@ exports.convertALPNProtocols = function convertALPNProtocols(protocols, out) { | 158 | 142 | }; | 159 | 143 |
| 160 | 144 | function unfqdn(host) { | 161 | | - return RegExpPrototypeSymbolReplace(/[.]$/, host, ''); | | 145 | + return host.replace(/[.]$/, ''); | 162 | 146 | } | 163 | 147 |
| 164 | 148 | // String#toLowerCase() is locale-sensitive so we use | 165 | 149 | // a conservative version that only lowercases A-Z. | 166 | 150 | function toLowerCase(c) { | 167 | | - return StringFromCharCode(32 + StringPrototypeCharCodeAt(c, 0)); | | 151 | + return StringFromCharCode(32 + c.charCodeAt(0)); | 168 | 152 | } | 169 | 153 |
| 170 | 154 | function splitHost(host) { | 171 | | - return StringPrototypeSplit( | 172 | | - RegExpPrototypeSymbolReplace(/[A-Z]/g, unfqdn(host), toLowerCase), | 173 | | - '.', | 174 | | - ); | | 155 | + return unfqdn(host).replace(/[A-Z]/g, toLowerCase).split('.'); | 175 | 156 | } | 176 | 157 |
| 177 | 158 | function check(hostParts, pattern, wildcards) { | @@ -185,15 +166,15 @@ function check(hostParts, pattern, wildcards) { | 185 | 166 | return false; | 186 | 167 |
| 187 | 168 | // Pattern has empty components, e.g. "bad..example.com". | 188 | | - if (ArrayPrototypeIncludes(patternParts, '')) | | 169 | + if (patternParts.includes('')) | 189 | 170 | return false; | 190 | 171 |
| 191 | 172 | // RFC 6125 allows IDNA U-labels (Unicode) in names but we have no | 192 | 173 | // good way to detect their encoding or normalize them so we simply | 193 | 174 | // reject them. Control characters and blanks are rejected as well | 194 | 175 | // because nothing good can come from accepting them. | 195 | | - const isBad = (s) => RegExpPrototypeExec(/[^\u0021-\u007F]/u, s) !== null; | 196 | | - if (ArrayPrototypeSome(patternParts, isBad)) | | 176 | + const isBad = (s) => /[^\u0021-\u007F]/u.test(s); | | 177 | + if (patternParts.some(isBad)) | 197 | 178 | return false; | 198 | 179 |
| 199 | 180 | // Check host parts from right to left first. | @@ -204,13 +185,13 @@ function check(hostParts, pattern, wildcards) { | 204 | 185 |
| 205 | 186 | const hostSubdomain = hostParts[0]; | 206 | 187 | const patternSubdomain = patternParts[0]; | 207 | | - const patternSubdomainParts = StringPrototypeSplit(patternSubdomain, '*'); | | 188 | + const patternSubdomainParts = patternSubdomain.split('*'); | 208 | 189 |
| 209 | 190 | // Short-circuit when the subdomain does not contain a wildcard. | 210 | 191 | // RFC 6125 does not allow wildcard substitution for components | 211 | 192 | // containing IDNA A-labels (Punycode) so match those verbatim. | 212 | 193 | if (patternSubdomainParts.length === 1 || | 213 | | - StringPrototypeIncludes(patternSubdomain, 'xn--')) | | 194 | + patternSubdomain.includes('xn--')) | 214 | 195 | return hostSubdomain === patternSubdomain; | 215 | 196 |
| 216 | 197 | if (!wildcards) | @@ -229,10 +210,10 @@ function check(hostParts, pattern, wildcards) { | 229 | 210 | if (prefix.length + suffix.length > hostSubdomain.length) | 230 | 211 | return false; | 231 | 212 |
| 232 | | - if (!StringPrototypeStartsWith(hostSubdomain, prefix)) | | 213 | + if (!hostSubdomain.startsWith(prefix)) | 233 | 214 | return false; | 234 | 215 |
| 235 | | - if (!StringPrototypeEndsWith(hostSubdomain, suffix)) | | 216 | + if (!hostSubdomain.endsWith(suffix)) | 236 | 217 | return false; | 237 | 218 |
| 238 | 219 | return true; | @@ -250,30 +231,29 @@ function splitEscapedAltNames(altNames) { | 250 | 231 | let currentToken = ''; | 251 | 232 | let offset = 0; | 252 | 233 | while (offset !== altNames.length) { | 253 | | - const nextSep = StringPrototypeIndexOf(altNames, ', ', offset); | 254 | | - const nextQuote = StringPrototypeIndexOf(altNames, '"', offset); | | 234 | + const nextSep = altNames.indexOf(',', offset); | | 235 | + const nextQuote = altNames.indexOf('"', offset); | 255 | 236 | if (nextQuote !== -1 && (nextSep === -1 || nextQuote < nextSep)) { | 256 | 237 | // There is a quote character and there is no separator before the quote. | 257 | | - currentToken += StringPrototypeSubstring(altNames, offset, nextQuote); | 258 | | - const match = RegExpPrototypeExec( | 259 | | - jsonStringPattern, StringPrototypeSubstring(altNames, nextQuote)); | | 238 | + currentToken += altNames.substring(offset, nextQuote); | | 239 | + const match = jsonStringPattern.exec(altNames.substring(nextQuote)); | 260 | 240 | if (!match) { | 261 | 241 | throw new ERR_TLS_CERT_ALTNAME_FORMAT(); | 262 | 242 | } | 263 | 243 | currentToken += JSONParse(match[0]); | 264 | 244 | offset = nextQuote + match[0].length; | 265 | 245 | } else if (nextSep !== -1) { | 266 | 246 | // 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); | 269 | 249 | currentToken = ''; | 270 | 250 | offset = nextSep + 2; | 271 | 251 | } else { | 272 | | - currentToken += StringPrototypeSubstring(altNames, offset); | | 252 | + currentToken += altNames.substring(offset); | 273 | 253 | offset = altNames.length; | 274 | 254 | } | 275 | 255 | } | 276 | | - ArrayPrototypePush(result, currentToken); | | 256 | + result.push(currentToken); | 277 | 257 | return result; | 278 | 258 | } | 279 | 259 |
| @@ -286,14 +266,14 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) { | 286 | 266 | hostname = '' + hostname; | 287 | 267 |
| 288 | 268 | if (altNames) { | 289 | | - const splitAltNames = StringPrototypeIncludes(altNames, '"') ? | | 269 | + const splitAltNames = altNames.includes('"') ? | 290 | 270 | splitEscapedAltNames(altNames) : | 291 | | - StringPrototypeSplit(altNames, ', '); | 292 | | - ArrayPrototypeForEach(splitAltNames, (name) => { | 293 | | - if (StringPrototypeStartsWith(name, 'DNS:')) { | 294 | | - ArrayPrototypePush(dnsNames, StringPrototypeSlice(name, 4)); | 295 | | - } else if (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 | + } else if (name.startsWith('IP Address:')) { | | 276 | + ips.push(canonicalizeIP(name.slice(11))); | 297 | 277 | } | 298 | 278 | }); | 299 | 279 | } | @@ -304,16 +284,15 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) { | 304 | 284 | hostname = unfqdn(hostname); // Remove trailing dot for error messages. | 305 | 285 |
| 306 | 286 | if (net.isIP(hostname)) { | 307 | | - valid = ArrayPrototypeIncludes(ips, canonicalizeIP(hostname)); | | 287 | + valid = ips.includes(canonicalizeIP(hostname)); | 308 | 288 | 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(', '); | 311 | 290 | } else if (dnsNames.length > 0 || subject?.CN) { | 312 | 291 | const hostParts = splitHost(hostname); | 313 | 292 | const wildcard = (pattern) => check(hostParts, pattern, true); | 314 | 293 |
| 315 | 294 | if (dnsNames.length > 0) { | 316 | | - valid = ArrayPrototypeSome(dnsNames, wildcard); | | 295 | + valid = dnsNames.some(wildcard); | 317 | 296 | if (!valid) | 318 | 297 | reason = | 319 | 298 | `Host: ${hostname}. is not in the cert's altnames: ${altNames}`; | @@ -322,7 +301,7 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) { | 322 | 301 | const cn = subject.CN; | 323 | 302 |
| 324 | 303 | if (ArrayIsArray(cn)) | 325 | | - valid = ArrayPrototypeSome(cn, wildcard); | | 304 | + valid = cn.some(wildcard); | 326 | 305 | else if (cn) | 327 | 306 | valid = wildcard(cn); | 328 | 307 |
| |
|