feat: support client certificates
This commit is contained in:
parent
97e2aa07a5
commit
e995f4a6f9
|
|
@ -12,6 +12,9 @@ see [APIRequestContext].
|
|||
|
||||
Creates new instances of [APIRequestContext].
|
||||
|
||||
### option: APIRequest.newContext.clientCertificates = %%-context-option-clientCertificates-%%
|
||||
* since: 1.46
|
||||
|
||||
### option: APIRequest.newContext.useragent = %%-context-option-useragent-%%
|
||||
* since: v1.16
|
||||
|
||||
|
|
|
|||
|
|
@ -256,6 +256,9 @@ await browser.CloseAsync();
|
|||
### option: Browser.newContext.proxy = %%-context-option-proxy-%%
|
||||
* since: v1.8
|
||||
|
||||
### option: Browser.newContext.clientCertificates = %%-context-option-clientCertificates-%%
|
||||
* since: 1.46
|
||||
|
||||
### option: Browser.newContext.storageState = %%-js-python-context-option-storage-state-%%
|
||||
* since: v1.8
|
||||
|
||||
|
|
@ -281,6 +284,9 @@ testing frameworks should explicitly create [`method: Browser.newContext`] follo
|
|||
### option: Browser.newPage.proxy = %%-context-option-proxy-%%
|
||||
* since: v1.8
|
||||
|
||||
### option: Browser.newPage.clientCertificates = %%-context-option-clientCertificates-%%
|
||||
* since: 1.46
|
||||
|
||||
### option: Browser.newPage.storageState = %%-js-python-context-option-storage-state-%%
|
||||
* since: v1.8
|
||||
|
||||
|
|
|
|||
|
|
@ -514,6 +514,17 @@ Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `no_
|
|||
|
||||
Does not enforce fixed viewport, allows resizing window in the headed mode.
|
||||
|
||||
## context-option-clientCertificates
|
||||
- `clientCertificates` <[Array]<[Object]>>
|
||||
- `url` <[string]> Glob pattern to match the URLs that the certificate is valid for.
|
||||
- `certs` <[Array]<[Object]>> List of client certificates to be used.
|
||||
- `cert` ?<[string]> Path to the file with the certificate in PEM format.
|
||||
- `key` ?<[string]> Path to the file with the private key in PEM format.
|
||||
- `passphrase` ?<[string]> Passphrase for the private key (PEM or PFX).
|
||||
- `pfx` ?<[string]> PFX or PKCS12 encoded private key and certificate chain.
|
||||
|
||||
An array of client certificates to be used. Each certificate object must have `cert` and `key` or `pfx` to load the client certificate. Optionally, `passphrase` property should be provided if the private key is encrypted. If the certificate is issued by a custom certificate authority, the `ca` property should be provided with the path to the file with the certificate authority's certificate. If the certificate is valid only for specific URLs, the `url` property should be provided with a glob pattern to match the URLs that the certificate is valid for.
|
||||
|
||||
## context-option-useragent
|
||||
- `userAgent` <[string]>
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,35 @@ export default defineConfig({
|
|||
]
|
||||
});
|
||||
```
|
||||
|
||||
## property: TestOptions.clientCertificates = %%-context-option-clientCertificates-%%
|
||||
* since: 1.46
|
||||
|
||||
**Usage**
|
||||
|
||||
```js title="playwright.config.ts"
|
||||
import { defineConfig } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
projects: [
|
||||
{
|
||||
name: 'Microsoft Edge',
|
||||
use: {
|
||||
...devices['Desktop Edge'],
|
||||
clientCertificates: [{
|
||||
url: 'https://example.com',
|
||||
certs: [{
|
||||
cert: 'client/alice_cert.pem',
|
||||
key: 'client/alice_key.pem',
|
||||
passphase: 'mysecretpassword',
|
||||
}],
|
||||
}],
|
||||
},
|
||||
},
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
## property: TestOptions.colorScheme = %%-context-option-colorscheme-%%
|
||||
* since: v1.10
|
||||
|
||||
|
|
|
|||
19
packages/playwright-core/bin/socks-certs/cert.pem
Normal file
19
packages/playwright-core/bin/socks-certs/cert.pem
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIUTcrzEueVL/OuLHr4LBIPWeS4UL0wDQYJKoZIhvcNAQEL
|
||||
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDcwNDA4NDAzNFoXDTM0MDcw
|
||||
MjA4NDAzNFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAQ8AMIIBCgKCAQEApof+SZVN4UGma4xJDVHhMSpmEJoCdMPr+HFadJJK/brF
|
||||
BNOhA1C5wNk8oD/XYo7enAHQH/EsBnq4MMxv79rXTGnIdXMF+43GdMDh5kh81FQy
|
||||
Esw8Vt4eif9eZkjUxI2GHhR2ovJewmQa7E+SeUB2RzJTqz8QPLhd74JFfgaci+S2
|
||||
8L37ScVjcw55T1PcNflzB4vwsQHBT3yND0MLDhm+8MLzmTl4Mw5PgIOaBl5Jh8Tr
|
||||
wQF4eeeB3FPJoMQhTP8aGBjW1mo+NmSSRAPIAZyhmCAnDeC33yRjAaiHjaL5Pr9f
|
||||
wt5zoF5+U1xWhGXWzGOE6p/VTj62F9a2fOXNHclYJQIDAQABo1MwUTAdBgNVHQ4E
|
||||
FgQU9BoVzGtb5x70KqGO/89N1hyqi5kwHwYDVR0jBBgwFoAU9BoVzGtb5x70KqGO
|
||||
/89N1hyqi5kwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAYcbI
|
||||
wvcfx2p8z0RNN3EA+epKX1SagZyJX4ORIO8kln1sDU+ceHde3n3xnp1dg6HG2qh1
|
||||
a7CZub/fNUaP9R8+6iiV0wPT7Ybkb2NIJcH1yq+/bfSS5OC5DO0yv9SUADdBoDwa
|
||||
zOuBAqdcYW1BHYcbAzsQnniRcejHu06ioaS6SwwJ8150rQnLT4Lh9LAl40W6v4nZ
|
||||
NdTGQETTrbjcgH1ER4IhWTKtVyPOxGF9A/OOawMEdfS8BhUO7YRS4QNFFaQMrJAb
|
||||
MDhDtjSyDogLr8P43xjjWvQWG9a7zTF0kKEsdJ0cEG5HATpg8bPHmrouxbs2HGeH
|
||||
kJXzMykrsYyXsInN3w==
|
||||
-----END CERTIFICATE-----
|
||||
28
packages/playwright-core/bin/socks-certs/key.pem
Normal file
28
packages/playwright-core/bin/socks-certs/key.pem
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmh/5JlU3hQaZr
|
||||
jEkNUeExKmYQmgJ0w+v4cVp0kkr9usUE06EDULnA2TygP9dijt6cAdAf8SwGergw
|
||||
zG/v2tdMach1cwX7jcZ0wOHmSHzUVDISzDxW3h6J/15mSNTEjYYeFHai8l7CZBrs
|
||||
T5J5QHZHMlOrPxA8uF3vgkV+BpyL5LbwvftJxWNzDnlPU9w1+XMHi/CxAcFPfI0P
|
||||
QwsOGb7wwvOZOXgzDk+Ag5oGXkmHxOvBAXh554HcU8mgxCFM/xoYGNbWaj42ZJJE
|
||||
A8gBnKGYICcN4LffJGMBqIeNovk+v1/C3nOgXn5TXFaEZdbMY4Tqn9VOPrYX1rZ8
|
||||
5c0dyVglAgMBAAECggEAB6zX4vNPKhUZAvbtvP/rlZUDLDu05kXLX+F1jk7ZxvTv
|
||||
NKg+UQVM8l7wxN/8YM3944nP2lEGuuu4BoO9mvvmlV6Avy0EdxITNflX0AHCQxT4
|
||||
U9Z253gIR0ruQl+T8tUk+8jsqNjr1iC//ukx8oWujdx7b7aR3IKQzcOeyU6rs2TN
|
||||
lyrVVsEaFVi9+wCw0xyiCmPlobrn+egdigw7Zhp2BRinC6W9eMxuPS2hlhQUhBm/
|
||||
eiD96YWp0RAv/L5qO93reoXIAzrrLdcUgPEnnq1zN7y2xihU2+B2sTph1m/A26+J
|
||||
yPcXd7vQrXlRXQU6PaCa+0oJULlpiAzy3HPbnr4BkQKBgQDdmekTX8dQqiEZPX1C
|
||||
017QRFbx0/x/TDFDSeJbDeauMzzCaGqCO2WVmYmTvFtby2G4/6BYowVtJVHm4uJl
|
||||
XsYk8dWIQGLPIj1Cw7ZieJvb2EVRxgnY2oMaOTOazHzPHFzZV718zwEeZrryT82J
|
||||
881E8wgM8V3DjkS4ye3TbwvimQKBgQDAYa/IdnpAg5z1TREi9Tt8fnoGpmSscAak
|
||||
USgeXVsvoNzXXkE94MiiCOOrX1r68TWYDAzq6MKGDewkWOfLwXWR6D5C2LyE1q9P
|
||||
1pxstgs/nC3ZUTz0yEH47ahSmhywhGlvXXOQEXUSLiVTOdeMCubMqwQW80F1868n
|
||||
aBHcj5/lbQKBgQDIojjsWaNT3TTqbUmj30vQtI8jlBLgDlPr4FEYr5VT0wAH5BHK
|
||||
p4xpzgFJyRfOHG312TuMBM087LUinfjsXsp3WJ1EJ0dO0mk0sY3HyfsTKNRaHTt9
|
||||
Ixnf/DpExS+bNMq73Tyqa6FPrSNFkAtAA4SuEHwRe9aw33ZI+EpjS/8uwQKBgQCi
|
||||
9NwqSLlLVnColEw0uVdXH+cLJPzX19i4bQo3lkp8MJ2ATJWk7XflUPRQoGf3ckQ8
|
||||
c9CpVtoXJUnmi+xkeo21Nu0uQFqHhzZewWIk75rdmdR4ZUjl649+ZQkUVviASNjq
|
||||
fVU7Lp5k9POm6LL9K+rOaPoA2rKTUAQItC2VD4+YjQKBgB6kgvgN6Mz/u0RE3kkV
|
||||
2GOoP5sso71Hxwh7o6JEzUMhR+e/T/LLcBwEjLYcf1FYRySHsXLn2Ar/Uw1J7pAZ
|
||||
ud54/at+7mTDliaT8Ar7S9vcso7ZfmuDX9qB9+c77idPskVBPo2tjJbwvFcB6sww
|
||||
5Elcfmj6tEP4YLJ6Kv3qTPhT
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
@ -28,7 +28,7 @@ import { Worker } from './worker';
|
|||
import { Events } from './events';
|
||||
import { TimeoutSettings } from '../common/timeoutSettings';
|
||||
import { Waiter } from './waiter';
|
||||
import type { URLMatch, Headers, WaitForEventOptions, BrowserContextOptions, StorageState, LaunchOptions } from './types';
|
||||
import type { URLMatch, Headers, WaitForEventOptions, BrowserContextOptions, StorageState, LaunchOptions, ClientCertificate } from './types';
|
||||
import { headersObjectToArray, isRegExp, isString, urlMatchesEqual } from '../utils';
|
||||
import { mkdirIfNeeded } from '../utils/fileUtils';
|
||||
import type * as api from '../../types/types';
|
||||
|
|
@ -529,6 +529,7 @@ export async function prepareBrowserContextParams(options: BrowserContextOptions
|
|||
reducedMotion: options.reducedMotion === null ? 'no-override' : options.reducedMotion,
|
||||
forcedColors: options.forcedColors === null ? 'no-override' : options.forcedColors,
|
||||
acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads),
|
||||
clientCertificates: await toClientCertificatesProtocol(options.clientCertificates),
|
||||
};
|
||||
if (!contextParams.recordVideo && options.videosPath) {
|
||||
contextParams.recordVideo = {
|
||||
|
|
@ -548,3 +549,31 @@ function toAcceptDownloadsProtocol(acceptDownloads?: boolean) {
|
|||
return 'accept';
|
||||
return 'deny';
|
||||
}
|
||||
|
||||
export async function toClientCertificatesProtocol(clientCertificates?: ClientCertificate[]): Promise<channels.PlaywrightNewRequestParams['clientCertificates']> {
|
||||
if (!clientCertificates)
|
||||
return undefined;
|
||||
return await Promise.all(clientCertificates.map(async clientCertificate => {
|
||||
if (clientCertificate.certs.length === 0)
|
||||
throw new Error('No certs specified for url: ' + clientCertificate.url);
|
||||
return {
|
||||
url: clientCertificate.url,
|
||||
certs: await Promise.all(clientCertificate.certs.map(async cert => {
|
||||
if (!cert.cert && !cert.key && !cert.passphrase && !cert.pfx)
|
||||
throw new Error('None of cert, key, passphrase or pfx is specified');
|
||||
if (cert.cert && !cert.key)
|
||||
throw new Error('cert is specified without key');
|
||||
if (!cert.cert && cert.key)
|
||||
throw new Error('key is specified without cert');
|
||||
if (cert.pfx && (cert.cert || cert.key || cert.passphrase))
|
||||
throw new Error('pfx is specified together with cert, key or passphrase');
|
||||
return {
|
||||
cert: cert.cert ? await fs.promises.readFile(cert.cert) : undefined,
|
||||
key: cert.key ? await fs.promises.readFile(cert.key) : undefined,
|
||||
passphrase: cert.passphrase,
|
||||
pfx: cert.pfx ? await fs.promises.readFile(cert.pfx) : undefined,
|
||||
};
|
||||
}))
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,10 +25,11 @@ import { assert, headersObjectToArray, isString } from '../utils';
|
|||
import { mkdirIfNeeded } from '../utils/fileUtils';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { RawHeaders } from './network';
|
||||
import type { FilePayload, Headers, StorageState } from './types';
|
||||
import type { ClientCertificate, FilePayload, Headers, StorageState } from './types';
|
||||
import type { Playwright } from './playwright';
|
||||
import { Tracing } from './tracing';
|
||||
import { TargetClosedError, isTargetClosedError } from './errors';
|
||||
import { toClientCertificatesProtocol } from './browserContext';
|
||||
|
||||
export type FetchOptions = {
|
||||
params?: { [key: string]: string; },
|
||||
|
|
@ -44,9 +45,10 @@ export type FetchOptions = {
|
|||
maxRetries?: number,
|
||||
};
|
||||
|
||||
type NewContextOptions = Omit<channels.PlaywrightNewRequestOptions, 'extraHTTPHeaders' | 'storageState' | 'tracesDir'> & {
|
||||
type NewContextOptions = Omit<channels.PlaywrightNewRequestOptions, 'extraHTTPHeaders' | 'clientCertificates' | 'storageState' | 'tracesDir'> & {
|
||||
extraHTTPHeaders?: Headers,
|
||||
storageState?: string | StorageState,
|
||||
clientCertificates?: ClientCertificate[];
|
||||
};
|
||||
|
||||
type RequestWithBodyOptions = Omit<FetchOptions, 'method'>;
|
||||
|
|
@ -74,6 +76,7 @@ export class APIRequest implements api.APIRequest {
|
|||
extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined,
|
||||
storageState,
|
||||
tracesDir,
|
||||
clientCertificates: await toClientCertificatesProtocol(options.clientCertificates),
|
||||
})).request);
|
||||
this._contexts.add(context);
|
||||
context._request = this;
|
||||
|
|
@ -175,7 +178,7 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
|
|||
const params = objectToArray(options.params);
|
||||
const method = options.method || options.request?.method();
|
||||
// Cannot call allHeaders() here as the request may be paused inside route handler.
|
||||
const headersObj = options.headers || options.request?.headers() ;
|
||||
const headersObj = options.headers || options.request?.headers();
|
||||
const headers = headersObj ? headersObjectToArray(headersObj) : undefined;
|
||||
let jsonData: any;
|
||||
let formData: channels.NameValue[] | undefined;
|
||||
|
|
@ -395,7 +398,7 @@ function isJsonContentType(headers?: HeadersArray): boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
function objectToArray(map?: { [key: string]: any }): NameValue[] | undefined {
|
||||
function objectToArray(map?: { [key: string]: any }): NameValue[] | undefined {
|
||||
if (!map)
|
||||
return undefined;
|
||||
const result = [];
|
||||
|
|
|
|||
|
|
@ -47,7 +47,17 @@ export type SetStorageState = {
|
|||
export type LifecycleEvent = channels.LifecycleEvent;
|
||||
export const kLifecycleEvents: Set<LifecycleEvent> = new Set(['load', 'domcontentloaded', 'networkidle', 'commit']);
|
||||
|
||||
export type BrowserContextOptions = Omit<channels.BrowserNewContextOptions, 'viewport' | 'noDefaultViewport' | 'extraHTTPHeaders' | 'storageState' | 'recordHar' | 'colorScheme' | 'reducedMotion' | 'forcedColors' | 'acceptDownloads'> & {
|
||||
export type ClientCertificate = {
|
||||
url: string;
|
||||
certs: {
|
||||
cert?: string;
|
||||
key?: string;
|
||||
passphrase?: string;
|
||||
pfx?: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type BrowserContextOptions = Omit<channels.BrowserNewContextOptions, 'viewport' | 'noDefaultViewport' | 'extraHTTPHeaders' | 'clientCertificates' | 'storageState' | 'recordHar' | 'colorScheme' | 'reducedMotion' | 'forcedColors' | 'acceptDownloads'> & {
|
||||
viewport?: Size | null;
|
||||
extraHTTPHeaders?: Headers;
|
||||
logger?: Logger;
|
||||
|
|
@ -70,6 +80,7 @@ export type BrowserContextOptions = Omit<channels.BrowserNewContextOptions, 'vie
|
|||
reducedMotion?: 'reduce' | 'no-preference' | null;
|
||||
forcedColors?: 'active' | 'none' | null;
|
||||
acceptDownloads?: boolean;
|
||||
clientCertificates?: ClientCertificate[];
|
||||
};
|
||||
|
||||
type LaunchOverrides = {
|
||||
|
|
|
|||
|
|
@ -331,6 +331,15 @@ scheme.PlaywrightNewRequestParams = tObject({
|
|||
userAgent: tOptional(tString),
|
||||
ignoreHTTPSErrors: tOptional(tBoolean),
|
||||
extraHTTPHeaders: tOptional(tArray(tType('NameValue'))),
|
||||
clientCertificates: tOptional(tArray(tObject({
|
||||
url: tString,
|
||||
certs: tArray(tObject({
|
||||
cert: tOptional(tBinary),
|
||||
key: tOptional(tBinary),
|
||||
passphrase: tOptional(tString),
|
||||
pfx: tOptional(tBinary),
|
||||
})),
|
||||
}))),
|
||||
httpCredentials: tOptional(tObject({
|
||||
username: tString,
|
||||
password: tString,
|
||||
|
|
@ -532,6 +541,15 @@ scheme.BrowserTypeLaunchPersistentContextParams = tObject({
|
|||
height: tNumber,
|
||||
})),
|
||||
ignoreHTTPSErrors: tOptional(tBoolean),
|
||||
clientCertificates: tOptional(tArray(tObject({
|
||||
url: tString,
|
||||
certs: tArray(tObject({
|
||||
cert: tOptional(tBinary),
|
||||
key: tOptional(tBinary),
|
||||
passphrase: tOptional(tString),
|
||||
pfx: tOptional(tBinary),
|
||||
})),
|
||||
}))),
|
||||
javaScriptEnabled: tOptional(tBoolean),
|
||||
bypassCSP: tOptional(tBoolean),
|
||||
userAgent: tOptional(tString),
|
||||
|
|
@ -614,6 +632,15 @@ scheme.BrowserNewContextParams = tObject({
|
|||
height: tNumber,
|
||||
})),
|
||||
ignoreHTTPSErrors: tOptional(tBoolean),
|
||||
clientCertificates: tOptional(tArray(tObject({
|
||||
url: tString,
|
||||
certs: tArray(tObject({
|
||||
cert: tOptional(tBinary),
|
||||
key: tOptional(tBinary),
|
||||
passphrase: tOptional(tString),
|
||||
pfx: tOptional(tBinary),
|
||||
})),
|
||||
}))),
|
||||
javaScriptEnabled: tOptional(tBoolean),
|
||||
bypassCSP: tOptional(tBoolean),
|
||||
userAgent: tOptional(tString),
|
||||
|
|
@ -676,6 +703,15 @@ scheme.BrowserNewContextForReuseParams = tObject({
|
|||
height: tNumber,
|
||||
})),
|
||||
ignoreHTTPSErrors: tOptional(tBoolean),
|
||||
clientCertificates: tOptional(tArray(tObject({
|
||||
url: tString,
|
||||
certs: tArray(tObject({
|
||||
cert: tOptional(tBinary),
|
||||
key: tOptional(tBinary),
|
||||
passphrase: tOptional(tString),
|
||||
pfx: tOptional(tBinary),
|
||||
})),
|
||||
}))),
|
||||
javaScriptEnabled: tOptional(tBoolean),
|
||||
bypassCSP: tOptional(tBoolean),
|
||||
userAgent: tOptional(tString),
|
||||
|
|
@ -2513,6 +2549,15 @@ scheme.AndroidDeviceLaunchBrowserParams = tObject({
|
|||
height: tNumber,
|
||||
})),
|
||||
ignoreHTTPSErrors: tOptional(tBoolean),
|
||||
clientCertificates: tOptional(tArray(tObject({
|
||||
url: tString,
|
||||
certs: tArray(tObject({
|
||||
cert: tOptional(tBinary),
|
||||
key: tOptional(tBinary),
|
||||
passphrase: tOptional(tString),
|
||||
pfx: tOptional(tBinary),
|
||||
})),
|
||||
}))),
|
||||
javaScriptEnabled: tOptional(tBoolean),
|
||||
bypassCSP: tOptional(tBoolean),
|
||||
userAgent: tOptional(tString),
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import type { RecentLogsCollector } from '../utils/debugLogger';
|
|||
import type { CallMetadata } from './instrumentation';
|
||||
import { SdkObject } from './instrumentation';
|
||||
import { Artifact } from './artifact';
|
||||
import { ClientCertificatesProxy, shouldUseMitmSocksProxy } from './socksClientCertificatesInterceptor';
|
||||
|
||||
export interface BrowserProcess {
|
||||
onclose?: ((exitCode: number | null, signal: string | null) => void);
|
||||
|
|
@ -84,7 +85,13 @@ export abstract class Browser extends SdkObject {
|
|||
|
||||
async newContext(metadata: CallMetadata, options: channels.BrowserNewContextParams): Promise<BrowserContext> {
|
||||
validateBrowserContextOptions(options, this.options);
|
||||
let clientCertificateProxy: ClientCertificatesProxy | undefined;
|
||||
if (shouldUseMitmSocksProxy(options)) {
|
||||
clientCertificateProxy = new ClientCertificatesProxy(options);
|
||||
options.proxy = { server: await clientCertificateProxy.listen() };
|
||||
}
|
||||
const context = await this.doCreateNewContext(options);
|
||||
context._socksServer = clientCertificateProxy;
|
||||
if (options.storageState)
|
||||
await context.setStorageState(metadata, options.storageState);
|
||||
return context;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import * as consoleApiSource from '../generated/consoleApiSource';
|
|||
import { BrowserContextAPIRequestContext } from './fetch';
|
||||
import type { Artifact } from './artifact';
|
||||
import { Clock } from './clock';
|
||||
import { shouldUseMitmSocksProxy, type ClientCertificatesProxy } from './socksClientCertificatesInterceptor';
|
||||
|
||||
export abstract class BrowserContext extends SdkObject {
|
||||
static Events = {
|
||||
|
|
@ -90,6 +91,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||
private _debugger!: Debugger;
|
||||
_closeReason: string | undefined;
|
||||
readonly clock: Clock;
|
||||
_socksServer: ClientCertificatesProxy | undefined;
|
||||
|
||||
constructor(browser: Browser, options: channels.BrowserNewContextParams, browserContextId: string | undefined) {
|
||||
super(browser, 'browser-context');
|
||||
|
|
@ -447,6 +449,8 @@ export abstract class BrowserContext extends SdkObject {
|
|||
await harRecorder.flush();
|
||||
await this.tracing.flush();
|
||||
|
||||
await this._socksServer?.close();
|
||||
|
||||
// Cleanup.
|
||||
const promises: Promise<void>[] = [];
|
||||
for (const { context, artifact } of this._browser._idToVideo.values()) {
|
||||
|
|
@ -687,6 +691,8 @@ export function validateBrowserContextOptions(options: channels.BrowserNewContex
|
|||
throw new Error(`Browser needs to be launched with the global proxy. If all contexts override the proxy, global proxy will be never used and can be any string, for example "launch({ proxy: { server: 'http://per-context' } })"`);
|
||||
options.proxy = normalizeProxySettings(options.proxy);
|
||||
}
|
||||
if (shouldUseMitmSocksProxy(options))
|
||||
options.ignoreHTTPSErrors = true;
|
||||
verifyGeolocation(options.geolocation);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import { Tracing } from './trace/recorder/tracing';
|
|||
import type * as types from './types';
|
||||
import type { HeadersArray, ProxySettings } from './types';
|
||||
import { kMaxCookieExpiresDateInSeconds } from './network';
|
||||
import { clientCertificatesToTLSOptions } from './socksClientCertificatesInterceptor';
|
||||
|
||||
type FetchRequestOptions = {
|
||||
userAgent: string;
|
||||
|
|
@ -49,6 +50,7 @@ type FetchRequestOptions = {
|
|||
timeoutSettings: TimeoutSettings;
|
||||
ignoreHTTPSErrors?: boolean;
|
||||
baseURL?: string;
|
||||
clientCertificates?: channels.BrowserNewContextOptions['clientCertificates'];
|
||||
};
|
||||
|
||||
type HeadersObject = Readonly<{ [name: string]: string }>;
|
||||
|
|
@ -190,9 +192,10 @@ export abstract class APIRequestContext extends SdkObject {
|
|||
maxRedirects: params.maxRedirects === 0 ? -1 : params.maxRedirects === undefined ? 20 : params.maxRedirects,
|
||||
timeout,
|
||||
deadline,
|
||||
...clientCertificatesToTLSOptions(this._defaultOptions().clientCertificates, requestUrl.toString()),
|
||||
__testHookLookup: (params as any).__testHookLookup,
|
||||
};
|
||||
// rejectUnauthorized = undefined is treated as true in node 12.
|
||||
// rejectUnauthorized = undefined is treated as true in Node.js 12.
|
||||
if (params.ignoreHTTPSErrors || defaults.ignoreHTTPSErrors)
|
||||
options.rejectUnauthorized = false;
|
||||
|
||||
|
|
@ -351,6 +354,7 @@ export abstract class APIRequestContext extends SdkObject {
|
|||
maxRedirects: options.maxRedirects - 1,
|
||||
timeout: options.timeout,
|
||||
deadline: options.deadline,
|
||||
...clientCertificatesToTLSOptions(this._defaultOptions().clientCertificates, url.toString()),
|
||||
__testHookLookup: options.__testHookLookup,
|
||||
};
|
||||
// rejectUnauthorized = undefined is treated as true in node 12.
|
||||
|
|
@ -522,6 +526,7 @@ export class BrowserContextAPIRequestContext extends APIRequestContext {
|
|||
timeoutSettings: this._context._timeoutSettings,
|
||||
ignoreHTTPSErrors: this._context._options.ignoreHTTPSErrors,
|
||||
baseURL: this._context._options.baseURL,
|
||||
clientCertificates: this._context._options.clientCertificates,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -568,6 +573,7 @@ export class GlobalAPIRequestContext extends APIRequestContext {
|
|||
extraHTTPHeaders: options.extraHTTPHeaders,
|
||||
ignoreHTTPSErrors: !!options.ignoreHTTPSErrors,
|
||||
httpCredentials: options.httpCredentials,
|
||||
clientCertificates: options.clientCertificates,
|
||||
proxy,
|
||||
timeoutSettings,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,203 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type net from 'net';
|
||||
import path from 'path';
|
||||
import type https from 'https';
|
||||
import fs from 'fs';
|
||||
import tls from 'tls';
|
||||
import stream from 'stream';
|
||||
import { createSocket } from '../utils/happy-eyeballs';
|
||||
import { assert, isUnderTest, urlMatches } from '../utils';
|
||||
import type { SocksSocketClosedPayload, SocksSocketDataPayload, SocksSocketRequestedPayload } from '../common/socksProxy';
|
||||
import { SocksProxy } from '../common/socksProxy';
|
||||
import type * as channels from '@protocol/channels';
|
||||
|
||||
class SocksConnectionDuplex extends stream.Duplex {
|
||||
constructor(private readonly writeCallback: (data: Buffer) => void) {
|
||||
super();
|
||||
}
|
||||
override _read(): void {}
|
||||
override _write(chunk: Buffer, encoding: BufferEncoding, callback: (error?: Error | null | undefined) => void): void {
|
||||
this.writeCallback(chunk);
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
class SocksProxyConnection {
|
||||
firstPackageReceived: boolean = false;
|
||||
isTLS: boolean = false;
|
||||
|
||||
target: net.Socket = null!;
|
||||
|
||||
// In case of http, we just pipe data to the target socket and they are |undefined|.
|
||||
internal: stream.Duplex | undefined;
|
||||
internalTLS: tls.TLSSocket | undefined;
|
||||
|
||||
constructor(private readonly socksProxy: ClientCertificatesProxy, private readonly uid: string, private readonly host: string, private readonly port: number) {}
|
||||
|
||||
async connect() {
|
||||
// WebKit on Darwin doesn't proxy localhost requests through the given proxy.
|
||||
// Workaround: Rewrite to localhost during tests.
|
||||
this.target = await createSocket(isUnderTest() ? 'localhost' : this.host, this.port);
|
||||
this.target.on('close', () => this.socksProxy._socksProxy.sendSocketEnd({ uid: this.uid }));
|
||||
this.target.on('error', error => this.socksProxy._socksProxy.sendSocketError({ uid: this.uid, error: error.message }));
|
||||
this.socksProxy._socksProxy.socketConnected({
|
||||
uid: this.uid,
|
||||
host: this.target.localAddress!,
|
||||
port: this.target.localPort!,
|
||||
});
|
||||
}
|
||||
|
||||
public onClose() {
|
||||
this.internal?.destroy();
|
||||
this.target.destroy();
|
||||
}
|
||||
|
||||
public onData(data: Buffer) {
|
||||
// HTTP / TLS are client-hello based protocols. This allows us to detect
|
||||
// the protocol on the first package and attach appropriate listeners.
|
||||
if (!this.firstPackageReceived) {
|
||||
this.firstPackageReceived = true;
|
||||
// 0x16 is SSLv3/TLS "handshake" content type: https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_record
|
||||
this.isTLS = data[0] === 0x16;
|
||||
if (this.isTLS)
|
||||
this._attachTLSListeners();
|
||||
else
|
||||
this.target.on('data', data => this.socksProxy._socksProxy.sendSocketData({ uid: this.uid, data }));
|
||||
}
|
||||
if (this.isTLS)
|
||||
this.internal!.push(data);
|
||||
else
|
||||
this.target.write(data);
|
||||
}
|
||||
|
||||
private _attachTLSListeners() {
|
||||
assert(this.isTLS);
|
||||
|
||||
this.internal = new SocksConnectionDuplex(data => this.socksProxy._socksProxy.sendSocketData({ uid: this.uid, data }));
|
||||
this.internalTLS = new tls.TLSSocket(this.internal, {
|
||||
isServer: true,
|
||||
// openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -keyout key.pem -out cert.pem -subj "/CN=localhost"
|
||||
key: fs.readFileSync(path.join(__dirname, '../../bin/socks-certs/key.pem')),
|
||||
cert: fs.readFileSync(path.join(__dirname, '../../bin/socks-certs/cert.pem')),
|
||||
});
|
||||
this.internalTLS.on('close', () => this.socksProxy._socksProxy.sendSocketEnd({ uid: this.uid }));
|
||||
|
||||
const targetTLS = tls.connect({
|
||||
socket: this.target,
|
||||
rejectUnauthorized: this.socksProxy.contextOptions.ignoreHTTPSErrors === true ? false : true,
|
||||
...clientCertificatesToTLSOptions(this.socksProxy.contextOptions.clientCertificates, `https://${this.host}:${this.port}/`),
|
||||
});
|
||||
|
||||
this.internalTLS.pipe(targetTLS);
|
||||
targetTLS.pipe(this.internalTLS);
|
||||
|
||||
// Handle close and errors
|
||||
const closeBothSockets = () => {
|
||||
this.internalTLS?.end();
|
||||
targetTLS.end();
|
||||
};
|
||||
|
||||
this.internalTLS.on('end', () => closeBothSockets());
|
||||
targetTLS.on('end', () => closeBothSockets());
|
||||
|
||||
this.internalTLS.on('error', () => closeBothSockets());
|
||||
targetTLS.on('error', error => {
|
||||
if (['DEPTH_ZERO_SELF_SIGNED_CERT', 'ERR_SSL_TLSV13_ALERT_CERTIFICATE_REQUIRED'].includes(error.code)) {
|
||||
this.internalTLS!.write('HTTP/1.1 503 Internal Server Error\r\n');
|
||||
this.internalTLS!.write('Content-Type: text/html; charset=utf-8\r\n');
|
||||
const responseBody = 'Self-signed certificate error: ' + error.message;
|
||||
this.internalTLS!.write('Content-Length: ' + Buffer.byteLength(responseBody) + '\r\n');
|
||||
this.internalTLS!.write('\r\n');
|
||||
this.internalTLS!.write(responseBody);
|
||||
this.internalTLS!.end();
|
||||
return;
|
||||
}
|
||||
closeBothSockets();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientCertificatesProxy {
|
||||
_socksProxy: SocksProxy;
|
||||
private _connections: Map<string, SocksProxyConnection> = new Map();
|
||||
|
||||
constructor(
|
||||
public readonly contextOptions: Pick<channels.BrowserNewContextOptions, 'clientCertificates' | 'ignoreHTTPSErrors'>
|
||||
) {
|
||||
this._socksProxy = new SocksProxy();
|
||||
this._socksProxy.setPattern('*');
|
||||
this._socksProxy.addListener(SocksProxy.Events.SocksRequested, async (payload: SocksSocketRequestedPayload) => {
|
||||
try {
|
||||
const connection = new SocksProxyConnection(this, payload.uid, payload.host, payload.port);
|
||||
await connection.connect();
|
||||
this._connections.set(payload.uid, connection);
|
||||
} catch (error) {
|
||||
this._socksProxy.socketFailed({ uid: payload.uid, errorCode: error.code });
|
||||
}
|
||||
});
|
||||
this._socksProxy.addListener(SocksProxy.Events.SocksData, async (payload: SocksSocketDataPayload) => {
|
||||
this._connections.get(payload.uid)?.onData(payload.data);
|
||||
});
|
||||
this._socksProxy.addListener(SocksProxy.Events.SocksClosed, (payload: SocksSocketClosedPayload) => {
|
||||
this._connections.get(payload.uid)?.onClose();
|
||||
this._connections.delete(payload.uid);
|
||||
});
|
||||
}
|
||||
|
||||
public async listen(): Promise<string> {
|
||||
return `socks5://127.0.0.1:${await this._socksProxy.listen(0)}`;
|
||||
}
|
||||
|
||||
public async close() {
|
||||
await this._socksProxy.close();
|
||||
}
|
||||
}
|
||||
|
||||
export function clientCertificatesToTLSOptions(
|
||||
clientCertificates: channels.BrowserNewContextOptions['clientCertificates'],
|
||||
requestURL: string
|
||||
): Pick<https.RequestOptions, 'pfx' | 'key' | 'passphrase' | 'cert' | 'ca'> | undefined {
|
||||
const matchingCerts = clientCertificates?.filter(c => urlMatches(undefined, requestURL, c.url));
|
||||
if (!matchingCerts || !matchingCerts.length)
|
||||
return;
|
||||
const requestOptions = {
|
||||
pfx: [] as { buf: Buffer, passphrase?: string }[],
|
||||
key: [] as { pem: Buffer, passphrase?: string }[],
|
||||
cert: [] as Buffer[],
|
||||
};
|
||||
for (const { certs } of matchingCerts) {
|
||||
for (const cert of certs) {
|
||||
if (cert.cert)
|
||||
requestOptions.cert.push(cert.cert);
|
||||
if (cert.key)
|
||||
requestOptions.key.push({ pem: cert.key, passphrase: cert.passphrase });
|
||||
if (cert.pfx)
|
||||
requestOptions.pfx.push({ buf: cert.pfx, passphrase: cert.passphrase });
|
||||
}
|
||||
}
|
||||
return requestOptions;
|
||||
}
|
||||
|
||||
export function shouldUseMitmSocksProxy(options: {
|
||||
ca?: Buffer[];
|
||||
clientCertificates?: channels.BrowserNewContextOptions['clientCertificates'];
|
||||
}) {
|
||||
if (options.clientCertificates && options.clientCertificates.length > 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
117
packages/playwright-core/types/types.d.ts
vendored
117
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -15590,6 +15590,45 @@ export interface APIRequest {
|
|||
*/
|
||||
baseURL?: string;
|
||||
|
||||
/**
|
||||
* An array of client certificates to be used. Each certificate object must have `cert` and `key` or `pfx` to load the
|
||||
* client certificate. Optionally, `passphrase` property should be provided if the private key is encrypted. If the
|
||||
* certificate is issued by a custom certificate authority, the `ca` property should be provided with the path to the
|
||||
* file with the certificate authority's certificate. If the certificate is valid only for specific URLs, the `url`
|
||||
* property should be provided with a glob pattern to match the URLs that the certificate is valid for.
|
||||
*/
|
||||
clientCertificates?: Array<{
|
||||
/**
|
||||
* Glob pattern to match the URLs that the certificate is valid for.
|
||||
*/
|
||||
url: string;
|
||||
|
||||
/**
|
||||
* List of client certificates to be used.
|
||||
*/
|
||||
certs: Array<{
|
||||
/**
|
||||
* Path to the file with the certificate in PEM format.
|
||||
*/
|
||||
cert?: string;
|
||||
|
||||
/**
|
||||
* Path to the file with the private key in PEM format.
|
||||
*/
|
||||
key?: string;
|
||||
|
||||
/**
|
||||
* Passphrase for the private key (PEM or PFX).
|
||||
*/
|
||||
passphrase?: string;
|
||||
|
||||
/**
|
||||
* PFX or PKCS12 encoded private key and certificate chain.
|
||||
*/
|
||||
pfx?: string;
|
||||
}>;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request. Defaults to none.
|
||||
*/
|
||||
|
|
@ -16741,6 +16780,45 @@ export interface Browser extends EventEmitter {
|
|||
*/
|
||||
bypassCSP?: boolean;
|
||||
|
||||
/**
|
||||
* An array of client certificates to be used. Each certificate object must have `cert` and `key` or `pfx` to load the
|
||||
* client certificate. Optionally, `passphrase` property should be provided if the private key is encrypted. If the
|
||||
* certificate is issued by a custom certificate authority, the `ca` property should be provided with the path to the
|
||||
* file with the certificate authority's certificate. If the certificate is valid only for specific URLs, the `url`
|
||||
* property should be provided with a glob pattern to match the URLs that the certificate is valid for.
|
||||
*/
|
||||
clientCertificates?: Array<{
|
||||
/**
|
||||
* Glob pattern to match the URLs that the certificate is valid for.
|
||||
*/
|
||||
url: string;
|
||||
|
||||
/**
|
||||
* List of client certificates to be used.
|
||||
*/
|
||||
certs: Array<{
|
||||
/**
|
||||
* Path to the file with the certificate in PEM format.
|
||||
*/
|
||||
cert?: string;
|
||||
|
||||
/**
|
||||
* Path to the file with the private key in PEM format.
|
||||
*/
|
||||
key?: string;
|
||||
|
||||
/**
|
||||
* Passphrase for the private key (PEM or PFX).
|
||||
*/
|
||||
passphrase?: string;
|
||||
|
||||
/**
|
||||
* PFX or PKCS12 encoded private key and certificate chain.
|
||||
*/
|
||||
pfx?: string;
|
||||
}>;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See
|
||||
* [page.emulateMedia([options])](https://playwright.dev/docs/api/class-page#page-emulate-media) for more details.
|
||||
|
|
@ -20173,6 +20251,45 @@ export interface BrowserContextOptions {
|
|||
*/
|
||||
bypassCSP?: boolean;
|
||||
|
||||
/**
|
||||
* An array of client certificates to be used. Each certificate object must have `cert` and `key` or `pfx` to load the
|
||||
* client certificate. Optionally, `passphrase` property should be provided if the private key is encrypted. If the
|
||||
* certificate is issued by a custom certificate authority, the `ca` property should be provided with the path to the
|
||||
* file with the certificate authority's certificate. If the certificate is valid only for specific URLs, the `url`
|
||||
* property should be provided with a glob pattern to match the URLs that the certificate is valid for.
|
||||
*/
|
||||
clientCertificates?: Array<{
|
||||
/**
|
||||
* Glob pattern to match the URLs that the certificate is valid for.
|
||||
*/
|
||||
url: string;
|
||||
|
||||
/**
|
||||
* List of client certificates to be used.
|
||||
*/
|
||||
certs: Array<{
|
||||
/**
|
||||
* Path to the file with the certificate in PEM format.
|
||||
*/
|
||||
cert?: string;
|
||||
|
||||
/**
|
||||
* Path to the file with the private key in PEM format.
|
||||
*/
|
||||
key?: string;
|
||||
|
||||
/**
|
||||
* Passphrase for the private key (PEM or PFX).
|
||||
*/
|
||||
passphrase?: string;
|
||||
|
||||
/**
|
||||
* PFX or PKCS12 encoded private key and certificate chain.
|
||||
*/
|
||||
pfx?: string;
|
||||
}>;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See
|
||||
* [page.emulateMedia([options])](https://playwright.dev/docs/api/class-page#page-emulate-media) for more details.
|
||||
|
|
|
|||
36
packages/playwright/types/test.d.ts
vendored
36
packages/playwright/types/test.d.ts
vendored
|
|
@ -4823,6 +4823,7 @@ export type Fixtures<T extends KeyValue = {}, W extends KeyValue = {}, PT extend
|
|||
type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
||||
type BrowserChannel = Exclude<LaunchOptions['channel'], undefined>;
|
||||
type ColorScheme = Exclude<BrowserContextOptions['colorScheme'], undefined>;
|
||||
type ClientCertificate = Exclude<BrowserContextOptions['clientCertificates'], undefined>[0];
|
||||
type ExtraHTTPHeaders = Exclude<BrowserContextOptions['extraHTTPHeaders'], undefined>;
|
||||
type Proxy = Exclude<BrowserContextOptions['proxy'], undefined>;
|
||||
type StorageState = Exclude<BrowserContextOptions['storageState'], undefined>;
|
||||
|
|
@ -5200,6 +5201,41 @@ export interface PlaywrightTestOptions {
|
|||
*
|
||||
*/
|
||||
colorScheme: ColorScheme;
|
||||
/**
|
||||
* An array of client certificates to be used. Each certificate object must have `cert` and `key` or `pfx` to load the
|
||||
* client certificate. Optionally, `passphrase` property should be provided if the private key is encrypted. If the
|
||||
* certificate is issued by a custom certificate authority, the `ca` property should be provided with the path to the
|
||||
* file with the certificate authority's certificate. If the certificate is valid only for specific URLs, the `url`
|
||||
* property should be provided with a glob pattern to match the URLs that the certificate is valid for.
|
||||
*
|
||||
* **Usage**
|
||||
*
|
||||
* ```js
|
||||
* // playwright.config.ts
|
||||
* import { defineConfig } from '@playwright/test';
|
||||
*
|
||||
* export default defineConfig({
|
||||
* projects: [
|
||||
* {
|
||||
* name: 'Microsoft Edge',
|
||||
* use: {
|
||||
* ...devices['Desktop Edge'],
|
||||
* clientCertificates: [{
|
||||
* url: 'https://example.com',
|
||||
* certs: [{
|
||||
* cert: 'client/alice_cert.pem',
|
||||
* key: 'client/alice_key.pem',
|
||||
* passphase: 'mysecretpassword',
|
||||
* }],
|
||||
* }],
|
||||
* },
|
||||
* },
|
||||
* ]
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
clientCertificates: ClientCertificate[];
|
||||
/**
|
||||
* Specify device scale factor (can be thought of as dpr). Defaults to `1`. Learn more about
|
||||
* [emulating devices with device scale factor](https://playwright.dev/docs/emulation#devices).
|
||||
|
|
|
|||
|
|
@ -576,6 +576,15 @@ export type PlaywrightNewRequestParams = {
|
|||
userAgent?: string,
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
extraHTTPHeaders?: NameValue[],
|
||||
clientCertificates?: {
|
||||
url: string,
|
||||
certs: {
|
||||
cert?: Binary,
|
||||
key?: Binary,
|
||||
passphrase?: string,
|
||||
pfx?: Binary,
|
||||
}[],
|
||||
}[],
|
||||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
|
|
@ -600,6 +609,15 @@ export type PlaywrightNewRequestOptions = {
|
|||
userAgent?: string,
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
extraHTTPHeaders?: NameValue[],
|
||||
clientCertificates?: {
|
||||
url: string,
|
||||
certs: {
|
||||
cert?: Binary,
|
||||
key?: Binary,
|
||||
passphrase?: string,
|
||||
pfx?: Binary,
|
||||
}[],
|
||||
}[],
|
||||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
|
|
@ -944,6 +962,15 @@ export type BrowserTypeLaunchPersistentContextParams = {
|
|||
height: number,
|
||||
},
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
clientCertificates?: {
|
||||
url: string,
|
||||
certs: {
|
||||
cert?: Binary,
|
||||
key?: Binary,
|
||||
passphrase?: string,
|
||||
pfx?: Binary,
|
||||
}[],
|
||||
}[],
|
||||
javaScriptEnabled?: boolean,
|
||||
bypassCSP?: boolean,
|
||||
userAgent?: string,
|
||||
|
|
@ -1017,6 +1044,15 @@ export type BrowserTypeLaunchPersistentContextOptions = {
|
|||
height: number,
|
||||
},
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
clientCertificates?: {
|
||||
url: string,
|
||||
certs: {
|
||||
cert?: Binary,
|
||||
key?: Binary,
|
||||
passphrase?: string,
|
||||
pfx?: Binary,
|
||||
}[],
|
||||
}[],
|
||||
javaScriptEnabled?: boolean,
|
||||
bypassCSP?: boolean,
|
||||
userAgent?: string,
|
||||
|
|
@ -1131,6 +1167,15 @@ export type BrowserNewContextParams = {
|
|||
height: number,
|
||||
},
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
clientCertificates?: {
|
||||
url: string,
|
||||
certs: {
|
||||
cert?: Binary,
|
||||
key?: Binary,
|
||||
passphrase?: string,
|
||||
pfx?: Binary,
|
||||
}[],
|
||||
}[],
|
||||
javaScriptEnabled?: boolean,
|
||||
bypassCSP?: boolean,
|
||||
userAgent?: string,
|
||||
|
|
@ -1190,6 +1235,15 @@ export type BrowserNewContextOptions = {
|
|||
height: number,
|
||||
},
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
clientCertificates?: {
|
||||
url: string,
|
||||
certs: {
|
||||
cert?: Binary,
|
||||
key?: Binary,
|
||||
passphrase?: string,
|
||||
pfx?: Binary,
|
||||
}[],
|
||||
}[],
|
||||
javaScriptEnabled?: boolean,
|
||||
bypassCSP?: boolean,
|
||||
userAgent?: string,
|
||||
|
|
@ -1252,6 +1306,15 @@ export type BrowserNewContextForReuseParams = {
|
|||
height: number,
|
||||
},
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
clientCertificates?: {
|
||||
url: string,
|
||||
certs: {
|
||||
cert?: Binary,
|
||||
key?: Binary,
|
||||
passphrase?: string,
|
||||
pfx?: Binary,
|
||||
}[],
|
||||
}[],
|
||||
javaScriptEnabled?: boolean,
|
||||
bypassCSP?: boolean,
|
||||
userAgent?: string,
|
||||
|
|
@ -1311,6 +1374,15 @@ export type BrowserNewContextForReuseOptions = {
|
|||
height: number,
|
||||
},
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
clientCertificates?: {
|
||||
url: string,
|
||||
certs: {
|
||||
cert?: Binary,
|
||||
key?: Binary,
|
||||
passphrase?: string,
|
||||
pfx?: Binary,
|
||||
}[],
|
||||
}[],
|
||||
javaScriptEnabled?: boolean,
|
||||
bypassCSP?: boolean,
|
||||
userAgent?: string,
|
||||
|
|
@ -4558,6 +4630,15 @@ export type AndroidDeviceLaunchBrowserParams = {
|
|||
height: number,
|
||||
},
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
clientCertificates?: {
|
||||
url: string,
|
||||
certs: {
|
||||
cert?: Binary,
|
||||
key?: Binary,
|
||||
passphrase?: string,
|
||||
pfx?: Binary,
|
||||
}[],
|
||||
}[],
|
||||
javaScriptEnabled?: boolean,
|
||||
bypassCSP?: boolean,
|
||||
userAgent?: string,
|
||||
|
|
@ -4615,6 +4696,15 @@ export type AndroidDeviceLaunchBrowserOptions = {
|
|||
height: number,
|
||||
},
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
clientCertificates?: {
|
||||
url: string,
|
||||
certs: {
|
||||
cert?: Binary,
|
||||
key?: Binary,
|
||||
passphrase?: string,
|
||||
pfx?: Binary,
|
||||
}[],
|
||||
}[],
|
||||
javaScriptEnabled?: boolean,
|
||||
bypassCSP?: boolean,
|
||||
userAgent?: string,
|
||||
|
|
|
|||
|
|
@ -433,6 +433,21 @@ ContextOptions:
|
|||
width: number
|
||||
height: number
|
||||
ignoreHTTPSErrors: boolean?
|
||||
clientCertificates:
|
||||
type: array?
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
url: string
|
||||
certs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
cert: binary?
|
||||
key: binary?
|
||||
passphrase: string?
|
||||
pfx: binary?
|
||||
javaScriptEnabled: boolean?
|
||||
bypassCSP: boolean?
|
||||
userAgent: string?
|
||||
|
|
@ -673,6 +688,21 @@ Playwright:
|
|||
extraHTTPHeaders:
|
||||
type: array?
|
||||
items: NameValue
|
||||
clientCertificates:
|
||||
type: array?
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
url: string
|
||||
certs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
cert: binary?
|
||||
key: binary?
|
||||
passphrase: string?
|
||||
pfx: binary?
|
||||
httpCredentials:
|
||||
type: object?
|
||||
properties:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQsFADA2MRIwEAYDVQQDDAlsb2Nh
|
||||
bGhvc3QxIDAeBgNVBAoMF0NsaWVudCBDZXJ0aWZpY2F0ZSBEZW1vMB4XDTI0MDYy
|
||||
NDEyMzEyM1oXDTI1MDYyNDEyMzEyM1owEDEOMAwGA1UEAwwFQWxpY2UwggIiMA0G
|
||||
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC4FGkFO0MTc2xgk8N9lrE8JW5MJQyx
|
||||
me+h4HUqc+FwOddY3EH4akCKuvgmHP8bmB76yg7hsWiMHzNW4dqb+ZpxGaA1FInY
|
||||
2gS/GHPaKmVH+lqtNeIAczu3gNo5yBEVp3GQmv0GGxwp5/ugOu+INfPtPqHEKdRm
|
||||
+Ti7uXvMAeohh4raAv3hx3N2x+AnXpEa4ocz8fXYh4y3puk86KCA9Zq5O7IwB6QC
|
||||
qBxSHdtamniqIzuevouXGd//ZCqjK8P57HqVZiBep/wByQlAN6WTRtjaPwPRT97A
|
||||
YM0ayH91aaQ8BtqSWu2LoXkILV+SRSqLRqbyB4TUaKg0RaG4Fk04vg4xjOnjUNcZ
|
||||
TEAKmAozQXTtazYqXGAgd5VENKk93wK1/B3zGnQBBmyyFsGbkCRZLPkxhXMIhoDc
|
||||
ZcS4qUqVfA+gtv6+KlgiPBD4D5lGEKFPVV03T0v8+elq61+RsWHVa8VO3afFqFvT
|
||||
i8yYwDS8udqGaE5EN7lyl1cYJwafH2mdiNdnQc4/Wp/zqgyPALjCbatyu3PAbTfK
|
||||
DleGBRBfxiLKUB39Jo2bafClqRYQnCdQA1vQX0OF2Q9ZTrsLhskVsVQZN5C/Yopc
|
||||
RsMpDWq7HPAP3WndVeawCBg6QFwnR0z6nmfi+UDz1hiSyl6bf3JJs3Nigll1W7UU
|
||||
J56zv89nS8M4xwIDAQABo0IwQDAdBgNVHQ4EFgQUo4T4xMvLao//h+6AcFhXMe5l
|
||||
iGkwHwYDVR0jBBgwFoAUCtaPQfMWTFHrjCUREWMGqIWYC/MwDQYJKoZIhvcNAQEL
|
||||
BQADggIBABoH28SHV5P1xsrsBtEx8ab0BhSPHuzEI8ytA0KgwojrXRKX5MsLbLyY
|
||||
9lFGH7fu+8830oDnfIe7989JMDGtccLO6DXWoWZjBVTkHPNjPSjF6TrhfqjeqNJH
|
||||
NsPW9XUn3UB2hEYJ1nLLAzYizXYVSfL5HExF6Ph7fAzP6AvSZPS6AsVHXh2/JPvz
|
||||
E/JzbeEewDsv3mB+DqUDne7Ukj/IIegGVx190KXwyLmgXlFd69R9GEVEO4FcXqpA
|
||||
NCCcCmnTzBtzrgYVyWB7vDigMoD2bKWlQXwCgthR6DKywtKpSm9UWilghbgkuzam
|
||||
toAOzIyZFGBt+44MnV/486XhbbIXzlixhSZPgSeOKM1tI69OOZ7ilvR6VxvPOrws
|
||||
ZsUJDWFYYXq++8uzWRe+nHglXEQc+4CWbqqhjnncqh94/jLpHNS+BcrgDK3BuVrq
|
||||
Skj8bNq8xnXgUPr+i9Rzmd4lRefKyjVehbRNXj4gp1Ap/TDQbl/A5Vdb31pTLYvO
|
||||
SvDQXImm5HqV5KyddvKnWCIYVD7ATzcvjLqJl52ykDz4eRorzWQ41K5uwd0SxEyB
|
||||
JZ0MesoWyEaWRlpjzqCNxCwO7Pp6RLMxDvC4/lkRt+7HxLLDWaw7bHplFm24GAYm
|
||||
ebC5La1SdbYKC8AahYwT5Ppn6ThiSqV1m3GsuzgBLjfkYMFGjRQQ
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIEVTCCAj0CAQAwEDEOMAwGA1UEAwwFQWxpY2UwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQC4FGkFO0MTc2xgk8N9lrE8JW5MJQyxme+h4HUqc+FwOddY
|
||||
3EH4akCKuvgmHP8bmB76yg7hsWiMHzNW4dqb+ZpxGaA1FInY2gS/GHPaKmVH+lqt
|
||||
NeIAczu3gNo5yBEVp3GQmv0GGxwp5/ugOu+INfPtPqHEKdRm+Ti7uXvMAeohh4ra
|
||||
Av3hx3N2x+AnXpEa4ocz8fXYh4y3puk86KCA9Zq5O7IwB6QCqBxSHdtamniqIzue
|
||||
vouXGd//ZCqjK8P57HqVZiBep/wByQlAN6WTRtjaPwPRT97AYM0ayH91aaQ8BtqS
|
||||
Wu2LoXkILV+SRSqLRqbyB4TUaKg0RaG4Fk04vg4xjOnjUNcZTEAKmAozQXTtazYq
|
||||
XGAgd5VENKk93wK1/B3zGnQBBmyyFsGbkCRZLPkxhXMIhoDcZcS4qUqVfA+gtv6+
|
||||
KlgiPBD4D5lGEKFPVV03T0v8+elq61+RsWHVa8VO3afFqFvTi8yYwDS8udqGaE5E
|
||||
N7lyl1cYJwafH2mdiNdnQc4/Wp/zqgyPALjCbatyu3PAbTfKDleGBRBfxiLKUB39
|
||||
Jo2bafClqRYQnCdQA1vQX0OF2Q9ZTrsLhskVsVQZN5C/YopcRsMpDWq7HPAP3Wnd
|
||||
VeawCBg6QFwnR0z6nmfi+UDz1hiSyl6bf3JJs3Nigll1W7UUJ56zv89nS8M4xwID
|
||||
AQABoAAwDQYJKoZIhvcNAQELBQADggIBAG7XNJcmByEF0rvSV6bY28PrfirPt09K
|
||||
dTsKNlv9a798k9eq/vqpnVrNslEFj/SVTBPl5r5FIYnueNiO54VlA6nJ+1yRSlvI
|
||||
2SGvgCRoD4xNcMyJgzMwmxovhNRHdheRP+A82EUfgoT8/HCm0UasTw1PcZKokprb
|
||||
T93pie4iV3CWBtVHd9/hZXsMnumT/LIbnUdCOkAsy7cIVsxOHNLZociJeV3LxIgL
|
||||
B5HQdDHPd9i4C6zFgZmm7imrzvFrFk+ksUx1jUeMTXCCvwVyj5cPRbxKduFLCOIA
|
||||
tP61RVeXt6ru5IZl+2n8GApKT9zkPhiGNmCdG54Z7Yc62fvijneeOlKPAwWRbxEq
|
||||
smdW0fOaf73n0eguZ8ujk2ZLVDb2knqk5MoPTuEaSea/haVg/vgP8O5sFwjOcJ6P
|
||||
D1flxGJafOGF0B0hnZXqFjUV2Ty+R5iSk7vaLiwc9asMybqyuEVv5q2/6gmoawt1
|
||||
6srZq/Z3jyaseOzYgeOugZcKZyZNpDgS5ZIq+iKu5fP5gcnmvhZca4fFqd2jYthO
|
||||
v6Xrd5tMcgpcZSRWAezh4p0AICnmdTlpQNcw6rdfr7RAT2OwxQmqW/SbniJCr/Nn
|
||||
W4v6KhZ7mYBrYt7ywVXuhZKmubwShZTprSEsQko5Tujk41/kLJl0C3kIdkMvMWzF
|
||||
gKrkAP6tuQQ7
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
XJVwXoZ9PUMxrfgEI9YmZdHOSJdU9Qc=
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEyzCCArOgAwIBAgIUBO3H8U57HcsnWmwP2Xm7wDgHnmkwDQYJKoZIhvcNAQEL
|
||||
BQAwDjEMMAoGA1UEAwwDQm9iMB4XDTI0MDYyNDEyMzEyOVoXDTI1MDYyNDEyMzEy
|
||||
OVowDjEMMAoGA1UEAwwDQm9iMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
|
||||
AgEAugNWP3UMpf33fOykU7vrIMv1LS32KZjloQnW6o6t5kjt/Yr3JDKnEuMMshpC
|
||||
4YGTEBJ0y5errBbCP3bQY62SnARAgXAaatRqGF+rqiKEnTDCXQo6ex27nna2LgjU
|
||||
JjpU7uqW3fIbECUDIG2zXpE+9VJ1CWViGrRFN7M57zoJ4SEutyMSu9Z8qeZ+/sHm
|
||||
UjqLgimjxx5KSLe6wx9BI7266cgVLbGLMEzZYvDz3+TznzcfTiUs0h74LBDlQYjh
|
||||
76f2td3gZZ7jii2toJgV+E72tjIOANkLVtIVgx0le/4dgwgoIEkjTm5bvbTijIuh
|
||||
O01TYjoV+r+aeTnl+uGGLGTG9KtMj+HOpDxAapFxLS8sM2kn9bBu+6XdP2sa89PX
|
||||
7AmZ1VjsMFILM20kvbOmfnKrVKTszo68LBeMIcYfR60KaPc/ZglvHFE7lxB4rOBz
|
||||
4QNCNMDciEh6npkntjGnO6q01DhYdBpccW65mAqX0+LrkjpUcBneYoTyd0Izd5M1
|
||||
QNm6WaL2Uye+wmNBIptq8I0A6PiWdVbef3xcF70I9JTJHFODHjs1PDov/llZpUyh
|
||||
cNx8WC+y/nPdt+XS0BR/ap0QWJ66CNa5tsLs6txdx6Aoa373bq6HlY/RRlvkhPxr
|
||||
OOKfon7qKQCQGPTSuSvw8qssehcko79chpaYTPJKaJZRK0sCAwEAAaMhMB8wHQYD
|
||||
VR0OBBYEFBcJPRVNgMzwnvzLnNpsVlQySkWpMA0GCSqGSIb3DQEBCwUAA4ICAQAu
|
||||
KjNiFaTPS4vRwva/kNY5MUBcTH0U3BGPOlWyvrudc3FM8+X7OhGpBLLAGmk/H+KT
|
||||
Kzd2B3btr1AAxprLMehxXVlSF0+5A2DLyNDq2wwoI+V2APpeeGA4cCRsL91ZqCH8
|
||||
57T/XpRWKKorlY17yfxO9GUFJGdl1Oki3wvOOaXWcuxUb7nHOjI7oguUcR4jfdIR
|
||||
WfeUe3P+Y8TLVe2WRdJYEdRfpKN2T+8dGNdVHJ3GokgoLQsj8wpKHM0cd81f+mMf
|
||||
jKfP++mR9w+UCRKgWxbCTFMhZvz4BKwpmLI0mLphumiWkfFJPrxTUmx/0JFfOLdi
|
||||
pDHqd5JfSeiBm+hKTWlY/kc7rPSe+VYiXM+Zs+4EIqjowjiRixW+lRU4lf+7ZDlm
|
||||
v+mi4C4JLHW6I7H085GlM+A6BmEPPBRNx2OOPEIqqLCRkJMi3RmS6X6jkAcCdhmn
|
||||
MsxEKjgG8dJn+kfDGxD1Vfz5PjqzFhHPyahdkXo2br8P3RH7jPY9lb6nvAKupHlV
|
||||
GgKJodeibtbZl1eeCrwdHjrawmQ7VhyQ91Dk7PP+z9h6fQqFxxULYP6WC0P7KVfD
|
||||
EBauab6AJdpwKPqsRL9w87yjl2481aeMdtezYjE1HuWTlObq9YcArbpE8Rm5gLb1
|
||||
9bZ5aZKz62m+O3LZMbsMDusgTeyZFMlB0yA32q+SGg==
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIEUzCCAjsCAQAwDjEMMAoGA1UEAwwDQm9iMIICIjANBgkqhkiG9w0BAQEFAAOC
|
||||
Ag8AMIICCgKCAgEAugNWP3UMpf33fOykU7vrIMv1LS32KZjloQnW6o6t5kjt/Yr3
|
||||
JDKnEuMMshpC4YGTEBJ0y5errBbCP3bQY62SnARAgXAaatRqGF+rqiKEnTDCXQo6
|
||||
ex27nna2LgjUJjpU7uqW3fIbECUDIG2zXpE+9VJ1CWViGrRFN7M57zoJ4SEutyMS
|
||||
u9Z8qeZ+/sHmUjqLgimjxx5KSLe6wx9BI7266cgVLbGLMEzZYvDz3+TznzcfTiUs
|
||||
0h74LBDlQYjh76f2td3gZZ7jii2toJgV+E72tjIOANkLVtIVgx0le/4dgwgoIEkj
|
||||
Tm5bvbTijIuhO01TYjoV+r+aeTnl+uGGLGTG9KtMj+HOpDxAapFxLS8sM2kn9bBu
|
||||
+6XdP2sa89PX7AmZ1VjsMFILM20kvbOmfnKrVKTszo68LBeMIcYfR60KaPc/Zglv
|
||||
HFE7lxB4rOBz4QNCNMDciEh6npkntjGnO6q01DhYdBpccW65mAqX0+LrkjpUcBne
|
||||
YoTyd0Izd5M1QNm6WaL2Uye+wmNBIptq8I0A6PiWdVbef3xcF70I9JTJHFODHjs1
|
||||
PDov/llZpUyhcNx8WC+y/nPdt+XS0BR/ap0QWJ66CNa5tsLs6txdx6Aoa373bq6H
|
||||
lY/RRlvkhPxrOOKfon7qKQCQGPTSuSvw8qssehcko79chpaYTPJKaJZRK0sCAwEA
|
||||
AaAAMA0GCSqGSIb3DQEBCwUAA4ICAQAFGvffXDPy6TKwrdQShBTCd2sH6lTW7K3D
|
||||
2AzL8ZWLC20QmQtz3YbyDODnauo/8EKGvCpvTHZZoPQbmKgFro9vbLMp++2npvY8
|
||||
9VD7FENkfnlyYspiaLicmmV+wN8MwDgKhnZYM41GnkxUrDCj8iOmFK3bmvvhOD1H
|
||||
1SCWhnWG6VdaWhIbE0faXzK7+0WhHILaWxZTgVAHKavQ3APYEh2+s1UwseugNBsL
|
||||
1p+ldROFK/SIisyzi009a24/Ccan9peJbmmWKKUF7oGqoIYfoeDMPpCG+rWxzpaG
|
||||
1S4DNAVgupLpX/oapzBZs3+mXCfh9NSkjKW0Z+M2yO0qOrhdnO2tdpQ2693JoxsG
|
||||
mFhNtfno+22pBHUY6w9jIFIxNbNMWlS54fZoCg7pgJ0YmME7zuoeu2IUeDb6MSoE
|
||||
fd4S+iGdIhyb83yafe9ws+6u7Es7/ivrU2E5E9dtae11liuGQvYxqIpR5ArGndwr
|
||||
Kv3czIKmkR6R8a7UPRXRona5do8L4uhBYz8SnEaH3ClW80NttDO5F3geHM+znTCS
|
||||
uc3ruF3fl4h6rh7khDBY7c62jA1F8f+plAgjrCAI6ZSyNUdAMpIzwayGE+i/7M92
|
||||
ivWpiPd4neB3ZSP3T6fNlz2SjnmTMwvGHcqvnYAohdqMqrn9MzA4lLX+/Qg+CeRS
|
||||
m3dDmOG0Kw==
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
n8nUUVeSi2z5bdctVUaQjQqc6zKI
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFTTCCAzWgAwIBAgIUWdMb2scNR1R1TxDuI4RrFt7mGOwwDQYJKoZIhvcNAQEL
|
||||
BQAwNjESMBAGA1UEAwwJbG9jYWxob3N0MSAwHgYDVQQKDBdDbGllbnQgQ2VydGlm
|
||||
aWNhdGUgRGVtbzAeFw0yNDA2MjQxMjMxMTNaFw0yNTA2MjQxMjMxMTNaMDYxEjAQ
|
||||
BgNVBAMMCWxvY2FsaG9zdDEgMB4GA1UECgwXQ2xpZW50IENlcnRpZmljYXRlIERl
|
||||
bW8wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9VMWaGggaYorXO5IF
|
||||
qj3s+W4nvrzzi3LHYjnugFnUAruOYdXbBboieSR2wnJeITyVp0MbVEsrcfwsCmlB
|
||||
RP3ehheJqW2GtDZ8eFTzZ/r2Npl/TQgGn5/R+HYe9NkXfWAT+VanURcNrP98eSRI
|
||||
zDS/Rx5ZgG9/iYRbB/Lcd+Stkp2UTLDlMTvAv8CFwFLE1bwHmcAqYBMMqEpKh3Z5
|
||||
oshmOSQz28A5o3UK2XCJptywV0lbugs0bBSQiayBs2BzMLbUFiWyh45LEvuWLYpv
|
||||
3FKQwMJi+ZBZ0lOXjFOfW0TXlZRLMSrSfnOP0dq8x2QAFEIIKGwgbZXyCLP+BVLD
|
||||
IJ39+AadSjXaJ+12FkOE8ETe6EEIKoydwzFnxtndMvflKYFILRF8vJp44MTJ/cZ2
|
||||
9ZSY7QPVoSEK9KjtedlmYSvyWYYxtoWN5zR/K7Oyb/lFy45oKU75LuWt5qx9X2eA
|
||||
uScy8SWgo17SN1IF1OdOYjExRXmjnHcQJIAPLfX6hkheee9S8uiWy8a42iBJW4ZV
|
||||
QSsGtbMb3BojjykZYkhTdjmjvjy16IqlJ4JEcRGKxuuvmlCXOs33D1ot7xb1GHwX
|
||||
oLvXo2IOSZAjDOSHCOFdvg9h0LcyEeGTeLHDeNrxRUo1z+/zRU3NE8A4UDyKP/rf
|
||||
ZTVphGSNoyvo0UwyIxFQutZK6QIDAQABo1MwUTAdBgNVHQ4EFgQUCtaPQfMWTFHr
|
||||
jCUREWMGqIWYC/MwHwYDVR0jBBgwFoAUCtaPQfMWTFHrjCUREWMGqIWYC/MwDwYD
|
||||
VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANgmi2fMKEFlIyKCz+4Oa
|
||||
GnOKIAACR0+yk2G+nrGxkkmT2bIiibcbYtLmfB3gQyA6O3qoi94t66QS83fIvRA5
|
||||
FHRs/wWGzKHcDCaJApxVhIN7V+480NB2W7VS2KSQh9EQQ9dziXS6xftycz7LeLR0
|
||||
WYcvojLCd1tkCFFnaN0cRjqjMdfGfsONtvc5KFGoySNs3r65ckQct7e34BELMBjv
|
||||
Zd+myNNnw6i8cnHFcnJpSYajX3dJrmaMtapjORjTjlpsjF7oYOx2dANHzOJ8sVqo
|
||||
3cElK/Ou88S/EHX/xOXx/jncz2OPGGL/8UEDh9Z2Co4/p1TYaxMHOBTlWlqjERWZ
|
||||
AmA9v9lU6gn+o5ITz0wm3M+StxF3DU9nCu3/FCezObNPM1sILRZoYmx8Ok85G/0l
|
||||
DeYgxE4je4CCh1rxyRZClRljbimE/FBbw/Ui6zrHiTAABfwVirY+wyDvPMiYhGmt
|
||||
sOfEveXVn5aX7sW1iOChZP3p2KuL5a19T5sl7+mev6Z1sea3IovYeXKsRFEVhTUU
|
||||
7533qt9lknpip9I27BT8aJKWUGP3GWXDs/DVoSkygnvbDDJfLK5Wqp8EPZCLyS1K
|
||||
L/lWamgel5BgCwb091P2rNT7Lu+XGhJOcfyFk1FAwiZ30yi3As4W9HXvdh3CNm5x
|
||||
55g7Z6Xmxn6xdtviPZmBPZU=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFTTCCAzWgAwIBAgIUTivXxf4wmYQ2SLCYkHRAIa1oI/kwDQYJKoZIhvcNAQEL
|
||||
BQAwNjESMBAGA1UEAwwJbG9jYWxob3N0MSAwHgYDVQQKDBdDbGllbnQgQ2VydGlm
|
||||
aWNhdGUgRGVtbzAeFw0yNDA2MjQxMjUyMDlaFw0yNTA2MjQxMjUyMDlaMDYxEjAQ
|
||||
BgNVBAMMCWxvY2FsaG9zdDEgMB4GA1UECgwXQ2xpZW50IENlcnRpZmljYXRlIERl
|
||||
bW8wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDHVOjEeKWwo0+X5ZDV
|
||||
bSVAHUDklVvcCczZWtIc3DOEU84Gc+JP/ug1AhgEAS4LbIMEpyIyzBcm0mMrLT7i
|
||||
02ci7J3ybIlaIn2gNQ703WaJ2hWQ+GzubFKDCZmiiabCZy/uYyJ9D0EWZ6gK64xx
|
||||
gWXiiud8Lbje73RwjquKCKDTH8kd4s99taK4UmpXa6ERVUTCbIOCq0rNvJTQYCf6
|
||||
XDVZeucnW4CceOyIbeYA0hbBA+WF4BuyC0rDYdyQLNPQPEMwo/thLc9VhEMiL0B0
|
||||
QHXvVNO3T0OSVcdgogjTpbAk6thKHjKjeqrwtFaU4pmyT68yxPk6NCKgVfMQTRt2
|
||||
5o5kCG3QyoFAWA0jMz+IGtwtjbn0wVFhVfRlX/pUQHrdxn8o+S+ynbM87b7DHEO/
|
||||
/RiMAropa4jcVWe0PC+1vJm7bXm1aLt0vUrde8lzYNN1C1iEVbF/yPvGBysxbFrh
|
||||
Ec4Temuj7B9vFieKf5hw7+lEzVteABUUee+zhmR8lunQz4fa09QB59ot2Z//IZyF
|
||||
+uduZZjYqV6YAzlulXzXJb+LAdpWVWXZzwq+ORhh0cExkZ0R72qrB7cj3qodR03t
|
||||
yYCddmK2/Iafw7HoN7Wa0aVFw81NSEyFKwL/Mxs+QQy0jkRzBPCTh8NzdWBA40Qo
|
||||
ZPjrfBZo3bHAzMFJ95MN2p04OQIDAQABo1MwUTAdBgNVHQ4EFgQUpGMrYBCb6V6L
|
||||
gjM7kN+rcFqcCYcwHwYDVR0jBBgwFoAUpGMrYBCb6V6LgjM7kN+rcFqcCYcwDwYD
|
||||
VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAPY42mRjSrkeQBhfINNy+
|
||||
W2S+1XIkxVcNyczt4xsxErUesrlStHFthv1n4b2RZbbAREQWNm7d06B0FvQ1/XBn
|
||||
NAvVKCN6gf/jO+BYv8kn/8TniWVmrlEn9Y3sX8H0bqReYKQD9YmroO8vbHFYpDpY
|
||||
vOiS1J0S5nePXz1VvhQ7VG3V/OP6jGT1NqDZU39AlbA8NN1WiftB6JxK9UvFQnCf
|
||||
coG8maKoDtlf4bXTbZMbsF80ocDJLWq0BJjnKy7I79qOr66mt+nqB93v/2zTIToP
|
||||
FD9Uw3lX+Vseyya3G5AqZZJj2suL/TZx3RkicGZyz/5v7dB0YuX/yjvKtdAif9Lj
|
||||
w/kaLNeICuiolwSia6Ss1V8IGMYyFoRIKcQyA1pWmSYRbP2b2ZVctK/zGgt2ydHG
|
||||
OUSENBoUykU3z8k+jr6cNKPwZJBITAD1vN2M6GFfET4R1iw55gcc2D5Tm8dkIHSq
|
||||
WXoLDpofnABkaowaD5Xe6tySCoHWxpkIDSx8mse/sM8S15azDhL7V+IWWequ7i2p
|
||||
oG5q19E3uy3IoANZdBzCZoYTcjv/CmTSJMOTMk4MaT4JmtGzMGNWwmr6dVOR1c21
|
||||
FuzKN5liY6qRzYgOisPCUwkYXkJelfwup3nK0g3jBI9S8lB2ucfB15dRmwVjGP1G
|
||||
ySv953S5j/5ZLvJmGkahPTw=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
m3Ea0Y3lf0rJCBJ10pFBP3z8orJyWkI=
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
3bsZaDP7F6iGp+Kh73PM8Yxe9Pyc
|
||||
-----END PRIVATE KEY-----
|
||||
252
tests/library/client-certificates.spec.ts
Normal file
252
tests/library/client-certificates.spec.ts
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { expect, playwrightTest as base } from '../config/browserTest';
|
||||
import https from 'https';
|
||||
import type net from 'net';
|
||||
|
||||
const test = base.extend<{ serverURL: string, serverURLRewrittenToLocalhost: string }>({
|
||||
serverURL: async ({ }, use) => {
|
||||
const server = https.createServer({
|
||||
key: fs.readFileSync(path.join(kClientCertificatesDir, 'server/server_key.pem')),
|
||||
cert: fs.readFileSync(path.join(kClientCertificatesDir, 'server/server_cert.pem')),
|
||||
ca: [
|
||||
fs.readFileSync(path.join(kClientCertificatesDir, 'server/server_cert.pem')),
|
||||
],
|
||||
requestCert: true,
|
||||
rejectUnauthorized: false,
|
||||
}, (req, res) => {
|
||||
const cert = (req.socket as import('tls').TLSSocket).getPeerCertificate();
|
||||
if ((req as any).client.authorized) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
res.end(`Hello ${cert.subject.CN}, your certificate was issued by ${cert.issuer.CN}!`);
|
||||
} else if (cert.subject) {
|
||||
res.writeHead(403, { 'Content-Type': 'text/html' });
|
||||
res.end(`Sorry ${cert.subject.CN}, certificates from ${cert.issuer.CN} are not welcome here.`);
|
||||
} else {
|
||||
res.writeHead(401, { 'Content-Type': 'text/html' });
|
||||
res.end(`Sorry, but you need to provide a client certificate to continue.`);
|
||||
}
|
||||
});
|
||||
await new Promise<void>(f => server.listen(0, 'localhost', () => f()));
|
||||
await use(`https://localhost:${(server.address() as net.AddressInfo).port}/`);
|
||||
await new Promise<void>(resolve => server.close(() => resolve()));
|
||||
},
|
||||
serverURLRewrittenToLocalhost: async ({ serverURL, browserName }, use) => {
|
||||
const parsed = new URL(serverURL);
|
||||
parsed.hostname = 'i-get-rewritten-to-localhost-on-the-server-side';
|
||||
const shouldRewriteToLocalhost = browserName === 'webkit' && process.platform === 'darwin';
|
||||
await use(shouldRewriteToLocalhost ? parsed.toString() : serverURL);
|
||||
}
|
||||
});
|
||||
|
||||
test.skip(({ mode }) => mode !== 'default');
|
||||
|
||||
const kClientCertificatesDir = path.join(__dirname, '../config/testserver/client-certificates');
|
||||
const kValidationSubTests: any[] = [
|
||||
[[{ url: 'test', certs: [] }], 'No certs specified for url: test'],
|
||||
[[{ url: 'test', certs: [{}] }]], 'None of cert, key, passphrase or pfx is specified',
|
||||
[[{
|
||||
url: 'test', certs: [{
|
||||
cert: 'foo',
|
||||
key: 'foo',
|
||||
passphrase: 'foo',
|
||||
pfx: 'foo',
|
||||
}]
|
||||
}], 'pfx is specified together with cert, key or passphrase']
|
||||
];
|
||||
|
||||
test.describe('fetch', () => {
|
||||
test('validate input', async ({ playwright }) => {
|
||||
for (const [clientCertificates, expected] of kValidationSubTests)
|
||||
await expect(playwright.request.newContext({ clientCertificates })).rejects.toThrow(expected);
|
||||
});
|
||||
|
||||
test('should fail with no client certificates provided', async ({ playwright, serverURL }) => {
|
||||
const request = await playwright.request.newContext({ ignoreHTTPSErrors: true });
|
||||
const response = await request.get(serverURL);
|
||||
expect(response.status()).toBe(401);
|
||||
expect(await response.text()).toBe('Sorry, but you need to provide a client certificate to continue.');
|
||||
await request.dispose();
|
||||
});
|
||||
|
||||
test('should keep supporting http', async ({ playwright, server }) => {
|
||||
const request = await playwright.request.newContext({
|
||||
clientCertificates: [{
|
||||
url: server.PREFIX,
|
||||
certs: [{
|
||||
cert: path.join(kClientCertificatesDir, 'client/alice_cert.pem'),
|
||||
key: path.join(kClientCertificatesDir, 'client/alice_key.pem'),
|
||||
}],
|
||||
}],
|
||||
});
|
||||
const response = await request.get(server.PREFIX + '/one-style.html');
|
||||
expect(response.url()).toBe(server.PREFIX + '/one-style.html');
|
||||
expect(response.status()).toBe(200);
|
||||
expect(await response.text()).toContain('<div>hello, world!</div>');
|
||||
await request.dispose();
|
||||
});
|
||||
|
||||
test('should throw with a untrusted CA', async ({ playwright, serverURL }) => {
|
||||
const request = await playwright.request.newContext({
|
||||
clientCertificates: [{
|
||||
url: serverURL,
|
||||
certs: [{
|
||||
cert: path.join(kClientCertificatesDir, 'client/alice_cert.pem'),
|
||||
key: path.join(kClientCertificatesDir, 'client/alice_key.pem'),
|
||||
}],
|
||||
}],
|
||||
});
|
||||
try {
|
||||
const response = await request.get(serverURL);
|
||||
expect(response.status()).toBe(503);
|
||||
expect(await response.text()).toBe('Self-signed certificate error: self-signed certificate');
|
||||
} catch (error) {
|
||||
expect(error.message).toContain('self-signed certificate');
|
||||
}
|
||||
});
|
||||
|
||||
test('should throw with matching CA but untrusted client certs', async ({ playwright, serverURL }) => {
|
||||
const request = await playwright.request.newContext({
|
||||
ignoreHTTPSErrors: true,
|
||||
clientCertificates: [{
|
||||
url: serverURL,
|
||||
certs: [{
|
||||
cert: path.join(kClientCertificatesDir, 'client/bob_cert.pem'),
|
||||
key: path.join(kClientCertificatesDir, 'client/bob_key.pem'),
|
||||
}],
|
||||
}],
|
||||
});
|
||||
const response = await request.get(serverURL);
|
||||
expect(response.url()).toBe(serverURL);
|
||||
expect(response.status()).toBe(403);
|
||||
expect(await response.text()).toBe('Sorry Bob, certificates from Bob are not welcome here.');
|
||||
await request.dispose();
|
||||
});
|
||||
|
||||
test('should work without CA', async ({ playwright, serverURL }) => {
|
||||
const request = await playwright.request.newContext({
|
||||
clientCertificates: [{
|
||||
url: serverURL,
|
||||
certs: [{
|
||||
cert: path.join(kClientCertificatesDir, 'client/alice_cert.pem'),
|
||||
key: path.join(kClientCertificatesDir, 'client/alice_key.pem'),
|
||||
}],
|
||||
}],
|
||||
ignoreHTTPSErrors: true,
|
||||
});
|
||||
const response = await request.get(serverURL);
|
||||
expect(response.url()).toBe(serverURL);
|
||||
expect(response.status()).toBe(200);
|
||||
expect(await response.text()).toBe('Hello Alice, your certificate was issued by localhost!');
|
||||
await request.dispose();
|
||||
});
|
||||
|
||||
test('should work in the browser with request interception', async ({ browser, playwright, serverURL }) => {
|
||||
const request = await playwright.request.newContext({
|
||||
ignoreHTTPSErrors: true,
|
||||
clientCertificates: [{
|
||||
url: serverURL,
|
||||
certs: [{
|
||||
cert: path.join(kClientCertificatesDir, 'client/alice_cert.pem'),
|
||||
key: path.join(kClientCertificatesDir, 'client/alice_key.pem'),
|
||||
}],
|
||||
}],
|
||||
});
|
||||
const page = await browser.newPage({ ignoreHTTPSErrors: true });
|
||||
await page.route('**/*', async route => {
|
||||
const response = await request.fetch(route.request());
|
||||
await route.fulfill({ response });
|
||||
});
|
||||
await page.goto(serverURL);
|
||||
await expect(page.getByText('Hello Alice, your certificate was issued by localhost!')).toBeVisible();
|
||||
await page.close();
|
||||
await request.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test.describe('browser', () => {
|
||||
test('validate input', async ({ browser }) => {
|
||||
for (const [clientCertificates, expected] of kValidationSubTests)
|
||||
await expect(browser.newContext({ clientCertificates })).rejects.toThrow(expected);
|
||||
});
|
||||
|
||||
test('should keep supporting http', async ({ browser, server }) => {
|
||||
const page = await browser.newPage({
|
||||
clientCertificates: [{
|
||||
url: server.PREFIX,
|
||||
certs: [{
|
||||
cert: path.join(kClientCertificatesDir, 'client/alice_cert.pem'),
|
||||
key: path.join(kClientCertificatesDir, 'client/alice_key.pem'),
|
||||
}],
|
||||
}],
|
||||
});
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
await expect(page.getByText('hello, world!')).toBeVisible();
|
||||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
test('should fail with no client certificates', async ({ browser, serverURLRewrittenToLocalhost }) => {
|
||||
const page = await browser.newPage({
|
||||
ignoreHTTPSErrors: true,
|
||||
clientCertificates: [{
|
||||
url: 'https://not-matching.com',
|
||||
certs: [{
|
||||
cert: path.join(kClientCertificatesDir, 'client/alice_cert.pem'),
|
||||
key: path.join(kClientCertificatesDir, 'client/alice_key.pem'),
|
||||
}],
|
||||
}],
|
||||
});
|
||||
await page.goto(serverURLRewrittenToLocalhost);
|
||||
await expect(page.getByText('Sorry, but you need to provide a client certificate to continue.')).toBeVisible();
|
||||
await page.close();
|
||||
});
|
||||
|
||||
test('should fail with untrusted client certs', async ({ browser, serverURLRewrittenToLocalhost }) => {
|
||||
const page = await browser.newPage({
|
||||
clientCertificates: [{
|
||||
url: serverURLRewrittenToLocalhost,
|
||||
certs: [{
|
||||
cert: path.join(kClientCertificatesDir, 'client/bob_cert.pem'),
|
||||
key: path.join(kClientCertificatesDir, 'client/bob_key.pem'),
|
||||
}],
|
||||
}],
|
||||
});
|
||||
await page.goto(serverURLRewrittenToLocalhost);
|
||||
await expect(page.getByText('Sorry Bob, certificates from Bob are not welcome here')).toBeVisible();
|
||||
await page.close();
|
||||
});
|
||||
|
||||
test('should pass with matching certificates', async ({ browser, serverURLRewrittenToLocalhost }) => {
|
||||
const page = await browser.newPage({
|
||||
ignoreHTTPSErrors: true,
|
||||
clientCertificates: [{
|
||||
url: serverURLRewrittenToLocalhost,
|
||||
certs: [{
|
||||
cert: path.join(kClientCertificatesDir, 'client/alice_cert.pem'),
|
||||
key: path.join(kClientCertificatesDir, 'client/alice_key.pem'),
|
||||
}],
|
||||
}],
|
||||
});
|
||||
await page.goto(serverURLRewrittenToLocalhost);
|
||||
await expect(page.getByText('Hello Alice, your certificate was issued by localhost!')).toBeVisible();
|
||||
await page.close();
|
||||
});
|
||||
});
|
||||
2
utils/generate_types/overrides-test.d.ts
vendored
2
utils/generate_types/overrides-test.d.ts
vendored
|
|
@ -152,6 +152,7 @@ export type Fixtures<T extends KeyValue = {}, W extends KeyValue = {}, PT extend
|
|||
type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
||||
type BrowserChannel = Exclude<LaunchOptions['channel'], undefined>;
|
||||
type ColorScheme = Exclude<BrowserContextOptions['colorScheme'], undefined>;
|
||||
type ClientCertificate = Exclude<BrowserContextOptions['clientCertificates'], undefined>[0];
|
||||
type ExtraHTTPHeaders = Exclude<BrowserContextOptions['extraHTTPHeaders'], undefined>;
|
||||
type Proxy = Exclude<BrowserContextOptions['proxy'], undefined>;
|
||||
type StorageState = Exclude<BrowserContextOptions['storageState'], undefined>;
|
||||
|
|
@ -209,6 +210,7 @@ export interface PlaywrightTestOptions {
|
|||
acceptDownloads: boolean;
|
||||
bypassCSP: boolean;
|
||||
colorScheme: ColorScheme;
|
||||
clientCertificates: ClientCertificate[];
|
||||
deviceScaleFactor: number | undefined;
|
||||
extraHTTPHeaders: ExtraHTTPHeaders | undefined;
|
||||
geolocation: Geolocation | undefined;
|
||||
|
|
|
|||
Loading…
Reference in a new issue