@weavetab/mcp 2.5.0-beta.2 → 2.5.0-beta.5

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.
Files changed (127) hide show
  1. package/AGENTS.md +1 -1
  2. package/ARCHITECTURE.md +6 -6
  3. package/LICENSE +216 -631
  4. package/README.md +153 -1074
  5. package/SECURITY.md +33 -22
  6. package/TROUBLESHOOTING.md +2 -2
  7. package/dist/cdp/bridge.js +2 -1
  8. package/dist/cdp/connector.d.ts +2 -0
  9. package/dist/cdp/connector.js +10 -6
  10. package/dist/cdp/helpers.d.ts +4 -0
  11. package/dist/cdp/helpers.js +181 -0
  12. package/dist/cdp/omni_broker.d.ts +1 -1
  13. package/dist/cdp/omni_broker.js +1 -1
  14. package/dist/cdp/walker.d.ts +25 -1
  15. package/dist/cdp/walker.js +304 -7
  16. package/dist/cli/plugin.d.ts +1 -0
  17. package/dist/cli/plugin.js +81 -18
  18. package/dist/cli/updater.d.ts +1 -1
  19. package/dist/cli/updater.js +27 -7
  20. package/dist/config/loader.d.ts +9 -1
  21. package/dist/config/loader.js +42 -3
  22. package/dist/desktop/discovery.d.ts +44 -0
  23. package/dist/desktop/discovery.js +364 -0
  24. package/dist/index.js +137 -21
  25. package/dist/intelligence/marks.d.ts +36 -0
  26. package/dist/intelligence/marks.js +30 -0
  27. package/dist/intelligence/patternCompiler.d.ts +118 -0
  28. package/dist/intelligence/patternCompiler.js +259 -0
  29. package/dist/intelligence/sessionMemory.d.ts +2 -0
  30. package/dist/intelligence/sessionMemory.js +18 -8
  31. package/dist/overlay/injector.js +2 -2
  32. package/dist/plugin/interface.d.ts +88 -5
  33. package/dist/plugin/loader.d.ts +12 -1
  34. package/dist/plugin/loader.js +280 -70
  35. package/dist/plugin/pipeline.d.ts +75 -0
  36. package/dist/plugin/pipeline.js +183 -0
  37. package/dist/plugin/registry.d.ts +2 -1
  38. package/dist/plugin/registry.js +24 -14
  39. package/dist/security/secrets.d.ts +7 -0
  40. package/dist/security/secrets.js +37 -0
  41. package/dist/sensors/actionability.js +1 -2
  42. package/dist/sensors/guard.js +4 -10
  43. package/dist/sensors/mutations.js +12 -0
  44. package/dist/sensors/operation_monitor.js +1 -0
  45. package/dist/server.d.ts +1 -1
  46. package/dist/server.js +453 -178
  47. package/dist/state/agent.d.ts +1 -0
  48. package/dist/state/agent.js +3 -0
  49. package/dist/state/delta.d.ts +12 -4
  50. package/dist/state/delta.js +67 -14
  51. package/dist/state/session.d.ts +6 -0
  52. package/dist/state/session.js +27 -0
  53. package/dist/tools/browser_detect.d.ts +16 -3
  54. package/dist/tools/browser_detect.js +52 -7
  55. package/dist/tools/burst.d.ts +17 -8
  56. package/dist/tools/burst.js +236 -77
  57. package/dist/tools/captcha.js +1 -1
  58. package/dist/tools/checkpoint.d.ts +38 -0
  59. package/dist/tools/checkpoint.js +271 -0
  60. package/dist/tools/click.d.ts +1 -0
  61. package/dist/tools/click.js +74 -70
  62. package/dist/tools/design_extract.js +1 -1
  63. package/dist/tools/eval.js +1 -1
  64. package/dist/tools/fill.d.ts +20 -1
  65. package/dist/tools/fill.js +199 -40
  66. package/dist/tools/find.d.ts +16 -0
  67. package/dist/tools/find.js +67 -44
  68. package/dist/tools/github/index.d.ts +19 -4
  69. package/dist/tools/github/index.js +40 -4
  70. package/dist/tools/highlight.d.ts +6 -2
  71. package/dist/tools/highlight.js +158 -72
  72. package/dist/tools/inspect.js +1 -1
  73. package/dist/tools/key.js +65 -56
  74. package/dist/tools/navigate.d.ts +1 -1
  75. package/dist/tools/navigate.js +34 -39
  76. package/dist/tools/pdf.js +21 -13
  77. package/dist/tools/pointer.d.ts +18 -3
  78. package/dist/tools/pointer.js +89 -1
  79. package/dist/tools/read.d.ts +28 -8
  80. package/dist/tools/read.js +230 -56
  81. package/dist/tools/scrape.js +67 -13
  82. package/dist/tools/screenshot.js +21 -16
  83. package/dist/tools/scroll.js +6 -11
  84. package/dist/tools/select.js +8 -2
  85. package/dist/tools/snapshot.js +3 -34
  86. package/dist/tools/tabs.js +15 -7
  87. package/dist/tools/thought.d.ts +2 -0
  88. package/dist/tools/thought.js +13 -14
  89. package/dist/tools/type.d.ts +2 -0
  90. package/dist/tools/type.js +107 -58
  91. package/dist/tools/type_secret.js +42 -76
  92. package/dist/tools/upload.js +1 -1
  93. package/dist/tools/video_compiler.d.ts +1 -1
  94. package/dist/tools/video_compiler.js +11 -12
  95. package/dist/version.d.ts +5 -0
  96. package/dist/version.js +5 -0
  97. package/docs/DOCUMENTATION.md +37 -41
  98. package/docs/PLUGINS.md +121 -0
  99. package/docs/TOOLS.md +54 -0
  100. package/docs/architecture/blind-injection.md +83 -20
  101. package/docs/architecture/overview.md +49 -49
  102. package/docs/architecture/security.md +8 -7
  103. package/docs/architecture/transport.md +2 -2
  104. package/docs/cli/cli-commands.md +7 -1
  105. package/docs/cli/cli-flags.md +15 -7
  106. package/docs/cli/cli-scripting.md +6 -3
  107. package/docs/configuration/config-locations.md +3 -4
  108. package/docs/configuration/env-variables.md +4 -4
  109. package/docs/configuration/folders.md +55 -54
  110. package/docs/desktop-automation/electron-apps.md +214 -162
  111. package/docs/get-started/introduction.md +60 -60
  112. package/docs/get-started/setup-wizard.md +7 -6
  113. package/docs/intelligence/site-profiles.md +151 -0
  114. package/docs/tools/tools.md +631 -293
  115. package/docs/troubleshooting/trouble-browser.md +1 -1
  116. package/docs/troubleshooting/trouble-loop.md +31 -31
  117. package/ext-dist/background.js +10 -62
  118. package/ext-dist/content.js +333 -920
  119. package/ext-dist/hud/hud.js +9 -9
  120. package/ext-dist/manifest.json +2 -6
  121. package/ext-dist/styles.css +6 -1
  122. package/icon.png +0 -0
  123. package/mcp.json +87 -71
  124. package/package.json +118 -119
  125. package/server.json +5 -5
  126. package/ext-dist/sidepanel/sidepanel.js +0 -25
  127. package/ext-dist/sidepanel.html +0 -547
