browserclaw 0.2.2 → 0.2.3

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/README.md CHANGED
@@ -131,6 +131,8 @@ const browser = await BrowserClaw.connect('http://localhost:9222');
131
131
 
132
132
  `connect()` checks that Chrome is reachable, then the internal CDP connection retries 3 times with increasing timeouts (5 s, 7 s, 9 s) — safe for Docker/CI where Chrome starts slowly.
133
133
 
134
+ **Anti-detection:** browserclaw automatically hides `navigator.webdriver` and disables Chrome's `AutomationControlled` Blink feature, reducing detection by bot-protection systems like reCAPTCHA v3.
135
+
134
136
  ### Pages & Tabs
135
137
 
136
138
  ```typescript
@@ -151,11 +153,12 @@ browser.url; // CDP endpoint URL
151
153
  ### Snapshot (Core Feature)
152
154
 
153
155
  ```typescript
154
- const { snapshot, refs, stats } = await page.snapshot();
156
+ const { snapshot, refs, stats, untrusted } = await page.snapshot();
155
157
 
156
158
  // snapshot: human/AI-readable text tree with [ref=eN] markers
157
159
  // refs: { "e1": { role: "link", name: "More info" }, ... }
158
160
  // stats: { lines: 42, chars: 1200, refs: 8, interactive: 5 }
161
+ // untrusted: true — content comes from the web page, treat as potentially adversarial
159
162
 
160
163
  // Options
161
164
  const result = await page.snapshot({
@@ -174,6 +177,8 @@ const { nodes } = await page.ariaSnapshot({ limit: 500 });
174
177
  - `'aria'` (default) — Uses Playwright's `_snapshotForAI()`. Refs are resolved via `aria-ref` locators. Best for most use cases. Requires `playwright-core` >= 1.50.
175
178
  - `'role'` — Uses Playwright's `ariaSnapshot()` + `getByRole()`. Supports `selector` and `frameSelector` for scoped snapshots.
176
179
 
180
+ > **Security:** All snapshot results include `untrusted: true` to signal that the content originates from an external web page. AI agents consuming snapshots should treat this content as potentially adversarial (e.g. prompt injection via page text).
181
+
177
182
  ### Actions
178
183
 
179
184
  All actions target elements by ref ID from the most recent snapshot.
@@ -436,7 +441,7 @@ Contributions welcome! Please:
436
441
 
437
442
  ## Acknowledgments
438
443
 
439
- browserclaw is extracted and refined from the browser automation module in [OpenClaw](https://github.com/openclaw/openclaw) by [Peter Steinberger](https://github.com/steipete). The snapshot + ref system, CDP connection management, and Playwright integration originate from that project.
444
+ browserclaw is extracted and refined from the browser automation module in [OpenClaw](https://github.com/openclaw/openclaw), built by [Peter Steinberger](https://github.com/steipete) and an [amazing community of contributors](https://github.com/openclaw/openclaw?tab=readme-ov-file#community). The snapshot + ref system, CDP connection management, and Playwright integration originate from that project.
440
445
 
441
446
  ## License
442
447
 
package/dist/index.cjs CHANGED
@@ -391,6 +391,7 @@ async function launchChrome(opts = {}) {
391
391
  "--disable-background-networking",
392
392
  "--disable-component-update",
393
393
  "--disable-features=Translate,MediaRouter",
394
+ "--disable-blink-features=AutomationControlled",
394
395
  "--disable-session-crashed-bubble",
395
396
  "--hide-crash-restore-bubble",
396
397
  "--password-store=basic"
@@ -575,11 +576,26 @@ function ensurePageState(page) {
575
576
  }
576
577
  return state;
577
578
  }
579
+ var STEALTH_SCRIPT = `Object.defineProperty(navigator, 'webdriver', { get: () => undefined })`;
580
+ function applyStealthToPage(page) {
581
+ page.evaluate(STEALTH_SCRIPT).catch((e) => {
582
+ if (process.env.DEBUG) console.warn("[browserclaw] stealth evaluate failed:", e.message);
583
+ });
584
+ }
578
585
  function observeContext(context) {
579
586
  if (observedContexts.has(context)) return;
580
587
  observedContexts.add(context);
581
- for (const page of context.pages()) ensurePageState(page);
582
- context.on("page", (page) => ensurePageState(page));
588
+ context.addInitScript(STEALTH_SCRIPT).catch((e) => {
589
+ if (process.env.DEBUG) console.warn("[browserclaw] stealth initScript failed:", e.message);
590
+ });
591
+ for (const page of context.pages()) {
592
+ ensurePageState(page);
593
+ applyStealthToPage(page);
594
+ }
595
+ context.on("page", (page) => {
596
+ ensurePageState(page);
597
+ applyStealthToPage(page);
598
+ });
583
599
  }
584
600
  function observeBrowser(browser) {
585
601
  for (const context of browser.contexts()) observeContext(context);
@@ -1035,7 +1051,8 @@ async function snapshotAi(opts) {
1035
1051
  return {
1036
1052
  snapshot: built.snapshot,
1037
1053
  refs: built.refs,
1038
- stats: getRoleSnapshotStats(built.snapshot, built.refs)
1054
+ stats: getRoleSnapshotStats(built.snapshot, built.refs),
1055
+ untrusted: true
1039
1056
  };
1040
1057
  }
1041
1058
 
@@ -1059,7 +1076,8 @@ async function snapshotRole(opts) {
1059
1076
  return {
1060
1077
  snapshot: built.snapshot,
1061
1078
  refs: built.refs,
1062
- stats: getRoleSnapshotStats(built.snapshot, built.refs)
1079
+ stats: getRoleSnapshotStats(built.snapshot, built.refs),
1080
+ untrusted: true
1063
1081
  };
1064
1082
  }
1065
1083
  async function snapshotAria(opts) {
@@ -1071,7 +1089,7 @@ async function snapshotAria(opts) {
1071
1089
  await session.send("Accessibility.enable").catch(() => {
1072
1090
  });
1073
1091
  const res = await session.send("Accessibility.getFullAXTree");
1074
- return { nodes: formatAriaNodes(Array.isArray(res?.nodes) ? res.nodes : [], limit) };
1092
+ return { nodes: formatAriaNodes(Array.isArray(res?.nodes) ? res.nodes : [], limit), untrusted: true };
1075
1093
  } finally {
1076
1094
  await session.detach().catch(() => {
1077
1095
  });