playwright-core
Advanced tools
Comparing version 1.46.0 to 1.46.1-beta-1723832682000
@@ -10,5 +10,3 @@ "use strict"; | ||
var _net = _interopRequireDefault(require("net")); | ||
var _path = _interopRequireDefault(require("path")); | ||
var _http = _interopRequireDefault(require("http2")); | ||
var _fs = _interopRequireDefault(require("fs")); | ||
var _tls = _interopRequireDefault(require("tls")); | ||
@@ -40,5 +38,9 @@ var _stream = _interopRequireDefault(require("stream")); | ||
if (dummyServerTlsOptions) return; | ||
const { | ||
cert, | ||
key | ||
} = (0, _utils.generateSelfSignedCertificate)(); | ||
dummyServerTlsOptions = { | ||
key: _fs.default.readFileSync(_path.default.join(__dirname, '../../bin/socks-certs/key.pem')), | ||
cert: _fs.default.readFileSync(_path.default.join(__dirname, '../../bin/socks-certs/cert.pem')) | ||
key, | ||
cert | ||
}; | ||
@@ -69,7 +71,5 @@ } | ||
}).then(socket => { | ||
socket.on('secureConnect', () => { | ||
// The server may not respond with ALPN, in which case we default to http/1.1. | ||
result.resolve(socket.alpnProtocol || 'http/1.1'); | ||
socket.end(); | ||
}); | ||
// The server may not respond with ALPN, in which case we default to http/1.1. | ||
result.resolve(socket.alpnProtocol || 'http/1.1'); | ||
socket.end(); | ||
}).catch(error => { | ||
@@ -102,4 +102,4 @@ _debugLogger.debugLogger.log('client-certificates', `ALPN error: ${error.message}`); | ||
this.target = await (0, _happyEyeballs.createSocket)(rewriteToLocalhostIfNeeded(this.host), this.port); | ||
this.target.on('close', this._targetCloseEventListener); | ||
this.target.on('error', error => this.socksProxy._socksProxy.sendSocketError({ | ||
this.target.once('close', this._targetCloseEventListener); | ||
this.target.once('error', error => this.socksProxy._socksProxy.sendSocketError({ | ||
uid: this.uid, | ||
@@ -150,5 +150,5 @@ error: error.message | ||
}); | ||
(_this$internal2 = this.internal) === null || _this$internal2 === void 0 || _this$internal2.on('close', () => dummyServer.close()); | ||
(_this$internal2 = this.internal) === null || _this$internal2 === void 0 || _this$internal2.once('close', () => dummyServer.close()); | ||
dummyServer.emit('connection', this.internal); | ||
dummyServer.on('secureConnection', internalTLS => { | ||
dummyServer.once('secureConnection', internalTLS => { | ||
_debugLogger.debugLogger.log('client-certificates', `Browser->Proxy ${this.host}:${this.port} chooses ALPN ${internalTLS.alpnProtocol}`); | ||
@@ -173,3 +173,3 @@ let targetTLS = undefined; | ||
const session = _http.default.performServerHandshake(internalTLS); | ||
session.on('stream', stream => { | ||
session.once('stream', stream => { | ||
stream.respond({ | ||
@@ -183,3 +183,3 @@ 'content-type': 'text/html', | ||
}); | ||
stream.on('error', () => closeBothSockets()); | ||
stream.once('error', () => closeBothSockets()); | ||
}); | ||
@@ -211,10 +211,9 @@ } else { | ||
targetTLS = _tls.default.connect(tlsOptions); | ||
targetTLS.on('secureConnect', () => { | ||
targetTLS.once('secureConnect', () => { | ||
internalTLS.pipe(targetTLS); | ||
targetTLS.pipe(internalTLS); | ||
}); | ||
internalTLS.on('end', () => closeBothSockets()); | ||
targetTLS.on('end', () => closeBothSockets()); | ||
internalTLS.on('error', () => closeBothSockets()); | ||
targetTLS.on('error', handleError); | ||
internalTLS.once('close', () => closeBothSockets()); | ||
internalTLS.once('error', () => closeBothSockets()); | ||
targetTLS.once('error', handleError); | ||
}); | ||
@@ -221,0 +220,0 @@ }); |
@@ -8,3 +8,5 @@ "use strict"; | ||
exports.createGuid = createGuid; | ||
exports.generateSelfSignedCertificate = generateSelfSignedCertificate; | ||
var _crypto = _interopRequireDefault(require("crypto")); | ||
var _debug = require("./debug"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -34,2 +36,141 @@ /** | ||
return hash.digest('hex'); | ||
} | ||
// Variable-length quantity encoding aka. base-128 encoding | ||
function encodeBase128(value) { | ||
const bytes = []; | ||
do { | ||
let byte = value & 0x7f; | ||
value >>>= 7; | ||
if (bytes.length > 0) byte |= 0x80; | ||
bytes.push(byte); | ||
} while (value > 0); | ||
return Buffer.from(bytes.reverse()); | ||
} | ||
; | ||
// ASN1/DER Speficiation: https://www.itu.int/rec/T-REC-X.680-X.693-202102-I/en | ||
class DER { | ||
static encodeSequence(data) { | ||
return this._encode(0x30, Buffer.concat(data)); | ||
} | ||
static encodeInteger(data) { | ||
(0, _debug.assert)(data >= -128 && data <= 127); | ||
return this._encode(0x02, Buffer.from([data])); | ||
} | ||
static encodeObjectIdentifier(oid) { | ||
const parts = oid.split('.').map(v => Number(v)); | ||
// Encode the second part, which could be large, using base-128 encoding if necessary | ||
const output = [encodeBase128(40 * parts[0] + parts[1])]; | ||
for (let i = 2; i < parts.length; i++) { | ||
output.push(encodeBase128(parts[i])); | ||
} | ||
return this._encode(0x06, Buffer.concat(output)); | ||
} | ||
static encodeNull() { | ||
return Buffer.from([0x05, 0x00]); | ||
} | ||
static encodeSet(data) { | ||
(0, _debug.assert)(data.length === 1, 'Only one item in the set is supported. We\'d need to sort the data to support more.'); | ||
// We expect the data to be already sorted. | ||
return this._encode(0x31, Buffer.concat(data)); | ||
} | ||
static encodeExplicitContextDependent(tag, data) { | ||
return this._encode(0xa0 + tag, data); | ||
} | ||
static encodePrintableString(data) { | ||
return this._encode(0x13, Buffer.from(data)); | ||
} | ||
static encodeBitString(data) { | ||
// The first byte of the content is the number of unused bits at the end | ||
const unusedBits = 0; // Assuming all bits are used | ||
const content = Buffer.concat([Buffer.from([unusedBits]), data]); | ||
return this._encode(0x03, content); | ||
} | ||
static encodeDate(date) { | ||
const year = date.getUTCFullYear(); | ||
const isGeneralizedTime = year >= 2050; | ||
const parts = [isGeneralizedTime ? year.toString() : year.toString().slice(-2), (date.getUTCMonth() + 1).toString().padStart(2, '0'), date.getUTCDate().toString().padStart(2, '0'), date.getUTCHours().toString().padStart(2, '0'), date.getUTCMinutes().toString().padStart(2, '0'), date.getUTCSeconds().toString().padStart(2, '0')]; | ||
const encodedDate = parts.join('') + 'Z'; | ||
const tag = isGeneralizedTime ? 0x18 : 0x17; // 0x18 for GeneralizedTime, 0x17 for UTCTime | ||
return this._encode(tag, Buffer.from(encodedDate)); | ||
} | ||
static _encode(tag, data) { | ||
const lengthBytes = this._encodeLength(data.length); | ||
return Buffer.concat([Buffer.from([tag]), lengthBytes, data]); | ||
} | ||
static _encodeLength(length) { | ||
if (length < 128) { | ||
return Buffer.from([length]); | ||
} else { | ||
const lengthBytes = []; | ||
while (length > 0) { | ||
lengthBytes.unshift(length & 0xFF); | ||
length >>= 8; | ||
} | ||
return Buffer.from([0x80 | lengthBytes.length, ...lengthBytes]); | ||
} | ||
} | ||
} | ||
// X.509 Specification: https://datatracker.ietf.org/doc/html/rfc2459#section-4.1 | ||
function generateSelfSignedCertificate() { | ||
const { | ||
privateKey, | ||
publicKey | ||
} = _crypto.default.generateKeyPairSync('rsa', { | ||
modulusLength: 2048 | ||
}); | ||
const publicKeyDer = publicKey.export({ | ||
type: 'pkcs1', | ||
format: 'der' | ||
}); | ||
const oneYearInMilliseconds = 365 * 24 * 60 * 60 * 1_000; | ||
const notBefore = new Date(new Date().getTime() - oneYearInMilliseconds); | ||
const notAfter = new Date(new Date().getTime() + oneYearInMilliseconds); | ||
// List of fields / structure: https://datatracker.ietf.org/doc/html/rfc2459#section-4.1 | ||
const tbsCertificate = DER.encodeSequence([DER.encodeExplicitContextDependent(0, DER.encodeInteger(1)), | ||
// version | ||
DER.encodeInteger(1), | ||
// serialNumber | ||
DER.encodeSequence([DER.encodeObjectIdentifier('1.2.840.113549.1.1.11'), | ||
// sha256WithRSAEncryption PKCS #1 | ||
DER.encodeNull()]), | ||
// signature | ||
DER.encodeSequence([DER.encodeSet([DER.encodeSequence([DER.encodeObjectIdentifier('2.5.4.3'), | ||
// commonName X.520 DN component | ||
DER.encodePrintableString('localhost')])]), DER.encodeSet([DER.encodeSequence([DER.encodeObjectIdentifier('2.5.4.10'), | ||
// organizationName X.520 DN component | ||
DER.encodePrintableString('Playwright Client Certificate Support')])])]), | ||
// issuer | ||
DER.encodeSequence([DER.encodeDate(notBefore), | ||
// notBefore | ||
DER.encodeDate(notAfter) // notAfter | ||
]), | ||
// validity | ||
DER.encodeSequence([DER.encodeSet([DER.encodeSequence([DER.encodeObjectIdentifier('2.5.4.3'), | ||
// commonName X.520 DN component | ||
DER.encodePrintableString('localhost')])]), DER.encodeSet([DER.encodeSequence([DER.encodeObjectIdentifier('2.5.4.10'), | ||
// organizationName X.520 DN component | ||
DER.encodePrintableString('Playwright Client Certificate Support')])])]), | ||
// subject | ||
DER.encodeSequence([DER.encodeSequence([DER.encodeObjectIdentifier('1.2.840.113549.1.1.1'), | ||
// rsaEncryption PKCS #1 | ||
DER.encodeNull()]), DER.encodeBitString(publicKeyDer)]) // SubjectPublicKeyInfo | ||
]); | ||
const signature = _crypto.default.sign('sha256', tbsCertificate, privateKey); | ||
const certificate = DER.encodeSequence([tbsCertificate, DER.encodeSequence([DER.encodeObjectIdentifier('1.2.840.113549.1.1.11'), | ||
// sha256WithRSAEncryption PKCS #1 | ||
DER.encodeNull()]), DER.encodeBitString(signature)]); | ||
const certPem = ['-----BEGIN CERTIFICATE-----', | ||
// Split the base64 string into lines of 64 characters | ||
certificate.toString('base64').match(/.{1,64}/g).join('\n'), '-----END CERTIFICATE-----'].join('\n'); | ||
return { | ||
cert: certPem, | ||
key: privateKey.export({ | ||
type: 'pkcs1', | ||
format: 'pem' | ||
}) | ||
}; | ||
} |
@@ -87,3 +87,3 @@ "use strict"; | ||
const socket = tls.connect(options); | ||
socket.on('connect', () => resolve(socket)); | ||
socket.on('secureConnect', () => resolve(socket)); | ||
socket.on('error', error => reject(error)); | ||
@@ -93,3 +93,6 @@ } else { | ||
if (err) reject(err); | ||
if (socket) resolve(socket); | ||
if (socket) { | ||
socket.on('secureConnect', () => resolve(socket)); | ||
socket.on('error', error => reject(error)); | ||
} | ||
}, true).catch(err => reject(err)); | ||
@@ -96,0 +99,0 @@ } |
{ | ||
"name": "playwright-core", | ||
"version": "1.46.0", | ||
"version": "1.46.1-beta-1723832682000", | ||
"description": "A high-level API to automate web browsers", | ||
@@ -5,0 +5,0 @@ "repository": { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
7357984
102581
171
305
3