package/SECURITY.md CHANGED
@@ -42,28 +42,43 @@ Weavetab interacts directly with the local Chromium browser instance via the Chr
42
42
 
43
43
  ---
44
44
 
45
- ## Blind Injection Threat Model
45
+ ## Blind Injection Threat Model & Hardening
46
46
 
47
- The **Blind Injection** architecture specifically ensures that credentials do not leak back into the LLM context or Weavetab logs.
47
+ The **Blind Injection** architecture specifically ensures that credentials do not leak back into the LLM context, Weavetab logs, DOM snapshots, visual captures, or scrape results.
48
48
 
49
49
  ### Threats Mitigated
50
50
 
51
- - **Accidental context inclusion** — LLM reading the DOM and echoing passwords
52
- - **Log scraping** — Passwords appearing in plaintext audit logs
53
- - **DOM snapshot leakage** — Secret values masked as `[REDACTED]` in all subsequent maps, deltas, and AX tree dumps
54
- - **Prompt injection domain theft** — Domain check runs before the secret value is ever resolved
51
+ - **Accidental Context Inclusion** — LLM reading the DOM, AX tree, or input discovery and echoing passwords
52
+ - **DOM & Scrape Scraping** — Passwords extracted via direct DOM access, `browser_scrape`, or `document.querySelector().value`
53
+ - **Visual Capture Leaks** — Passwords visible in screenshots, MHTML snapshots, or PDF printouts
54
+ - **Log Scraping** — Passwords appearing in plaintext action logs or audit records
55
+ - **DOM Snapshot Leakage** — Secret values masked as `[REDACTED]` in all subsequent maps, deltas, and AX tree dumps
56
+ - **Prompt Injection Domain Theft** — Domain check runs before the secret value is ever resolved from disk
57
+ - **Coercion & Serialization Leaks** — `JSON.stringify(el)`, `String(el)`, or array spreads leaking underlying element values
58
+
59
+ ### Defense-in-Depth Implementation
60
+
61
+ 1. **Storage at Rest**: Secrets in `~/.weavetab/secrets.json` are encrypted using AES-256-GCM with a machine-isolated key in `~/.weavetab-system/secrets-key`.
62
+ 2. **In-Process Resolution**: The AI model only receives and passes the key name (`envKey: "MY_KEY"`). The secret value is resolved in memory and injected directly via CDP `Input.insertText`.
63
+ 3. **DOM Property Hardening**: Injected elements are tagged `data-wt-secret="true"`, instance `.value` getters are overridden with `[REDACTED]`, and `Symbol.iterator`, `Symbol.toPrimitive`, and `toJSON` are locked.
64
+ 4. **In-Memory SessionSecretTracker**: Tracks node IDs and secret substrings per session to redact any coincidental echoes across ActionMaps, delta diffs, or scrape outputs.
65
+ 5. **Universal Tool Containment**:
66
+ - `browser_type`: Broad secret regex (`type`, `name`, `id`, `autocomplete`, `aria-label`, PINs, tokens, multi-language variants) auto-hardens target fields and suppresses raw value readbacks.
67
+ - `browser_fill`: Registers secrets in both single and `blast_mode`, redacting return values and scan discovery mode (`scan: true`).
68
+ - `browser_scrape`: Evaluates in-page extraction substituting all attributes of sensitive nodes with `[REDACTED]`.
69
+ - `browser_screenshot`, `browser_snapshot`, `browser_pdf`: Dynamically blanks/masks sensitive fields during capture and restores them in `finally` blocks.
55
70
 
