browserclaw 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +64 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -9
- package/dist/index.d.ts +47 -9
- package/dist/index.js +63 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as playwright_core from 'playwright-core';
|
|
2
|
+
import { lookup } from 'node:dns/promises';
|
|
2
3
|
|
|
3
4
|
interface FrameEvalResult {
|
|
4
5
|
frameUrl: string;
|
|
@@ -6,6 +7,18 @@ interface FrameEvalResult {
|
|
|
6
7
|
result: unknown;
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Policy for controlling which URLs browser navigation is allowed to reach.
|
|
12
|
+
* By default all private/internal addresses are blocked to prevent SSRF attacks.
|
|
13
|
+
*/
|
|
14
|
+
interface SsrfPolicy {
|
|
15
|
+
/** Allow navigation to private/internal network addresses. Default: `false` */
|
|
16
|
+
allowPrivateNetwork?: boolean;
|
|
17
|
+
/** Hostnames explicitly allowed even if they resolve to private addresses */
|
|
18
|
+
allowedHostnames?: string[];
|
|
19
|
+
/** Alias for allowedHostnames */
|
|
20
|
+
hostnameAllowlist?: string[];
|
|
21
|
+
}
|
|
9
22
|
/** Supported browser types that can be detected and launched. */
|
|
10
23
|
type ChromeKind = 'chrome' | 'brave' | 'edge' | 'chromium' | 'canary' | 'custom';
|
|
11
24
|
/** A detected browser executable on the system. */
|
|
@@ -33,19 +46,27 @@ interface LaunchOptions {
|
|
|
33
46
|
profileColor?: string;
|
|
34
47
|
/** Additional Chrome command-line arguments (e.g. `['--start-maximized']`). */
|
|
35
48
|
chromeArgs?: string[];
|
|
49
|
+
/**
|
|
50
|
+
* SSRF policy controlling which URLs navigation is allowed to reach.
|
|
51
|
+
* By default all private/internal addresses are blocked.
|
|
52
|
+
*/
|
|
53
|
+
ssrfPolicy?: SsrfPolicy;
|
|
36
54
|
/**
|
|
37
55
|
* Allow navigation to internal/loopback addresses (localhost, 127.x, private IPs).
|
|
38
|
-
*
|
|
39
|
-
* Set to `true` if you need to access local development servers.
|
|
56
|
+
* @deprecated Use `ssrfPolicy: { allowPrivateNetwork: true }` instead.
|
|
40
57
|
*/
|
|
41
58
|
allowInternal?: boolean;
|
|
42
59
|
}
|
|
43
60
|
/** Options for connecting to an existing browser instance. */
|
|
44
61
|
interface ConnectOptions {
|
|
62
|
+
/**
|
|
63
|
+
* SSRF policy controlling which URLs navigation is allowed to reach.
|
|
64
|
+
* By default all private/internal addresses are blocked.
|
|
65
|
+
*/
|
|
66
|
+
ssrfPolicy?: SsrfPolicy;
|
|
45
67
|
/**
|
|
46
68
|
* Allow navigation to internal/loopback addresses (localhost, 127.x, private IPs).
|
|
47
|
-
*
|
|
48
|
-
* Set to `true` if you need to access local development servers.
|
|
69
|
+
* @deprecated Use `ssrfPolicy: { allowPrivateNetwork: true }` instead.
|
|
49
70
|
*/
|
|
50
71
|
allowInternal?: boolean;
|
|
51
72
|
/**
|
|
@@ -176,7 +197,7 @@ interface ClickOptions {
|
|
|
176
197
|
/** Mouse button to use */
|
|
177
198
|
button?: 'left' | 'right' | 'middle';
|
|
178
199
|
/** Modifier keys to hold during click */
|
|
179
|
-
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[];
|
|
200
|
+
modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[];
|
|
180
201
|
/** Timeout in milliseconds. Default: `8000` */
|
|
181
202
|
timeoutMs?: number;
|
|
182
203
|
}
|
|
@@ -390,9 +411,9 @@ interface HttpCredentials {
|
|
|
390
411
|
declare class CrawlPage {
|
|
391
412
|
private readonly cdpUrl;
|
|
392
413
|
private readonly targetId;
|
|
393
|
-
private readonly
|
|
414
|
+
private readonly ssrfPolicy;
|
|
394
415
|
/** @internal */
|
|
395
|
-
constructor(cdpUrl: string, targetId: string,
|
|
416
|
+
constructor(cdpUrl: string, targetId: string, ssrfPolicy?: SsrfPolicy);
|
|
396
417
|
/** The CDP target ID for this page. Use this to identify the page in multi-tab scenarios. */
|
|
397
418
|
get id(): string;
|
|
398
419
|
/**
|
|
@@ -663,6 +684,8 @@ declare class CrawlPage {
|
|
|
663
684
|
*/
|
|
664
685
|
evaluate(fn: string, opts?: {
|
|
665
686
|
ref?: string;
|
|
687
|
+
timeoutMs?: number;
|
|
688
|
+
signal?: AbortSignal;
|
|
666
689
|
}): Promise<unknown>;
|
|
667
690
|
/**
|
|
668
691
|
* Run JavaScript in ALL frames on the page (including cross-origin iframes).
|
|
@@ -987,7 +1010,7 @@ declare class CrawlPage {
|
|
|
987
1010
|
*/
|
|
988
1011
|
declare class BrowserClaw {
|
|
989
1012
|
private readonly cdpUrl;
|
|
990
|
-
private readonly
|
|
1013
|
+
private readonly ssrfPolicy;
|
|
991
1014
|
private chrome;
|
|
992
1015
|
private constructor();
|
|
993
1016
|
/**
|
|
@@ -1087,6 +1110,7 @@ declare class BrowserClaw {
|
|
|
1087
1110
|
stop(): Promise<void>;
|
|
1088
1111
|
}
|
|
1089
1112
|
|
|
1113
|
+
type LookupFn = typeof lookup;
|
|
1090
1114
|
/**
|
|
1091
1115
|
* Thrown when a navigation URL is blocked by SSRF policy.
|
|
1092
1116
|
* Callers can catch this specifically to distinguish navigation blocks
|
|
@@ -1095,5 +1119,19 @@ declare class BrowserClaw {
|
|
|
1095
1119
|
declare class InvalidBrowserNavigationUrlError extends Error {
|
|
1096
1120
|
constructor(message: string);
|
|
1097
1121
|
}
|
|
1122
|
+
/** Options for browser navigation SSRF policy. */
|
|
1123
|
+
type BrowserNavigationPolicyOptions = {
|
|
1124
|
+
ssrfPolicy?: SsrfPolicy;
|
|
1125
|
+
};
|
|
1126
|
+
/** Build a BrowserNavigationPolicyOptions from an SsrfPolicy. */
|
|
1127
|
+
declare function withBrowserNavigationPolicy(ssrfPolicy?: SsrfPolicy): BrowserNavigationPolicyOptions;
|
|
1128
|
+
/**
|
|
1129
|
+
* Assert that a URL is allowed for browser navigation under the given SSRF policy.
|
|
1130
|
+
* Throws `InvalidBrowserNavigationUrlError` if the URL is blocked.
|
|
1131
|
+
*/
|
|
1132
|
+
declare function assertBrowserNavigationAllowed(opts: {
|
|
1133
|
+
url: string;
|
|
1134
|
+
lookupFn?: LookupFn;
|
|
1135
|
+
} & BrowserNavigationPolicyOptions): Promise<void>;
|
|
1098
1136
|
|
|
1099
|
-
export { type AriaNode, type AriaSnapshotResult, BrowserClaw, type BrowserTab, type ChromeExecutable, type ChromeKind, type ClickOptions, type ColorScheme, type ConnectOptions, type ConsoleMessage, type CookieData, CrawlPage, type DialogOptions, type DownloadResult, type FormField, type FrameEvalResult, type GeolocationOptions, type HttpCredentials, InvalidBrowserNavigationUrlError, type LaunchOptions, type NetworkRequest, type PageError, type ResponseBodyResult, type RoleRefInfo, type RoleRefs, type ScreenshotOptions, type SnapshotOptions, type SnapshotResult, type SnapshotStats, type StorageKind, type TraceStartOptions, type TypeOptions, type UntrustedContentMeta, type WaitOptions };
|
|
1137
|
+
export { type AriaNode, type AriaSnapshotResult, BrowserClaw, type BrowserNavigationPolicyOptions, type BrowserTab, type ChromeExecutable, type ChromeKind, type ClickOptions, type ColorScheme, type ConnectOptions, type ConsoleMessage, type CookieData, CrawlPage, type DialogOptions, type DownloadResult, type FormField, type FrameEvalResult, type GeolocationOptions, type HttpCredentials, InvalidBrowserNavigationUrlError, type LaunchOptions, type LookupFn, type NetworkRequest, type PageError, type ResponseBodyResult, type RoleRefInfo, type RoleRefs, type ScreenshotOptions, type SnapshotOptions, type SnapshotResult, type SnapshotStats, type SsrfPolicy, type StorageKind, type TraceStartOptions, type TypeOptions, type UntrustedContentMeta, type WaitOptions, assertBrowserNavigationAllowed, withBrowserNavigationPolicy };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as playwright_core from 'playwright-core';
|
|
2
|
+
import { lookup } from 'node:dns/promises';
|
|
2
3
|
|
|
3
4
|
interface FrameEvalResult {
|
|
4
5
|
frameUrl: string;
|
|
@@ -6,6 +7,18 @@ interface FrameEvalResult {
|
|
|
6
7
|
result: unknown;
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Policy for controlling which URLs browser navigation is allowed to reach.
|
|
12
|
+
* By default all private/internal addresses are blocked to prevent SSRF attacks.
|
|
13
|
+
*/
|
|
14
|
+
interface SsrfPolicy {
|
|
15
|
+
/** Allow navigation to private/internal network addresses. Default: `false` */
|
|
16
|
+
allowPrivateNetwork?: boolean;
|
|
17
|
+
/** Hostnames explicitly allowed even if they resolve to private addresses */
|
|
18
|
+
allowedHostnames?: string[];
|
|
19
|
+
/** Alias for allowedHostnames */
|
|
20
|
+
hostnameAllowlist?: string[];
|
|
21
|
+
}
|
|
9
22
|
/** Supported browser types that can be detected and launched. */
|
|
10
23
|
type ChromeKind = 'chrome' | 'brave' | 'edge' | 'chromium' | 'canary' | 'custom';
|
|
11
24
|
/** A detected browser executable on the system. */
|
|
@@ -33,19 +46,27 @@ interface LaunchOptions {
|
|
|
33
46
|
profileColor?: string;
|
|
34
47
|
/** Additional Chrome command-line arguments (e.g. `['--start-maximized']`). */
|
|
35
48
|
chromeArgs?: string[];
|
|
49
|
+
/**
|
|
50
|
+
* SSRF policy controlling which URLs navigation is allowed to reach.
|
|
51
|
+
* By default all private/internal addresses are blocked.
|
|
52
|
+
*/
|
|
53
|
+
ssrfPolicy?: SsrfPolicy;
|
|
36
54
|
/**
|
|
37
55
|
* Allow navigation to internal/loopback addresses (localhost, 127.x, private IPs).
|
|
38
|
-
*
|
|
39
|
-
* Set to `true` if you need to access local development servers.
|
|
56
|
+
* @deprecated Use `ssrfPolicy: { allowPrivateNetwork: true }` instead.
|
|
40
57
|
*/
|
|
41
58
|
allowInternal?: boolean;
|
|
42
59
|
}
|
|
43
60
|
/** Options for connecting to an existing browser instance. */
|
|
44
61
|
interface ConnectOptions {
|
|
62
|
+
/**
|
|
63
|
+
* SSRF policy controlling which URLs navigation is allowed to reach.
|
|
64
|
+
* By default all private/internal addresses are blocked.
|
|
65
|
+
*/
|
|
66
|
+
ssrfPolicy?: SsrfPolicy;
|
|
45
67
|
/**
|
|
46
68
|
* Allow navigation to internal/loopback addresses (localhost, 127.x, private IPs).
|
|
47
|
-
*
|
|
48
|
-
* Set to `true` if you need to access local development servers.
|
|
69
|
+
* @deprecated Use `ssrfPolicy: { allowPrivateNetwork: true }` instead.
|
|
49
70
|
*/
|
|
50
71
|
allowInternal?: boolean;
|
|
51
72
|
/**
|
|
@@ -176,7 +197,7 @@ interface ClickOptions {
|
|
|
176
197
|
/** Mouse button to use */
|
|
177
198
|
button?: 'left' | 'right' | 'middle';
|
|
178
199
|
/** Modifier keys to hold during click */
|
|
179
|
-
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[];
|
|
200
|
+
modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[];
|
|
180
201
|
/** Timeout in milliseconds. Default: `8000` */
|
|
181
202
|
timeoutMs?: number;
|
|
182
203
|
}
|
|
@@ -390,9 +411,9 @@ interface HttpCredentials {
|
|
|
390
411
|
declare class CrawlPage {
|
|
391
412
|
private readonly cdpUrl;
|
|
392
413
|
private readonly targetId;
|
|
393
|
-
private readonly
|
|
414
|
+
private readonly ssrfPolicy;
|
|
394
415
|
/** @internal */
|
|
395
|
-
constructor(cdpUrl: string, targetId: string,
|
|
416
|
+
constructor(cdpUrl: string, targetId: string, ssrfPolicy?: SsrfPolicy);
|
|
396
417
|
/** The CDP target ID for this page. Use this to identify the page in multi-tab scenarios. */
|
|
397
418
|
get id(): string;
|
|
398
419
|
/**
|
|
@@ -663,6 +684,8 @@ declare class CrawlPage {
|
|
|
663
684
|
*/
|
|
664
685
|
evaluate(fn: string, opts?: {
|
|
665
686
|
ref?: string;
|
|
687
|
+
timeoutMs?: number;
|
|
688
|
+
signal?: AbortSignal;
|
|
666
689
|
}): Promise<unknown>;
|
|
667
690
|
/**
|
|
668
691
|
* Run JavaScript in ALL frames on the page (including cross-origin iframes).
|
|
@@ -987,7 +1010,7 @@ declare class CrawlPage {
|
|
|
987
1010
|
*/
|
|
988
1011
|
declare class BrowserClaw {
|
|
989
1012
|
private readonly cdpUrl;
|
|
990
|
-
private readonly
|
|
1013
|
+
private readonly ssrfPolicy;
|
|
991
1014
|
private chrome;
|
|
992
1015
|
private constructor();
|
|
993
1016
|
/**
|
|
@@ -1087,6 +1110,7 @@ declare class BrowserClaw {
|
|
|
1087
1110
|
stop(): Promise<void>;
|
|
1088
1111
|
}
|
|
1089
1112
|
|
|
1113
|
+
type LookupFn = typeof lookup;
|
|
1090
1114
|
/**
|
|
1091
1115
|
* Thrown when a navigation URL is blocked by SSRF policy.
|
|
1092
1116
|
* Callers can catch this specifically to distinguish navigation blocks
|
|
@@ -1095,5 +1119,19 @@ declare class BrowserClaw {
|
|
|
1095
1119
|
declare class InvalidBrowserNavigationUrlError extends Error {
|
|
1096
1120
|
constructor(message: string);
|
|
1097
1121
|
}
|
|
1122
|
+
/** Options for browser navigation SSRF policy. */
|
|
1123
|
+
type BrowserNavigationPolicyOptions = {
|
|
1124
|
+
ssrfPolicy?: SsrfPolicy;
|
|
1125
|
+
};
|
|
1126
|
+
/** Build a BrowserNavigationPolicyOptions from an SsrfPolicy. */
|
|
1127
|
+
declare function withBrowserNavigationPolicy(ssrfPolicy?: SsrfPolicy): BrowserNavigationPolicyOptions;
|
|
1128
|
+
/**
|
|
1129
|
+
* Assert that a URL is allowed for browser navigation under the given SSRF policy.
|
|
1130
|
+
* Throws `InvalidBrowserNavigationUrlError` if the URL is blocked.
|
|
1131
|
+
*/
|
|
1132
|
+
declare function assertBrowserNavigationAllowed(opts: {
|
|
1133
|
+
url: string;
|
|
1134
|
+
lookupFn?: LookupFn;
|
|
1135
|
+
} & BrowserNavigationPolicyOptions): Promise<void>;
|
|
1098
1136
|
|
|
1099
|
-
export { type AriaNode, type AriaSnapshotResult, BrowserClaw, type BrowserTab, type ChromeExecutable, type ChromeKind, type ClickOptions, type ColorScheme, type ConnectOptions, type ConsoleMessage, type CookieData, CrawlPage, type DialogOptions, type DownloadResult, type FormField, type FrameEvalResult, type GeolocationOptions, type HttpCredentials, InvalidBrowserNavigationUrlError, type LaunchOptions, type NetworkRequest, type PageError, type ResponseBodyResult, type RoleRefInfo, type RoleRefs, type ScreenshotOptions, type SnapshotOptions, type SnapshotResult, type SnapshotStats, type StorageKind, type TraceStartOptions, type TypeOptions, type UntrustedContentMeta, type WaitOptions };
|
|
1137
|
+
export { type AriaNode, type AriaSnapshotResult, BrowserClaw, type BrowserNavigationPolicyOptions, type BrowserTab, type ChromeExecutable, type ChromeKind, type ClickOptions, type ColorScheme, type ConnectOptions, type ConsoleMessage, type CookieData, CrawlPage, type DialogOptions, type DownloadResult, type FormField, type FrameEvalResult, type GeolocationOptions, type HttpCredentials, InvalidBrowserNavigationUrlError, type LaunchOptions, type LookupFn, type NetworkRequest, type PageError, type ResponseBodyResult, type RoleRefInfo, type RoleRefs, type ScreenshotOptions, type SnapshotOptions, type SnapshotResult, type SnapshotStats, type SsrfPolicy, type StorageKind, type TraceStartOptions, type TypeOptions, type UntrustedContentMeta, type WaitOptions, assertBrowserNavigationAllowed, withBrowserNavigationPolicy };
|
package/dist/index.js
CHANGED
|
@@ -1384,6 +1384,32 @@ var InvalidBrowserNavigationUrlError = class extends Error {
|
|
|
1384
1384
|
this.name = "InvalidBrowserNavigationUrlError";
|
|
1385
1385
|
}
|
|
1386
1386
|
};
|
|
1387
|
+
function withBrowserNavigationPolicy(ssrfPolicy) {
|
|
1388
|
+
return { ssrfPolicy };
|
|
1389
|
+
}
|
|
1390
|
+
async function assertBrowserNavigationAllowed(opts) {
|
|
1391
|
+
const policy = opts.ssrfPolicy;
|
|
1392
|
+
if (policy?.allowPrivateNetwork) return;
|
|
1393
|
+
const allowedHostnames = [
|
|
1394
|
+
...policy?.allowedHostnames ?? [],
|
|
1395
|
+
...policy?.hostnameAllowlist ?? []
|
|
1396
|
+
];
|
|
1397
|
+
if (allowedHostnames.length) {
|
|
1398
|
+
let parsed;
|
|
1399
|
+
try {
|
|
1400
|
+
parsed = new URL(opts.url);
|
|
1401
|
+
} catch {
|
|
1402
|
+
throw new InvalidBrowserNavigationUrlError(`Invalid URL: "${opts.url}"`);
|
|
1403
|
+
}
|
|
1404
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
1405
|
+
if (allowedHostnames.some((h) => h.toLowerCase() === hostname)) return;
|
|
1406
|
+
}
|
|
1407
|
+
if (await isInternalUrlResolved(opts.url, opts.lookupFn)) {
|
|
1408
|
+
throw new InvalidBrowserNavigationUrlError(
|
|
1409
|
+
`Navigation to internal/loopback address blocked: "${opts.url}". Use ssrfPolicy: { allowPrivateNetwork: true } if this is intentional.`
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1387
1413
|
function assertSafeOutputPath(path2, allowedRoots) {
|
|
1388
1414
|
if (!path2 || typeof path2 !== "string") {
|
|
1389
1415
|
throw new Error("Output path is required.");
|
|
@@ -1506,7 +1532,7 @@ function isInternalUrl(url) {
|
|
|
1506
1532
|
}
|
|
1507
1533
|
return false;
|
|
1508
1534
|
}
|
|
1509
|
-
async function isInternalUrlResolved(url) {
|
|
1535
|
+
async function isInternalUrlResolved(url, lookupFn = lookup) {
|
|
1510
1536
|
if (isInternalUrl(url)) return true;
|
|
1511
1537
|
let parsed;
|
|
1512
1538
|
try {
|
|
@@ -1515,7 +1541,7 @@ async function isInternalUrlResolved(url) {
|
|
|
1515
1541
|
return true;
|
|
1516
1542
|
}
|
|
1517
1543
|
try {
|
|
1518
|
-
const { address } = await
|
|
1544
|
+
const { address } = await lookupFn(parsed.hostname);
|
|
1519
1545
|
if (isInternalIP(address)) return true;
|
|
1520
1546
|
} catch {
|
|
1521
1547
|
return true;
|
|
@@ -1527,9 +1553,8 @@ async function isInternalUrlResolved(url) {
|
|
|
1527
1553
|
async function navigateViaPlaywright(opts) {
|
|
1528
1554
|
const url = String(opts.url ?? "").trim();
|
|
1529
1555
|
if (!url) throw new Error("url is required");
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
}
|
|
1556
|
+
const policy = opts.allowInternal ? { ...opts.ssrfPolicy, allowPrivateNetwork: true } : opts.ssrfPolicy;
|
|
1557
|
+
await assertBrowserNavigationAllowed({ url, ssrfPolicy: policy });
|
|
1533
1558
|
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
1534
1559
|
ensurePageState(page);
|
|
1535
1560
|
await page.goto(url, { timeout: normalizeTimeoutMs(opts.timeoutMs, 2e4) });
|
|
@@ -1552,8 +1577,9 @@ async function listPagesViaPlaywright(opts) {
|
|
|
1552
1577
|
}
|
|
1553
1578
|
async function createPageViaPlaywright(opts) {
|
|
1554
1579
|
const targetUrl = (opts.url ?? "").trim() || "about:blank";
|
|
1555
|
-
if (targetUrl !== "about:blank"
|
|
1556
|
-
|
|
1580
|
+
if (targetUrl !== "about:blank") {
|
|
1581
|
+
const policy = opts.allowInternal ? { ...opts.ssrfPolicy, allowPrivateNetwork: true } : opts.ssrfPolicy;
|
|
1582
|
+
await assertBrowserNavigationAllowed({ url: targetUrl, ssrfPolicy: policy });
|
|
1557
1583
|
}
|
|
1558
1584
|
const { browser } = await connectBrowser(opts.cdpUrl);
|
|
1559
1585
|
const context = browser.contexts()[0] ?? await browser.newContext();
|
|
@@ -1673,6 +1699,7 @@ async function evaluateViaPlaywright(opts) {
|
|
|
1673
1699
|
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
1674
1700
|
ensurePageState(page);
|
|
1675
1701
|
restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
|
|
1702
|
+
const timeout = opts.timeoutMs != null ? opts.timeoutMs : void 0;
|
|
1676
1703
|
if (opts.ref) {
|
|
1677
1704
|
const locator = refLocator(page, opts.ref);
|
|
1678
1705
|
return await locator.evaluate(
|
|
@@ -1685,10 +1712,11 @@ async function evaluateViaPlaywright(opts) {
|
|
|
1685
1712
|
throw new Error("Invalid evaluate function: " + (err instanceof Error ? err.message : String(err)));
|
|
1686
1713
|
}
|
|
1687
1714
|
},
|
|
1688
|
-
fnText
|
|
1715
|
+
fnText,
|
|
1716
|
+
{ timeout }
|
|
1689
1717
|
);
|
|
1690
1718
|
}
|
|
1691
|
-
|
|
1719
|
+
const evalPromise = page.evaluate(
|
|
1692
1720
|
// eslint-disable-next-line no-eval
|
|
1693
1721
|
(fnBody) => {
|
|
1694
1722
|
try {
|
|
@@ -1700,6 +1728,13 @@ async function evaluateViaPlaywright(opts) {
|
|
|
1700
1728
|
},
|
|
1701
1729
|
fnText
|
|
1702
1730
|
);
|
|
1731
|
+
if (!opts.signal) return evalPromise;
|
|
1732
|
+
return Promise.race([
|
|
1733
|
+
evalPromise,
|
|
1734
|
+
new Promise((_, reject) => {
|
|
1735
|
+
opts.signal.addEventListener("abort", () => reject(new Error("Evaluate aborted")), { once: true });
|
|
1736
|
+
})
|
|
1737
|
+
]);
|
|
1703
1738
|
}
|
|
1704
1739
|
|
|
1705
1740
|
// src/actions/download.ts
|
|
@@ -2061,12 +2096,12 @@ async function storageClearViaPlaywright(opts) {
|
|
|
2061
2096
|
var CrawlPage = class {
|
|
2062
2097
|
cdpUrl;
|
|
2063
2098
|
targetId;
|
|
2064
|
-
|
|
2099
|
+
ssrfPolicy;
|
|
2065
2100
|
/** @internal */
|
|
2066
|
-
constructor(cdpUrl, targetId,
|
|
2101
|
+
constructor(cdpUrl, targetId, ssrfPolicy) {
|
|
2067
2102
|
this.cdpUrl = cdpUrl;
|
|
2068
2103
|
this.targetId = targetId;
|
|
2069
|
-
this.
|
|
2104
|
+
this.ssrfPolicy = ssrfPolicy;
|
|
2070
2105
|
}
|
|
2071
2106
|
/** The CDP target ID for this page. Use this to identify the page in multi-tab scenarios. */
|
|
2072
2107
|
get id() {
|
|
@@ -2399,7 +2434,7 @@ var CrawlPage = class {
|
|
|
2399
2434
|
targetId: this.targetId,
|
|
2400
2435
|
url,
|
|
2401
2436
|
timeoutMs: opts?.timeoutMs,
|
|
2402
|
-
|
|
2437
|
+
ssrfPolicy: this.ssrfPolicy
|
|
2403
2438
|
});
|
|
2404
2439
|
}
|
|
2405
2440
|
/**
|
|
@@ -2478,7 +2513,9 @@ var CrawlPage = class {
|
|
|
2478
2513
|
cdpUrl: this.cdpUrl,
|
|
2479
2514
|
targetId: this.targetId,
|
|
2480
2515
|
fn,
|
|
2481
|
-
ref: opts?.ref
|
|
2516
|
+
ref: opts?.ref,
|
|
2517
|
+
timeoutMs: opts?.timeoutMs,
|
|
2518
|
+
signal: opts?.signal
|
|
2482
2519
|
});
|
|
2483
2520
|
}
|
|
2484
2521
|
/**
|
|
@@ -2929,12 +2966,12 @@ var CrawlPage = class {
|
|
|
2929
2966
|
};
|
|
2930
2967
|
var BrowserClaw = class _BrowserClaw {
|
|
2931
2968
|
cdpUrl;
|
|
2932
|
-
|
|
2969
|
+
ssrfPolicy;
|
|
2933
2970
|
chrome;
|
|
2934
|
-
constructor(cdpUrl, chrome,
|
|
2971
|
+
constructor(cdpUrl, chrome, ssrfPolicy) {
|
|
2935
2972
|
this.cdpUrl = cdpUrl;
|
|
2936
2973
|
this.chrome = chrome;
|
|
2937
|
-
this.
|
|
2974
|
+
this.ssrfPolicy = ssrfPolicy;
|
|
2938
2975
|
}
|
|
2939
2976
|
/**
|
|
2940
2977
|
* Launch a new Chrome instance and connect to it.
|
|
@@ -2962,7 +2999,8 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
2962
2999
|
static async launch(opts = {}) {
|
|
2963
3000
|
const chrome = await launchChrome(opts);
|
|
2964
3001
|
const cdpUrl = `http://127.0.0.1:${chrome.cdpPort}`;
|
|
2965
|
-
|
|
3002
|
+
const ssrfPolicy = opts.allowInternal ? { ...opts.ssrfPolicy, allowPrivateNetwork: true } : opts.ssrfPolicy;
|
|
3003
|
+
return new _BrowserClaw(cdpUrl, chrome, ssrfPolicy);
|
|
2966
3004
|
}
|
|
2967
3005
|
/**
|
|
2968
3006
|
* Connect to an already-running Chrome instance via its CDP endpoint.
|
|
@@ -2983,7 +3021,8 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
2983
3021
|
throw new Error(`Cannot connect to Chrome at ${cdpUrl}. Is Chrome running with --remote-debugging-port?`);
|
|
2984
3022
|
}
|
|
2985
3023
|
await connectBrowser(cdpUrl, opts?.authToken);
|
|
2986
|
-
|
|
3024
|
+
const ssrfPolicy = opts?.allowInternal ? { ...opts.ssrfPolicy, allowPrivateNetwork: true } : opts?.ssrfPolicy;
|
|
3025
|
+
return new _BrowserClaw(cdpUrl, null, ssrfPolicy);
|
|
2987
3026
|
}
|
|
2988
3027
|
/**
|
|
2989
3028
|
* Open a URL in a new tab and return the page handle.
|
|
@@ -2998,8 +3037,8 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
2998
3037
|
* ```
|
|
2999
3038
|
*/
|
|
3000
3039
|
async open(url) {
|
|
3001
|
-
const tab = await createPageViaPlaywright({ cdpUrl: this.cdpUrl, url,
|
|
3002
|
-
return new CrawlPage(this.cdpUrl, tab.targetId, this.
|
|
3040
|
+
const tab = await createPageViaPlaywright({ cdpUrl: this.cdpUrl, url, ssrfPolicy: this.ssrfPolicy });
|
|
3041
|
+
return new CrawlPage(this.cdpUrl, tab.targetId, this.ssrfPolicy);
|
|
3003
3042
|
}
|
|
3004
3043
|
/**
|
|
3005
3044
|
* Get a CrawlPage handle for the currently active tab.
|
|
@@ -3012,7 +3051,7 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
3012
3051
|
if (!pages.length) throw new Error("No pages available. Use browser.open(url) to create a tab.");
|
|
3013
3052
|
const tid = await pageTargetId(pages[0]).catch(() => null);
|
|
3014
3053
|
if (!tid) throw new Error("Failed to get targetId for the current page.");
|
|
3015
|
-
return new CrawlPage(this.cdpUrl, tid, this.
|
|
3054
|
+
return new CrawlPage(this.cdpUrl, tid, this.ssrfPolicy);
|
|
3016
3055
|
}
|
|
3017
3056
|
/**
|
|
3018
3057
|
* List all open tabs.
|
|
@@ -3047,7 +3086,7 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
3047
3086
|
* @returns CrawlPage for the specified tab
|
|
3048
3087
|
*/
|
|
3049
3088
|
page(targetId) {
|
|
3050
|
-
return new CrawlPage(this.cdpUrl, targetId, this.
|
|
3089
|
+
return new CrawlPage(this.cdpUrl, targetId, this.ssrfPolicy);
|
|
3051
3090
|
}
|
|
3052
3091
|
/** The CDP endpoint URL for this browser connection. */
|
|
3053
3092
|
get url() {
|
|
@@ -3069,6 +3108,6 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
3069
3108
|
}
|
|
3070
3109
|
};
|
|
3071
3110
|
|
|
3072
|
-
export { BrowserClaw, CrawlPage, InvalidBrowserNavigationUrlError };
|
|
3111
|
+
export { BrowserClaw, CrawlPage, InvalidBrowserNavigationUrlError, assertBrowserNavigationAllowed, withBrowserNavigationPolicy };
|
|
3073
3112
|
//# sourceMappingURL=index.js.map
|
|
3074
3113
|
//# sourceMappingURL=index.js.map
|