browserclaw 0.2.8 → 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 +47 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -8
- package/dist/index.d.ts +34 -8
- package/dist/index.js +47 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1393,6 +1393,32 @@ var InvalidBrowserNavigationUrlError = class extends Error {
|
|
|
1393
1393
|
this.name = "InvalidBrowserNavigationUrlError";
|
|
1394
1394
|
}
|
|
1395
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
|
+
}
|
|
1396
1422
|
function assertSafeOutputPath(path2, allowedRoots) {
|
|
1397
1423
|
if (!path2 || typeof path2 !== "string") {
|
|
1398
1424
|
throw new Error("Output path is required.");
|
|
@@ -1536,9 +1562,8 @@ async function isInternalUrlResolved(url) {
|
|
|
1536
1562
|
async function navigateViaPlaywright(opts) {
|
|
1537
1563
|
const url = String(opts.url ?? "").trim();
|
|
1538
1564
|
if (!url) throw new Error("url is required");
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
}
|
|
1565
|
+
const policy = opts.allowInternal ? { ...opts.ssrfPolicy, allowPrivateNetwork: true } : opts.ssrfPolicy;
|
|
1566
|
+
await assertBrowserNavigationAllowed({ url, ssrfPolicy: policy });
|
|
1542
1567
|
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
1543
1568
|
ensurePageState(page);
|
|
1544
1569
|
await page.goto(url, { timeout: normalizeTimeoutMs(opts.timeoutMs, 2e4) });
|
|
@@ -1561,8 +1586,9 @@ async function listPagesViaPlaywright(opts) {
|
|
|
1561
1586
|
}
|
|
1562
1587
|
async function createPageViaPlaywright(opts) {
|
|
1563
1588
|
const targetUrl = (opts.url ?? "").trim() || "about:blank";
|
|
1564
|
-
if (targetUrl !== "about:blank"
|
|
1565
|
-
|
|
1589
|
+
if (targetUrl !== "about:blank") {
|
|
1590
|
+
const policy = opts.allowInternal ? { ...opts.ssrfPolicy, allowPrivateNetwork: true } : opts.ssrfPolicy;
|
|
1591
|
+
await assertBrowserNavigationAllowed({ url: targetUrl, ssrfPolicy: policy });
|
|
1566
1592
|
}
|
|
1567
1593
|
const { browser } = await connectBrowser(opts.cdpUrl);
|
|
1568
1594
|
const context = browser.contexts()[0] ?? await browser.newContext();
|
|
@@ -2070,12 +2096,12 @@ async function storageClearViaPlaywright(opts) {
|
|
|
2070
2096
|
var CrawlPage = class {
|
|
2071
2097
|
cdpUrl;
|
|
2072
2098
|
targetId;
|
|
2073
|
-
|
|
2099
|
+
ssrfPolicy;
|
|
2074
2100
|
/** @internal */
|
|
2075
|
-
constructor(cdpUrl, targetId,
|
|
2101
|
+
constructor(cdpUrl, targetId, ssrfPolicy) {
|
|
2076
2102
|
this.cdpUrl = cdpUrl;
|
|
2077
2103
|
this.targetId = targetId;
|
|
2078
|
-
this.
|
|
2104
|
+
this.ssrfPolicy = ssrfPolicy;
|
|
2079
2105
|
}
|
|
2080
2106
|
/** The CDP target ID for this page. Use this to identify the page in multi-tab scenarios. */
|
|
2081
2107
|
get id() {
|
|
@@ -2408,7 +2434,7 @@ var CrawlPage = class {
|
|
|
2408
2434
|
targetId: this.targetId,
|
|
2409
2435
|
url,
|
|
2410
2436
|
timeoutMs: opts?.timeoutMs,
|
|
2411
|
-
|
|
2437
|
+
ssrfPolicy: this.ssrfPolicy
|
|
2412
2438
|
});
|
|
2413
2439
|
}
|
|
2414
2440
|
/**
|
|
@@ -2938,12 +2964,12 @@ var CrawlPage = class {
|
|
|
2938
2964
|
};
|
|
2939
2965
|
var BrowserClaw = class _BrowserClaw {
|
|
2940
2966
|
cdpUrl;
|
|
2941
|
-
|
|
2967
|
+
ssrfPolicy;
|
|
2942
2968
|
chrome;
|
|
2943
|
-
constructor(cdpUrl, chrome,
|
|
2969
|
+
constructor(cdpUrl, chrome, ssrfPolicy) {
|
|
2944
2970
|
this.cdpUrl = cdpUrl;
|
|
2945
2971
|
this.chrome = chrome;
|
|
2946
|
-
this.
|
|
2972
|
+
this.ssrfPolicy = ssrfPolicy;
|
|
2947
2973
|
}
|
|
2948
2974
|
/**
|
|
2949
2975
|
* Launch a new Chrome instance and connect to it.
|
|
@@ -2971,7 +2997,8 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
2971
2997
|
static async launch(opts = {}) {
|
|
2972
2998
|
const chrome = await launchChrome(opts);
|
|
2973
2999
|
const cdpUrl = `http://127.0.0.1:${chrome.cdpPort}`;
|
|
2974
|
-
|
|
3000
|
+
const ssrfPolicy = opts.allowInternal ? { ...opts.ssrfPolicy, allowPrivateNetwork: true } : opts.ssrfPolicy;
|
|
3001
|
+
return new _BrowserClaw(cdpUrl, chrome, ssrfPolicy);
|
|
2975
3002
|
}
|
|
2976
3003
|
/**
|
|
2977
3004
|
* Connect to an already-running Chrome instance via its CDP endpoint.
|
|
@@ -2992,7 +3019,8 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
2992
3019
|
throw new Error(`Cannot connect to Chrome at ${cdpUrl}. Is Chrome running with --remote-debugging-port?`);
|
|
2993
3020
|
}
|
|
2994
3021
|
await connectBrowser(cdpUrl, opts?.authToken);
|
|
2995
|
-
|
|
3022
|
+
const ssrfPolicy = opts?.allowInternal ? { ...opts.ssrfPolicy, allowPrivateNetwork: true } : opts?.ssrfPolicy;
|
|
3023
|
+
return new _BrowserClaw(cdpUrl, null, ssrfPolicy);
|
|
2996
3024
|
}
|
|
2997
3025
|
/**
|
|
2998
3026
|
* Open a URL in a new tab and return the page handle.
|
|
@@ -3007,8 +3035,8 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
3007
3035
|
* ```
|
|
3008
3036
|
*/
|
|
3009
3037
|
async open(url) {
|
|
3010
|
-
const tab = await createPageViaPlaywright({ cdpUrl: this.cdpUrl, url,
|
|
3011
|
-
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);
|
|
3012
3040
|
}
|
|
3013
3041
|
/**
|
|
3014
3042
|
* Get a CrawlPage handle for the currently active tab.
|
|
@@ -3021,7 +3049,7 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
3021
3049
|
if (!pages.length) throw new Error("No pages available. Use browser.open(url) to create a tab.");
|
|
3022
3050
|
const tid = await pageTargetId(pages[0]).catch(() => null);
|
|
3023
3051
|
if (!tid) throw new Error("Failed to get targetId for the current page.");
|
|
3024
|
-
return new CrawlPage(this.cdpUrl, tid, this.
|
|
3052
|
+
return new CrawlPage(this.cdpUrl, tid, this.ssrfPolicy);
|
|
3025
3053
|
}
|
|
3026
3054
|
/**
|
|
3027
3055
|
* List all open tabs.
|
|
@@ -3056,7 +3084,7 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
3056
3084
|
* @returns CrawlPage for the specified tab
|
|
3057
3085
|
*/
|
|
3058
3086
|
page(targetId) {
|
|
3059
|
-
return new CrawlPage(this.cdpUrl, targetId, this.
|
|
3087
|
+
return new CrawlPage(this.cdpUrl, targetId, this.ssrfPolicy);
|
|
3060
3088
|
}
|
|
3061
3089
|
/** The CDP endpoint URL for this browser connection. */
|
|
3062
3090
|
get url() {
|
|
@@ -3081,5 +3109,6 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
3081
3109
|
exports.BrowserClaw = BrowserClaw;
|
|
3082
3110
|
exports.CrawlPage = CrawlPage;
|
|
3083
3111
|
exports.InvalidBrowserNavigationUrlError = InvalidBrowserNavigationUrlError;
|
|
3112
|
+
exports.withBrowserNavigationPolicy = withBrowserNavigationPolicy;
|
|
3084
3113
|
//# sourceMappingURL=index.cjs.map
|
|
3085
3114
|
//# sourceMappingURL=index.cjs.map
|