56
71
  ### Threats NOT Mitigated
57
72
 
58
- - This does **not** protect against a compromised host or a malicious process reading the secrets file directly
59
- - It is not "zero-knowledge"; Weavetab must read the secret from `secrets.json` to inject it
73
+ - This does **not** protect against a compromised host or a malicious OS process with elevated privileges reading the secrets file directly.
74
+ - It is not "zero-knowledge"; Weavetab must read and decrypt the secret from `secrets.json` to inject it into Chromium.
60
75
 
61
76
  ### Secrets Resolution Order
62
77
 
63
78
  When `browser_type_secret({ envKey: 'MY_SECRET' })` is called, the `JsonSecretStore` resolves from:
64
79
 
65
- 1. `./.Weavetab/secrets.json` (project-scoped, opt-in via `Weavetab secrets init --project`)
66
- 2. `~/.Weavetab/secrets.json` (global default)
80
+ 1. `./.weavetab/secrets.json` (project-scoped, opt-in via `weavetab secrets init --project`)
81
+ 2. `~/.weavetab/secrets.json` (global default)
67
82
 
68
83
  The old `EnvSecretProvider` (`.env`-based) is **deprecated and no longer supported**. All secrets must be stored in `secrets.json`. See the [README Secrets Management section](README.md#-secrets-management) for the full CLI workflow.
69
84
 
@@ -72,14 +87,10 @@ The old `EnvSecretProvider` (`.env`-based) is **deprecated and no longer support
72
87
  ```json
73
88
  {
74
89
  "ACME_CORP_PASSWORD": {
75
- "value": "hunter2",
90
+ "value": "encrypted_aes_gcm_string",
76
91
  "domains": ["login.example.com"],
77
- "description": "Account password"
78
- },
79
- "GITHUB_2FA": {
80
- "value": "123456",
81
- "domains": ["github.com"],
82
- "description": "2FA Code"
92
+ "createdAt": "2026-08-29T10:00:00.000Z",
93
+ "updatedAt": "2026-08-29T10:00:00.000Z"
83
94
  }
84
95
  }
85
96
  ```
@@ -91,9 +102,9 @@ The old `EnvSecretProvider` (`.env`-based) is **deprecated and no longer support
91
102
  | Tool Category | admin | viewer | system |
92
103
  |---------------|-------|--------|--------|
93
104
  | All mutation tools | ✅ | ❌ | ❌ |
94
- | Read-only (map, find, scrape, console, eval, inspect, snapshot, screenshot, pdf, cookies, tabs, wait, github_*) | ✅ | ✅ | ❌ |
105
+ | Read-only (map, find, scrape, console, eval, inspect, snapshot, screenshot, pdf, cookies, tabs, wait, browser_github) | ✅ | ✅ | ❌ |
95
106
  | burst, plan, wait, map | ✅ | ❌ | ✅ |
96
- | System (logs, detect, performance, reset_loop_counter) | ✅ | ❌ | ❌ |
107
+ | System (logs, detect, performance) | ✅ | ❌ | ❌ |
97
108
 
98
109
  ### Role Definitions
99
110
 
@@ -131,7 +142,7 @@ If you discover a security vulnerability in Weavetab, please **do not** open a p
131
142
 
132
143
  Instead, report via one of the following channels:
133
144
 
134
- - **Email**: security@weavetab.pages.dev
145
+ - **Email**: security@weavetab.dev
135
146
  - **GitHub**: Use the private vulnerability reporting feature on the repository
136
147
 
137
148
  ### Disclosure Timeline
@@ -185,8 +196,8 @@ To receive security notifications:
185
196
 
186
197
  For security-related inquiries:
187
198
 
188
- - **Security Vulnerabilities**: security@weavetab.pages.dev
189
- - **Security Questions**: conduct@weavetab.pages.dev
199
+ - **Security Vulnerabilities**: security@weavetab.dev
200
+ - **Security Questions**: conduct@weavetab.dev
190
201
  - **GitHub Security**: https://github.com/weavetab/mcp/security
191
202
 
192
203
  ---
@@ -108,7 +108,7 @@ browser_click({ id: "w:42" })
108
108
 
109
109
  **Cause:** The warning previously relied on strict text matching.
110
110
 
111
- **Solution:** `stuck_warning` now respects the underlying `delta` computation. If the DOM changed, the warning will not fire. If you still see false positives, use `browser_reset_loop_counter` to manually reset.
111
+ **Solution:** `stuck_warning` now respects the underlying `delta` computation. If the DOM changed, the warning will not fire. If you still see false positives, use `browser_map({ force: true })` or `browser_wait({ reset_loop: true })` to manually reset.
112
112
 
113
113
  ---
114
114
 
@@ -360,6 +360,6 @@ If you continue to experience issues:
360
360
  ## Additional Resources
361
361
 
362
362
  - [Architecture Documentation](ARCHITECTURE.md)
363
- - [Tool Reference](docs/TOOLS.md)
363
+ - [Tool Reference](docs/tools/tools.md)
364
364
  - [Security Policy](SECURITY.md)
365
365
  - [Contributing Guidelines](CONTRIBUTING.md)
@@ -940,7 +940,8 @@ export async function launchElectronApp(appName, config) {
940
940
  throw new Error(`[Weavetab] ✗ Invalid or unreachable dev URL for "${appName}": ${e?.message || e}`);
941
941
  }
942
942
  }
