browserclaw 0.2.7 → 0.2.9
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 +54 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -8
- package/dist/index.d.ts +43 -8
- package/dist/index.js +53 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1387,6 +1387,38 @@ async function pressKeyViaPlaywright(opts) {
|
|
|
1387
1387
|
ensurePageState(page);
|
|
1388
1388
|
await page.keyboard.press(key, { delay: Math.max(0, Math.floor(opts.delayMs ?? 0)) });
|
|
1389
1389
|
}
|
|
1390
|
+
var InvalidBrowserNavigationUrlError = class extends Error {
|
|
1391
|
+
constructor(message) {
|
|
1392
|
+
super(message);
|
|
1393
|
+
this.name = "InvalidBrowserNavigationUrlError";
|
|
1394
|
+
}
|
|
1395
|
+
};
|
|
1396
|
+
function withBrowserNavigationPolicy(ssrfPolicy) {
|
|
1397
|
+
return { ssrfPolicy };
|
|
1398
|
+
}
|
|
1399
|
+
async function assertBrowserNavigationAllowed(opts) {
|
|
1400
|
+
const policy = opts.ssrfPolicy;
|
|
1401
|
+
if (policy?.allowPrivateNetwork) return;
|
|
1402
|
+
const allowedHostnames = [
|
|
1403
|
+
...policy?.allowedHostnames ?? [],
|
|
1404
|
+
...policy?.hostnameAllowlist ?? []
|
|
1405
|
+
];
|
|
1406
|
+
if (allowedHostnames.length) {
|
|
1407
|
+
let parsed;
|
|
1408
|
+
try {
|
|
1409
|
+
parsed = new URL(opts.url);
|
|
1410
|
+
} catch {
|
|
1411
|
+
throw new InvalidBrowserNavigationUrlError(`Invalid URL: "${opts.url}"`);
|
|
1412
|
+
}
|
|
1413
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
1414
|
+
if (allowedHostnames.some((h) => h.toLowerCase() === hostname)) return;
|
|
1415
|
+
}
|
|
1416
|
+
if (await isInternalUrlResolved(opts.url)) {
|
|
1417
|
+
throw new InvalidBrowserNavigationUrlError(
|
|
1418
|
+
`Navigation to internal/loopback address blocked: "${opts.url}". Use ssrfPolicy: { allowPrivateNetwork: true } if this is intentional.`
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1390
1422
|
function assertSafeOutputPath(path2, allowedRoots) {
|
|
1391
1423
|
if (!path2 || typeof path2 !== "string") {
|
|
1392
1424
|
throw new Error("Output path is required.");
|
|
@@ -1530,9 +1562,8 @@ async function isInternalUrlResolved(url) {
|
|
|
1530
1562
|
async function navigateViaPlaywright(opts) {
|
|
1531
1563
|
const url = String(opts.url ?? "").trim();
|
|
1532
1564
|
if (!url) throw new Error("url is required");
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
}
|
|
1565
|
+
const policy = opts.allowInternal ? { ...opts.ssrfPolicy, allowPrivateNetwork: true } : opts.ssrfPolicy;
|
|
1566
|
+
await assertBrowserNavigationAllowed({ url, ssrfPolicy: policy });
|
|
1536
1567
|
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
1537
1568
|
ensurePageState(page);
|
|
1538
1569
|
await page.goto(url, { timeout: normalizeTimeoutMs(opts.timeoutMs, 2e4) });
|
|
@@ -1555,8 +1586,9 @@ async function listPagesViaPlaywright(opts) {
|
|
|
1555
1586
|
}
|
|
1556
1587
|
async function createPageViaPlaywright(opts) {
|
|
1557
1588
|
const targetUrl = (opts.url ?? "").trim() || "about:blank";
|
|
1558
|
-
if (targetUrl !== "about:blank"
|
|
1559
|
-
|
|
1589
|
+
if (targetUrl !== "about:blank") {
|
|
1590
|
+
const policy = opts.allowInternal ? { ...opts.ssrfPolicy, allowPrivateNetwork: true } : opts.ssrfPolicy;
|
|
1591
|
+
await assertBrowserNavigationAllowed({ url: targetUrl, ssrfPolicy: policy });
|
|
1560
1592
|
}
|
|
1561
1593
|
const { browser } = await connectBrowser(opts.cdpUrl);
|
|
1562
1594
|
const context = browser.contexts()[0] ?? await browser.newContext();
|
|
@@ -2064,12 +2096,12 @@ async function storageClearViaPlaywright(opts) {
|
|
|
2064
2096
|
var CrawlPage = class {
|
|
2065
2097
|
cdpUrl;
|
|
2066
2098
|
targetId;
|
|
2067
|
-
|
|
2099
|
+
ssrfPolicy;
|
|
2068
2100
|
/** @internal */
|
|
2069
|
-
constructor(cdpUrl, targetId,
|
|
2101
|
+
constructor(cdpUrl, targetId, ssrfPolicy) {
|
|
2070
2102
|
this.cdpUrl = cdpUrl;
|
|
2071
2103
|
this.targetId = targetId;
|
|
2072
|
-
this.
|
|
2104
|
+
this.ssrfPolicy = ssrfPolicy;
|
|
2073
2105
|
}
|
|
2074
2106
|
/** The CDP target ID for this page. Use this to identify the page in multi-tab scenarios. */
|
|
2075
2107
|
get id() {
|
|
@@ -2402,7 +2434,7 @@ var CrawlPage = class {
|
|
|
2402
2434
|
targetId: this.targetId,
|
|
2403
2435
|
url,
|
|
2404
2436
|
timeoutMs: opts?.timeoutMs,
|
|
2405
|
-
|
|
2437
|
+
ssrfPolicy: this.ssrfPolicy
|
|
2406
2438
|
});
|
|
2407
2439
|
}
|
|
2408
2440
|
/**
|
|
@@ -2932,12 +2964,12 @@ var CrawlPage = class {
|
|
|
2932
2964
|
};
|
|
2933
2965
|
var BrowserClaw = class _BrowserClaw {
|
|
2934
2966
|
cdpUrl;
|
|
2935
|
-
|
|
2967
|
+
ssrfPolicy;
|
|
2936
2968
|
chrome;
|
|
2937
|
-
constructor(cdpUrl, chrome,
|
|
2969
|
+
constructor(cdpUrl, chrome, ssrfPolicy) {
|
|
2938
2970
|
this.cdpUrl = cdpUrl;
|
|
2939
2971
|
this.chrome = chrome;
|
|
2940
|
-
this.
|
|
2972
|
+
this.ssrfPolicy = ssrfPolicy;
|
|
2941
2973
|
}
|
|
2942
2974
|
/**
|
|
2943
2975
|
* Launch a new Chrome instance and connect to it.
|
|
@@ -2965,7 +2997,8 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
2965
2997
|
static async launch(opts = {}) {
|
|
2966
2998
|
const chrome = await launchChrome(opts);
|
|
2967
2999
|
const cdpUrl = `http://127.0.0.1:${chrome.cdpPort}`;
|
|
2968
|
-
|
|
3000
|
+
const ssrfPolicy = opts.allowInternal ? { ...opts.ssrfPolicy, allowPrivateNetwork: true } : opts.ssrfPolicy;
|
|
3001
|
+
return new _BrowserClaw(cdpUrl, chrome, ssrfPolicy);
|
|
2969
3002
|
}
|
|
2970
3003
|
/**
|
|
2971
3004
|
* Connect to an already-running Chrome instance via its CDP endpoint.
|
|
@@ -2986,7 +3019,8 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
2986
3019
|
throw new Error(`Cannot connect to Chrome at ${cdpUrl}. Is Chrome running with --remote-debugging-port?`);
|
|
2987
3020
|
}
|
|
2988
3021
|
await connectBrowser(cdpUrl, opts?.authToken);
|
|
2989
|
-
|
|
3022
|
+
const ssrfPolicy = opts?.allowInternal ? { ...opts.ssrfPolicy, allowPrivateNetwork: true } : opts?.ssrfPolicy;
|
|
3023
|
+
return new _BrowserClaw(cdpUrl, null, ssrfPolicy);
|
|
2990
3024
|
}
|
|
2991
3025
|
/**
|
|
2992
3026
|
* Open a URL in a new tab and return the page handle.
|
|
@@ -3001,8 +3035,8 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
3001
3035
|
* ```
|
|
3002
3036
|
*/
|
|
3003
3037
|
async open(url) {
|
|
3004
|
-
const tab = await createPageViaPlaywright({ cdpUrl: this.cdpUrl, url,
|
|
3005
|
-
return new CrawlPage(this.cdpUrl, tab.targetId, this.
|
|
3038
|
+
const tab = await createPageViaPlaywright({ cdpUrl: this.cdpUrl, url, ssrfPolicy: this.ssrfPolicy });
|
|
3039
|
+
return new CrawlPage(this.cdpUrl, tab.targetId, this.ssrfPolicy);
|
|
3006
3040
|
}
|
|
3007
3041
|
/**
|
|
3008
3042
|
* Get a CrawlPage handle for the currently active tab.
|
|
@@ -3015,7 +3049,7 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
3015
3049
|
if (!pages.length) throw new Error("No pages available. Use browser.open(url) to create a tab.");
|
|
3016
3050
|
const tid = await pageTargetId(pages[0]).catch(() => null);
|
|
3017
3051
|
if (!tid) throw new Error("Failed to get targetId for the current page.");
|
|
3018
|
-
return new CrawlPage(this.cdpUrl, tid, this.
|
|
3052
|
+
return new CrawlPage(this.cdpUrl, tid, this.ssrfPolicy);
|
|
3019
3053
|
}
|
|
3020
3054
|
/**
|
|
3021
3055
|
* List all open tabs.
|
|
@@ -3050,7 +3084,7 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
3050
3084
|
* @returns CrawlPage for the specified tab
|
|
3051
3085
|
*/
|
|
3052
3086
|
page(targetId) {
|
|
3053
|
-
return new CrawlPage(this.cdpUrl, targetId, this.
|
|
3087
|
+
return new CrawlPage(this.cdpUrl, targetId, this.ssrfPolicy);
|
|
3054
3088
|
}
|
|
3055
3089
|
/** The CDP endpoint URL for this browser connection. */
|
|
3056
3090
|
get url() {
|
|
@@ -3074,5 +3108,7 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
3074
3108
|
|
|
3075
3109
|
exports.BrowserClaw = BrowserClaw;
|
|
3076
3110
|
exports.CrawlPage = CrawlPage;
|
|
3111
|
+
exports.InvalidBrowserNavigationUrlError = InvalidBrowserNavigationUrlError;
|
|
3112
|
+
exports.withBrowserNavigationPolicy = withBrowserNavigationPolicy;
|
|
3077
3113
|
//# sourceMappingURL=index.cjs.map
|
|
3078
3114
|
//# sourceMappingURL=index.cjs.map
|