@weavetab/mcp 2.5.0-beta.2 → 2.5.0-beta.4
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/AGENTS.md +1 -1
- package/ARCHITECTURE.md +6 -6
- package/LICENSE +1 -1
- package/README.md +158 -961
- package/SECURITY.md +33 -22
- package/TROUBLESHOOTING.md +2 -2
- package/dist/cdp/bridge.js +2 -1
- package/dist/cdp/connector.d.ts +2 -0
- package/dist/cdp/connector.js +8 -4
- package/dist/cdp/helpers.d.ts +4 -0
- package/dist/cdp/helpers.js +181 -0
- package/dist/cdp/omni_broker.d.ts +1 -1
- package/dist/cdp/omni_broker.js +1 -1
- package/dist/cdp/walker.d.ts +25 -1
- package/dist/cdp/walker.js +304 -7
- package/dist/cli/plugin.js +24 -17
- package/dist/cli/updater.d.ts +1 -1
- package/dist/cli/updater.js +27 -7
- package/dist/config/loader.d.ts +8 -0
- package/dist/config/loader.js +41 -2
- package/dist/desktop/discovery.d.ts +44 -0
- package/dist/desktop/discovery.js +364 -0
- package/dist/index.js +95 -15
- package/dist/intelligence/marks.d.ts +36 -0
- package/dist/intelligence/marks.js +30 -0
- package/dist/intelligence/patternCompiler.d.ts +118 -0
- package/dist/intelligence/patternCompiler.js +259 -0
- package/dist/intelligence/sessionMemory.d.ts +2 -0
- package/dist/intelligence/sessionMemory.js +18 -8
- package/dist/overlay/injector.js +2 -2
- package/dist/plugin/interface.d.ts +76 -2
- package/dist/plugin/loader.d.ts +15 -1
- package/dist/plugin/loader.js +217 -70
- package/dist/plugin/pipeline.d.ts +75 -0
- package/dist/plugin/pipeline.js +183 -0
- package/dist/plugin/registry.js +3 -2
- package/dist/security/secrets.d.ts +1 -0
- package/dist/security/secrets.js +13 -0
- package/dist/sensors/actionability.js +1 -2
- package/dist/sensors/guard.js +4 -10
- package/dist/sensors/mutations.js +12 -0
- package/dist/sensors/operation_monitor.js +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +428 -156
- package/dist/state/agent.d.ts +1 -0
- package/dist/state/agent.js +3 -0
- package/dist/state/delta.d.ts +12 -4
- package/dist/state/delta.js +67 -14
- package/dist/state/session.d.ts +6 -0
- package/dist/state/session.js +27 -0
- package/dist/tools/browser_detect.d.ts +16 -3
- package/dist/tools/browser_detect.js +50 -5
- package/dist/tools/burst.d.ts +17 -8
- package/dist/tools/burst.js +236 -77
- package/dist/tools/checkpoint.d.ts +38 -0
- package/dist/tools/checkpoint.js +271 -0
- package/dist/tools/click.d.ts +1 -0
- package/dist/tools/click.js +74 -70
- package/dist/tools/design_extract.js +1 -1
- package/dist/tools/eval.js +1 -1
- package/dist/tools/fill.d.ts +20 -1
- package/dist/tools/fill.js +199 -40
- package/dist/tools/find.d.ts +16 -0
- package/dist/tools/find.js +67 -44
- package/dist/tools/github/index.d.ts +19 -4
- package/dist/tools/github/index.js +40 -4
- package/dist/tools/highlight.d.ts +6 -2
- package/dist/tools/highlight.js +158 -72
- package/dist/tools/inspect.js +1 -1
- package/dist/tools/key.js +65 -56
- package/dist/tools/navigate.js +29 -34
- package/dist/tools/pdf.js +21 -13
- package/dist/tools/pointer.d.ts +18 -3
- package/dist/tools/pointer.js +89 -1
- package/dist/tools/read.d.ts +28 -8
- package/dist/tools/read.js +230 -56
- package/dist/tools/scrape.js +67 -13
- package/dist/tools/screenshot.js +21 -16
- package/dist/tools/scroll.js +6 -11
- package/dist/tools/select.js +8 -2
- package/dist/tools/snapshot.js +3 -34
- package/dist/tools/tabs.js +14 -6
- package/dist/tools/thought.d.ts +2 -0
- package/dist/tools/thought.js +13 -14
- package/dist/tools/type.d.ts +2 -0
- package/dist/tools/type.js +107 -58
- package/dist/tools/type_secret.js +42 -76
- package/dist/tools/upload.js +1 -1
- package/dist/tools/video_compiler.d.ts +1 -1
- package/dist/tools/video_compiler.js +11 -12
- package/dist/version.d.ts +5 -0
- package/dist/version.js +5 -0
- package/docs/DOCUMENTATION.md +21 -32
- package/docs/PLUGINS.md +121 -0
- package/docs/TOOLS.md +56 -0
- package/docs/architecture/blind-injection.md +83 -20
- package/docs/architecture/overview.md +49 -49
- package/docs/configuration/folders.md +55 -54
- package/docs/desktop-automation/electron-apps.md +217 -162
- package/docs/get-started/introduction.md +60 -60
- package/docs/intelligence/site-profiles.md +151 -0
- package/docs/tools/tools.md +631 -293
- package/docs/troubleshooting/trouble-loop.md +31 -31
- package/ext-dist/background.js +10 -62
- package/ext-dist/content.js +333 -920
- package/ext-dist/hud/hud.js +9 -9
- package/ext-dist/manifest.json +2 -6
- package/ext-dist/styles.css +6 -1
- package/mcp.json +101 -67
- package/package.json +5 -6
- package/server.json +3 -3
- package/ext-dist/sidepanel/sidepanel.js +0 -25
- 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
|
|
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
|
|
52
|
-
- **
|
|
53
|
-
- **
|
|
54
|
-
- **
|
|
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. `./.
|
|
66
|
-
2. `~/.
|
|
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": "
|
|
90
|
+
"value": "encrypted_aes_gcm_string",
|
|
76
91
|
"domains": ["login.example.com"],
|
|
77
|
-
"
|
|
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,
|
|
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
|
|
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.
|
|
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.
|
|
189
|
-
- **Security Questions**: conduct@weavetab.
|
|
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
|
---
|
package/TROUBLESHOOTING.md
CHANGED
|
@@ -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 `
|
|
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/
|
|
363
|
+
- [Tool Reference](docs/tools/tools.md)
|
|
364
364
|
- [Security Policy](SECURITY.md)
|
|
365
365
|
- [Contributing Guidelines](CONTRIBUTING.md)
|
package/dist/cdp/bridge.js
CHANGED
|
@@ -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
|
|
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
|
}
|
package/dist/cdp/connector.d.ts
CHANGED
|
@@ -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[]>;
|
package/dist/cdp/connector.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
528
|
+
logDebug(`[Weavetab Security] Profile isolation`);
|
|
525
529
|
}
|
|
526
530
|
await session.Target.setDiscoverTargets({ discover: true });
|
|
527
531
|
session.on("Target.targetActivated", (event) => {
|
package/dist/cdp/helpers.d.ts
CHANGED
|
@@ -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>;
|
package/dist/cdp/helpers.js
CHANGED
|
@@ -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
|
|
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";
|
package/dist/cdp/omni_broker.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Weavetab — cdp/omni_broker.ts
|
|
3
3
|
* Omni-Context Execution Broker
|
|
4
|
-
* Handles Target.setAutoAttach to
|
|
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";
|
package/dist/cdp/walker.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Weavetab by fy2ne
|
|
5
5
|
*/
|
|
6
6
|
import CDP from "chrome-remote-interface";
|
|
7
|
-
import type
|
|
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>;
|