943
- const execPath = appCfg.path;
943
+ const { resolveElectronExecutablePath } = await import("../desktop/discovery.js");
944
+ const execPath = resolveElectronExecutablePath(appName, appCfg.path) || appCfg.path;
944
945
  if (!execPath || !isValidElectronExecutablePath(execPath)) {
945
946
  throw new Error(`[Weavetab] ✗ Invalid or non-existent Electron executable path for "${appName}": ${execPath}`);
946
947
  }
@@ -21,6 +21,8 @@ export interface BrowserClient {
21
21
  isElectron?: boolean;
22
22
  /** Sticky pruning state: if true, all subsequent DOM reads by action tools (click, type, etc.) will also apply pruning. */
23
23
  pruneActive?: boolean;
24
+ /** Port the CDP session is connected to */
25
+ port?: number;
24
26
  }
25
27
  export declare function getOrConnect(config: Config, targetApp?: string): Promise<BrowserClient>;
26
28
  export declare function getTabList(agentId?: string, targetApp?: string): Promise<any[]>;
@@ -17,6 +17,7 @@ import { recordTabActivity } from "../tools/tabs.js";
17
17
  import { initDialogHandler } from "../tools/dialog.js";
18
18
  import { WT_REGISTRY_INIT_EXPR } from "./walker.js";
19
19
  import { urcInvalidate } from "./ref_cache.js";
20
+ import { marksInvalidate } from "../intelligence/marks.js";
20
21
  import { readLock, clearLock, isLockValid, isLockPending, acquireMutex } from "./lock.js";
21
22
  import { getSessionProfile } from "./profiles.js";
22
23
  import { safelyKillWeavetabProcesses, verifyProfileIsolation, verifyBrowserFingerprint, clearProfileVerificationCache } from "./security.js";
@@ -97,7 +98,6 @@ async function waitForPageReady(session) {
97
98
  // ─── Session init (overlay + re-injection) ───────────────────────────────────
98
99
  async function initSession(session, targetId, port, config, isElectron) {
99
100
  initDialogHandler(session);
100
- // Initialize Omni-Context Broker for iframes
101
101
  const { initOmniBroker } = await import("./omni_broker.js");
102
102
  await initOmniBroker(session, targetId, port);
103
103
  if (isElectron) {
@@ -151,6 +151,7 @@ async function initSession(session, targetId, port, config, isElectron) {
151
151
  clearTabCache(targetId);
152
152
  // Invalidate URC on navigation — elements from the old page are stale
153
153
  urcInvalidate(targetId);
154
+ marksInvalidate(targetId);
154
155
  if (isElectron || activeEngine === "chromium") {
155
156
  try {
156
157
  await waitForPageReady(session);
@@ -223,7 +224,10 @@ async function initSession(session, targetId, port, config, isElectron) {
223
224
  // ─── Main connection entry point ──────────────────────────────────────────────
224
225
  export async function getOrConnect(config, targetApp) {
225
226
  if (getConfigMode(config) === "attach" && !targetApp) {
226
- throw new Error("[!] Attach mode (connect to your real browser) coming soon. Use session mode for now.");
227
+ // track as limitation - use session mode
228
+ const err = new Error("[!] Attach mode is not yet supported. Use session mode (default) instead.");
229
+ err.code = "NOT_IMPLEMENTED";
230
+ throw err;
227
231
  }
228
232
  // 1. Check existing active session for this agent & app
229
233
  const activeClient = sessionRegistry.getActive(getConfigAgentId(config), targetApp);
@@ -505,7 +509,7 @@ export async function getOrConnect(config, targetApp) {
505
509
  // Continue anyway for now - in production this should be a hard failure
506
510
  }
507
511
  else {
508
- logDebug(`[Weavetab Security] Browser fingerprint verified successfully`);
512
+ logDebug(`[Weavetab Security] Browser fingerprint verified`);
509
513
  }
510
514
  }
511
515
  // NOTE: initSession() already installs the frameNavigated handler (overlay
@@ -521,7 +525,7 @@ export async function getOrConnect(config, targetApp) {
521
525
  // Continue anyway for now - in production this should be a hard failure
522
526
  }
523
527
  else {
524
- logDebug(`[Weavetab Security] Profile isolation verified successfully`);
528
+ logDebug(`[Weavetab Security] Profile isolation`);
525
529
  }
526
530
  await session.Target.setDiscoverTargets({ discover: true });
527
531
  session.on("Target.targetActivated", (event) => {
@@ -610,7 +614,7 @@ export async function getTabList(agentId, targetApp) {
610
614
  }
611
615
  }
612
616
  if (!port)
613
- throw new Error(`[!] No active ${targetApp ? targetApp : "browser"} session. Run browser_go or launch app first.`);
617
+ throw new Error(`[!] No active ${targetApp ? targetApp : "browser"} session. Run browser_navigate or launch app first.`);
614
618
  try {
615
619
  const metaSession = await withCDPTimeout(CDP({ host: "127.0.0.1", port }), 5000, "getTabList CDP");
616
620
  const { targetInfos } = await withCDPTimeout(metaSession.Target.getTargets(), 5000, "getTabList getTargets");
@@ -667,7 +671,7 @@ export async function connectToTab(targetId, port, agentId, browserContextId, ap
667
671
  const lock = readLock(appName);
668
672
  const p = port || (appName ? lock?.port : (actualPort || lock?.port));
669
673
  if (!p)
670
- throw new Error(`[!] No active ${appName || "browser"} session. Run browser_go first.`);
674
+ throw new Error(`[!] No active ${appName || "browser"} session. Run browser_navigate first.`);
671
675
  let t = null;
672
676
  try {
673
677
  const targets = await withCDPTimeout(CDP.List({ host: "127.0.0.1", port: p }), 3000, "connectToTab CDP.List");
@@ -9,3 +9,7 @@ export declare function sendToExtension(session: CDP.Client, message: object): P
9
9
  export declare function getFromExtension<T = any>(session: CDP.Client, message: object, retries?: number): Promise<T | null>;
10
10
  export declare function hideWeaveTabOverlay(session: CDP.Client): Promise<void>;
11
11
  export declare function restoreWeaveTabOverlay(session: CDP.Client): Promise<void>;
12
+ export declare function applySecretHardening(session: CDP.Client, objectId?: string, selector?: string): Promise<void>;
13
+ export declare function registerAndHardenSecretField(session: CDP.Client, targetId: string, selector: string, secretValue?: string, objectId?: string): Promise<void>;
14
+ export declare function maskSecretFieldsForCapture(session: CDP.Client): Promise<void>;
15
+ export declare function restoreSecretFieldsAfterCapture(session: CDP.Client): Promise<void>;
@@ -1,4 +1,5 @@
1
1
  import { logDebug } from "../audit/logger.js";
2
+ import { SessionSecretTracker } from "../security/secrets.js";
2
3
  const MAX_QUEUE_SIZE = 50;
3
4
  const BATCH_INTERVAL_MS = 10; // Reduced from 20ms for faster message delivery
4
5
  const messageQueues = new Map();
@@ -191,3 +192,183 @@ export async function restoreWeaveTabOverlay(session) {
191
192
  returnByValue: false,
192
193
  }).catch(() => { });
193
194
  }
195
+ // ─── Secret Hardening & Containment Helpers ──────────────────────────────────
196
+ export async function applySecretHardening(session, objectId, selector) {
197
+ const hardeningScript = `function() {
198
+ try {
199
+ const el = this;
200
+ if (!el) return;
201
+ // Mark as secret
202
+ try { el.setAttribute('data-wt-secret', 'true'); } catch (_) {}
203
+
204
+ // 1. Redact direct .value access via instance-level property override
205
+ const proto = Object.getPrototypeOf(el);
206
+ const origDesc = proto ? Object.getOwnPropertyDescriptor(proto, 'value') : null;
207
+ if (origDesc) {
208
+ Object.defineProperty(el, 'value', {
209
+ get: function() { return '[REDACTED]'; },
210
+ set: origDesc.set,
211
+ configurable: true,
212
+ enumerable: true
213
+ });
214
+ }
215
+
216
+ // 2. Defeat array spread, iteration, and coercion
217
+ try {
218
+ Object.defineProperty(el, Symbol.iterator, {
219
+ value: function* () { yield '[REDACTED]'; },
220
+ configurable: true
221
+ });
222
+ Object.defineProperty(el, Symbol.toPrimitive, {
223
+ value: function() { return '[REDACTED]'; },
224
+ configurable: true
225
+ });
226
+ // Defeat JSON.stringify / structuredClone leaks
227
+ Object.defineProperty(el, 'toJSON', {
228
+ value: function() { return '[REDACTED]'; },
229
+ configurable: true
230
+ });
231
+ } catch(_) {}
232
+ } catch (e) {
233
+ // fail silently so as not to disrupt the page execution
234
+ }
235
+ }`;
236
+ if (objectId) {
237
+ await session.Runtime.callFunctionOn({
238
+ functionDeclaration: hardeningScript,
239
+ objectId,
240
+ silent: true
241
+ }).catch(() => { });
242
+ }
243
+ else if (selector) {
244
+ await session.Runtime.evaluate({
245
+ expression: `(function(sel) {
246
+ const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
247
+ if (el) {
248
+ (${hardeningScript}).call(el);
249
+ }
250
+ })(${JSON.stringify(selector)})`,
251
+ returnByValue: false
252
+ }).catch(() => { });
253
+ }
254
+ }
255
+ export async function registerAndHardenSecretField(session, targetId, selector, secretValue, objectId) {
256
+ try {
257
+ if (secretValue) {
258
+ SessionSecretTracker.registerSecretValue(targetId, secretValue);
259
+ }
260
+ let resolvedObjectId = objectId;
261
+ if (!resolvedObjectId && selector) {
262
+ const nodeInfo = await session.Runtime.evaluate({
263
+ expression: `(function(sel) {
264
+ return document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
265
+ })(${JSON.stringify(selector)})`,
266
+ returnByValue: false
267
+ }).catch(() => null);
268
+ resolvedObjectId = nodeInfo?.result?.objectId;
269
+ }
270
+ if (resolvedObjectId) {
271
+ try {
272
+ const nodeDetails = await session.DOM.requestNode({ objectId: resolvedObjectId });
273
+ if (nodeDetails?.nodeId) {
274
+ const desc = await session.DOM.describeNode({ nodeId: nodeDetails.nodeId });
275
+ if (desc?.node?.backendNodeId) {
276
+ SessionSecretTracker.markNodeAsSecret(targetId, desc.node.backendNodeId);
277
+ }
278
+ }
279
+ }
280
+ catch { /* DOM lookup best-effort */ }
281
+ }
282
+ await applySecretHardening(session, resolvedObjectId, selector);
283
+ }
284
+ catch (err) {
285
+ logDebug(`[Weavetab] registerAndHardenSecretField failed: ${err}`);
286
+ }
287
+ }
288
+ export async function maskSecretFieldsForCapture(session) {
289
+ await session.Runtime.evaluate({
290
+ expression: `(function() {
291
+ var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
292
+
293
+ function queryAllDeep(root, matchFn) {
294
+ var matched = [];
295
+ var elements = root.querySelectorAll('*');
296
+ for (var i = 0; i < elements.length; i++) {
297
+ var el = elements[i];
298
+ if (matchFn(el)) matched.push(el);
299
+ if (el.shadowRoot) {
300
+ matched = matched.concat(queryAllDeep(el.shadowRoot, matchFn));
301
+ }
302
+ }
303
+ return matched;
304
+ }
305
+
306
+ function isSecretNode(el) {
307
+ if (!el || !el.tagName) return false;
308
+ if (el.hasAttribute && el.hasAttribute('data-wt-secret')) return true;
309
+ var tag = el.tagName.toLowerCase();
310
+ if (tag === 'input' || tag === 'textarea') {
311
+ var t = (el.getAttribute('type') || '').toLowerCase();
312
+ var ac = (el.getAttribute('autocomplete') || '').toLowerCase();
313
+ var name = (el.getAttribute('name') || '').toLowerCase();
314
+ var id = (el.getAttribute('id') || '').toLowerCase();
315
+ var aria = (el.getAttribute('aria-label') || '').toLowerCase();
316
+ return t === 'password' || ac.includes('password') || secretRegex.test(name) || secretRegex.test(id) || secretRegex.test(aria);
317
+ }
318
+ return false;
319
+ }
320
+
321
+ var els = queryAllDeep(document, isSecretNode);
322
+ for (var i = 0; i < els.length; i++) {
323
+ var el = els[i];
324
+ el.__wt_cap_val_backup = el.value;
325
+ try { el.value = ''; } catch(e) {}
326
+ if (el.isContentEditable) {
327
+ el.__wt_cap_txt_backup = el.textContent;
328
+ try { el.textContent = ''; } catch(e) {}
329
+ }
330
+ el.__wt_cap_sec_backup = el.style.webkitTextSecurity;
331
+ try { el.style.webkitTextSecurity = 'disc'; } catch(e) {}
332
+ }
333
+ })()`,
334
+ returnByValue: false,
335
+ }).catch(() => { });
336
+ }
337
+ export async function restoreSecretFieldsAfterCapture(session) {
338
+ await session.Runtime.evaluate({
339
+ expression: `(function() {
340
+ function queryAllDeep(root) {
341
+ var matched = [];
342
+ var elements = root.querySelectorAll('*');
343
+ for (var i = 0; i < elements.length; i++) {
344
+ var el = elements[i];
345
+ if (el.__wt_cap_val_backup !== undefined || el.__wt_cap_txt_backup !== undefined || el.__wt_cap_sec_backup !== undefined) {
346
+ matched.push(el);
347
+ }
348
+ if (el.shadowRoot) {
349
+ matched = matched.concat(queryAllDeep(el.shadowRoot));
350
+ }
351
+ }
352
+ return matched;
353
+ }
354
+
355
+ var els = queryAllDeep(document);
356
+ for (var i = 0; i < els.length; i++) {
357
+ var el = els[i];
358
+ if (el.__wt_cap_val_backup !== undefined) {
359
+ try { el.value = el.__wt_cap_val_backup; } catch(e) {}
360
+ delete el.__wt_cap_val_backup;
361
+ }
362
+ if (el.__wt_cap_txt_backup !== undefined) {
363
+ try { el.textContent = el.__wt_cap_txt_backup; } catch(e) {}
364
+ delete el.__wt_cap_txt_backup;
365
+ }
366
+ if (el.__wt_cap_sec_backup !== undefined) {
367
+ try { el.style.webkitTextSecurity = el.__wt_cap_sec_backup; } catch(e) {}
368
+ delete el.__wt_cap_sec_backup;
369
+ }
370
+ }
371
+ })()`,
372
+ returnByValue: false,
373
+ }).catch(() => { });
374
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Weavetab — cdp/omni_broker.ts
3
3
  * Omni-Context Execution Broker
4
- * Handles Target.setAutoAttach to seamlessly execute code in cross-origin iframes.
4
+ * Handles Target.setAutoAttach to run code in cross-origin iframes.
5
5
  * Weavetab by fy2ne
6
6
  */
7
7
  import CDP from "chrome-remote-interface";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Weavetab — cdp/omni_broker.ts
3
3
  * Omni-Context Execution Broker
4
- * Handles Target.setAutoAttach to seamlessly execute code in cross-origin iframes.
4
+ * Handles Target.setAutoAttach to run code in cross-origin iframes.
5
5
  * Weavetab by fy2ne
6
6
  */
7
7
  import CDP from "chrome-remote-interface";
@@ -4,7 +4,7 @@
4
4
  * Weavetab by fy2ne
5
5
  */
6
6
  import CDP from "chrome-remote-interface";
7
- import type { RefEntry } from "../state/delta.js";
7
+ import { type RefEntry } from "../state/delta.js";
8
8
  export declare const WT_REGISTRY_INIT_EXPR = "\n window.__weavetab = window.__weavetab || {};\n window.__weavetab.registry = window.__weavetab.registry || new WeakMap();\n";
9
9
  export type HrefType = "same-origin" | "external" | "anchor" | "email" | "unknown";
10
10
  export interface ActionElement {
@@ -31,6 +31,9 @@ export interface ActionElement {
31
31
  y?: number;
32
32
  viewport?: string;
33
33
  structuralIdiom?: string;
34
+ containerContext?: string;
35
+ isModal?: boolean;
36
+ modalTitle?: string;
34
37
  }
35
38
  export interface ActionMap {
36
39
  url: string;
@@ -89,3 +92,24 @@ export declare function resolveByAria(session: CDP.Client, query: string): Promi
89
92
  } | null>;
90
93
  export declare function clearAXTreeCache(targetId: string): void;
91
94
  export declare function buildAXTreeMap(session: CDP.Client, _options?: WalkerOptions, targetId?: string): Promise<ActionMap>;
95
+ /**
96
+ * Sanitizes element IDs in an ActionElement list, replacing empty or malformed
97
+ * IDs with sequential fallback IDs.
98
+ */
99
+ export declare function sanitizeElementIds(els: ActionElement[], counter: {
100
+ value: number;
101
+ }): number;
102
+ export interface RecoveredRefResult {
103
+ recovered: boolean;
104
+ element?: ActionElement;
105
+ strategy?: "connected_existing" | "stored_selector" | "exact_label" | "placeholder_or_name" | "fuzzy_label";
106
+ warning?: string;
107
+ }
108
+ /**
109
+ * Recovers a detached or invalidated w:NN ref on reactive SPAs.
110
+ * When hydration or DOM re-renders detach an element, this helper executes a sub-5ms
111
+ * multi-tier recovery heuristic (connected node verification, stored CSS selector,
112
+ * exact label/aria, placeholder/name match, and fuzzy substring) and re-stamps
113
+ * data-wt-id="NN" onto the live DOM node so subsequent interaction succeeds.
114
+ */
115
+ export declare function recoverDetachedRef(session: CDP.Client, targetId: string, ref: string, preferredCandidate?: Partial<ActionElement>): Promise<RecoveredRefResult>;