agent-browser-stealth 0.15.1-fork.6 → 0.15.1-fork.7
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/bin/agent-browser-darwin-arm64 +0 -0
- package/dist/action-policy.d.ts +14 -0
- package/dist/action-policy.d.ts.map +1 -0
- package/dist/action-policy.js +253 -0
- package/dist/action-policy.js.map +1 -0
- package/dist/actions.d.ts +5 -1
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +126 -70
- package/dist/actions.js.map +1 -1
- package/dist/auth-cli.d.ts +2 -0
- package/dist/auth-cli.d.ts.map +1 -0
- package/dist/auth-cli.js +97 -0
- package/dist/auth-cli.js.map +1 -0
- package/dist/auth-vault.d.ts +36 -0
- package/dist/auth-vault.d.ts.map +1 -0
- package/dist/auth-vault.js +125 -0
- package/dist/auth-vault.js.map +1 -0
- package/dist/browser.d.ts +19 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +121 -5
- package/dist/browser.js.map +1 -1
- package/dist/confirmation.d.ts +8 -0
- package/dist/confirmation.d.ts.map +1 -0
- package/dist/confirmation.js +30 -0
- package/dist/confirmation.js.map +1 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +3 -2
- package/dist/daemon.js.map +1 -1
- package/dist/domain-filter.d.ts +28 -0
- package/dist/domain-filter.d.ts.map +1 -0
- package/dist/domain-filter.js +149 -0
- package/dist/domain-filter.js.map +1 -0
- package/dist/encryption.d.ts +25 -2
- package/dist/encryption.d.ts.map +1 -1
- package/dist/encryption.js +96 -10
- package/dist/encryption.js.map +1 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +51 -0
- package/dist/protocol.js.map +1 -1
- package/dist/stealth.d.ts +3 -1
- package/dist/stealth.d.ts.map +1 -1
- package/dist/stealth.js +19 -11
- package/dist/stealth.js.map +1 -1
- package/dist/types.d.ts +74 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ActionPolicy {
|
|
2
|
+
default: 'allow' | 'deny';
|
|
3
|
+
allow?: string[];
|
|
4
|
+
deny?: string[];
|
|
5
|
+
}
|
|
6
|
+
export type PolicyDecision = 'allow' | 'deny' | 'confirm';
|
|
7
|
+
export declare const KNOWN_CATEGORIES: Set<string>;
|
|
8
|
+
export declare function getActionCategory(action: string): string;
|
|
9
|
+
export declare function loadPolicyFile(policyPath: string): ActionPolicy;
|
|
10
|
+
export declare function initPolicyReloader(policyPath: string, policy: ActionPolicy): void;
|
|
11
|
+
export declare function reloadPolicyIfChanged(): ActionPolicy | null;
|
|
12
|
+
export declare function checkPolicy(action: string, policy: ActionPolicy | null, confirmCategories: Set<string>): PolicyDecision;
|
|
13
|
+
export declare function describeAction(action: string, command: Record<string, unknown>): string;
|
|
14
|
+
//# sourceMappingURL=action-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-policy.d.ts","sourceRoot":"","sources":["../src/action-policy.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AA4K1D,eAAO,MAAM,gBAAgB,aAE5B,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAwB/D;AAQD,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAIjF;AAED,wBAAgB,qBAAqB,IAAI,YAAY,GAAG,IAAI,CAkB3D;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,GAAG,IAAI,EAC3B,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,GAC7B,cAAc,CAkBhB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAyBvF"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
const ACTION_CATEGORIES = {
|
|
4
|
+
navigate: 'navigate',
|
|
5
|
+
back: 'navigate',
|
|
6
|
+
forward: 'navigate',
|
|
7
|
+
reload: 'navigate',
|
|
8
|
+
tab_new: 'navigate',
|
|
9
|
+
click: 'click',
|
|
10
|
+
dblclick: 'click',
|
|
11
|
+
tap: 'click',
|
|
12
|
+
fill: 'fill',
|
|
13
|
+
type: 'fill',
|
|
14
|
+
// The `keyboard` action is a compound command that dispatches to sub-actions
|
|
15
|
+
// (type, inserttext, press, down, up). Its primary use is text input, so it
|
|
16
|
+
// maps to 'fill'. The interact-like sub-actions (press, down, up) are less
|
|
17
|
+
// common and don't have separate top-level action names in the protocol.
|
|
18
|
+
keyboard: 'fill',
|
|
19
|
+
inserttext: 'fill',
|
|
20
|
+
select: 'fill',
|
|
21
|
+
multiselect: 'fill',
|
|
22
|
+
check: 'fill',
|
|
23
|
+
uncheck: 'fill',
|
|
24
|
+
clear: 'fill',
|
|
25
|
+
selectall: 'fill',
|
|
26
|
+
setvalue: 'fill',
|
|
27
|
+
download: 'download',
|
|
28
|
+
waitfordownload: 'download',
|
|
29
|
+
upload: 'upload',
|
|
30
|
+
evaluate: 'eval',
|
|
31
|
+
evalhandle: 'eval',
|
|
32
|
+
addscript: 'eval',
|
|
33
|
+
addinitscript: 'eval',
|
|
34
|
+
snapshot: 'snapshot',
|
|
35
|
+
screenshot: 'snapshot',
|
|
36
|
+
pdf: 'snapshot',
|
|
37
|
+
diff_snapshot: 'snapshot',
|
|
38
|
+
diff_screenshot: 'snapshot',
|
|
39
|
+
diff_url: 'snapshot',
|
|
40
|
+
scroll: 'scroll',
|
|
41
|
+
scrollintoview: 'scroll',
|
|
42
|
+
wait: 'wait',
|
|
43
|
+
waitforurl: 'wait',
|
|
44
|
+
waitforloadstate: 'wait',
|
|
45
|
+
waitforfunction: 'wait',
|
|
46
|
+
gettext: 'get',
|
|
47
|
+
content: 'get',
|
|
48
|
+
innerhtml: 'get',
|
|
49
|
+
innertext: 'get',
|
|
50
|
+
inputvalue: 'get',
|
|
51
|
+
url: 'get',
|
|
52
|
+
title: 'get',
|
|
53
|
+
getattribute: 'get',
|
|
54
|
+
count: 'get',
|
|
55
|
+
boundingbox: 'get',
|
|
56
|
+
styles: 'get',
|
|
57
|
+
isvisible: 'get',
|
|
58
|
+
isenabled: 'get',
|
|
59
|
+
ischecked: 'get',
|
|
60
|
+
responsebody: 'get',
|
|
61
|
+
route: 'network',
|
|
62
|
+
unroute: 'network',
|
|
63
|
+
requests: 'network',
|
|
64
|
+
state_save: 'state',
|
|
65
|
+
state_load: 'state',
|
|
66
|
+
cookies_set: 'state',
|
|
67
|
+
storage_set: 'state',
|
|
68
|
+
credentials: 'state',
|
|
69
|
+
hover: 'interact',
|
|
70
|
+
focus: 'interact',
|
|
71
|
+
drag: 'interact',
|
|
72
|
+
press: 'interact',
|
|
73
|
+
keydown: 'interact',
|
|
74
|
+
keyup: 'interact',
|
|
75
|
+
mousemove: 'interact',
|
|
76
|
+
mousedown: 'interact',
|
|
77
|
+
mouseup: 'interact',
|
|
78
|
+
wheel: 'interact',
|
|
79
|
+
dispatch: 'interact',
|
|
80
|
+
// These are always allowed (internal/meta operations)
|
|
81
|
+
launch: '_internal',
|
|
82
|
+
close: '_internal',
|
|
83
|
+
tab_list: '_internal',
|
|
84
|
+
tab_switch: '_internal',
|
|
85
|
+
tab_close: '_internal',
|
|
86
|
+
window_new: '_internal',
|
|
87
|
+
frame: '_internal',
|
|
88
|
+
mainframe: '_internal',
|
|
89
|
+
dialog: '_internal',
|
|
90
|
+
session: '_internal',
|
|
91
|
+
console: '_internal',
|
|
92
|
+
errors: '_internal',
|
|
93
|
+
cookies_get: '_internal',
|
|
94
|
+
cookies_clear: '_internal',
|
|
95
|
+
storage_get: '_internal',
|
|
96
|
+
storage_clear: '_internal',
|
|
97
|
+
state_list: '_internal',
|
|
98
|
+
state_show: '_internal',
|
|
99
|
+
state_clear: '_internal',
|
|
100
|
+
state_clean: '_internal',
|
|
101
|
+
state_rename: '_internal',
|
|
102
|
+
highlight: '_internal',
|
|
103
|
+
bringtofront: '_internal',
|
|
104
|
+
trace_start: '_internal',
|
|
105
|
+
trace_stop: '_internal',
|
|
106
|
+
har_start: '_internal',
|
|
107
|
+
har_stop: '_internal',
|
|
108
|
+
video_start: '_internal',
|
|
109
|
+
video_stop: '_internal',
|
|
110
|
+
recording_start: '_internal',
|
|
111
|
+
recording_stop: '_internal',
|
|
112
|
+
recording_restart: '_internal',
|
|
113
|
+
profiler_start: '_internal',
|
|
114
|
+
profiler_stop: '_internal',
|
|
115
|
+
clipboard: '_internal',
|
|
116
|
+
viewport: '_internal',
|
|
117
|
+
useragent: '_internal',
|
|
118
|
+
device: '_internal',
|
|
119
|
+
geolocation: '_internal',
|
|
120
|
+
permissions: '_internal',
|
|
121
|
+
emulatemedia: '_internal',
|
|
122
|
+
offline: '_internal',
|
|
123
|
+
headers: '_internal',
|
|
124
|
+
addstyle: 'eval',
|
|
125
|
+
expose: 'eval',
|
|
126
|
+
timezone: '_internal',
|
|
127
|
+
locale: '_internal',
|
|
128
|
+
pause: '_internal',
|
|
129
|
+
setcontent: 'eval',
|
|
130
|
+
screencast_start: '_internal',
|
|
131
|
+
screencast_stop: '_internal',
|
|
132
|
+
input_mouse: '_internal',
|
|
133
|
+
input_keyboard: '_internal',
|
|
134
|
+
input_touch: '_internal',
|
|
135
|
+
auth_save: '_internal',
|
|
136
|
+
auth_login: '_internal',
|
|
137
|
+
auth_list: '_internal',
|
|
138
|
+
auth_delete: '_internal',
|
|
139
|
+
auth_show: '_internal',
|
|
140
|
+
confirm: '_internal',
|
|
141
|
+
deny: '_internal',
|
|
142
|
+
// Find/semantic locator actions (read-only element resolution)
|
|
143
|
+
getbyrole: 'get',
|
|
144
|
+
getbytext: 'get',
|
|
145
|
+
getbylabel: 'get',
|
|
146
|
+
getbyplaceholder: 'get',
|
|
147
|
+
getbyalttext: 'get',
|
|
148
|
+
getbytitle: 'get',
|
|
149
|
+
getbytestid: 'get',
|
|
150
|
+
nth: 'get',
|
|
151
|
+
};
|
|
152
|
+
// User-facing categories used in policy files. '_internal' is excluded because
|
|
153
|
+
// internal actions always bypass policy. 'unknown' is intentionally not a value
|
|
154
|
+
// in ACTION_CATEGORIES -- it is only the fallback return of getActionCategory()
|
|
155
|
+
// for unrecognized actions. If a user puts "unknown" in a policy file,
|
|
156
|
+
// loadPolicyFile will warn about it as unrecognized, which is correct.
|
|
157
|
+
export const KNOWN_CATEGORIES = new Set(Object.values(ACTION_CATEGORIES).filter((c) => c !== '_internal'));
|
|
158
|
+
export function getActionCategory(action) {
|
|
159
|
+
return ACTION_CATEGORIES[action] ?? 'unknown';
|
|
160
|
+
}
|
|
161
|
+
export function loadPolicyFile(policyPath) {
|
|
162
|
+
const resolved = resolve(policyPath);
|
|
163
|
+
const content = readFileSync(resolved, 'utf-8');
|
|
164
|
+
const policy = JSON.parse(content);
|
|
165
|
+
if (policy.default !== 'allow' && policy.default !== 'deny') {
|
|
166
|
+
throw new Error(`Invalid action policy: "default" must be "allow" or "deny", got "${policy.default}"`);
|
|
167
|
+
}
|
|
168
|
+
for (const list of [policy.allow, policy.deny]) {
|
|
169
|
+
if (!list)
|
|
170
|
+
continue;
|
|
171
|
+
for (const category of list) {
|
|
172
|
+
if (!KNOWN_CATEGORIES.has(category)) {
|
|
173
|
+
console.warn(`[agent-browser] Warning: unrecognized action category "${category}" in policy file. ` +
|
|
174
|
+
`Known categories: ${[...KNOWN_CATEGORIES].sort().join(', ')}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return policy;
|
|
179
|
+
}
|
|
180
|
+
let cachedPolicyPath = null;
|
|
181
|
+
let cachedPolicyMtimeMs = 0;
|
|
182
|
+
let cachedPolicy = null;
|
|
183
|
+
const RELOAD_CHECK_INTERVAL_MS = 5_000;
|
|
184
|
+
let lastCheckMs = 0;
|
|
185
|
+
export function initPolicyReloader(policyPath, policy) {
|
|
186
|
+
cachedPolicyPath = resolve(policyPath);
|
|
187
|
+
cachedPolicyMtimeMs = statSync(cachedPolicyPath).mtimeMs;
|
|
188
|
+
cachedPolicy = policy;
|
|
189
|
+
}
|
|
190
|
+
export function reloadPolicyIfChanged() {
|
|
191
|
+
if (!cachedPolicyPath)
|
|
192
|
+
return cachedPolicy;
|
|
193
|
+
const now = Date.now();
|
|
194
|
+
if (now - lastCheckMs < RELOAD_CHECK_INTERVAL_MS)
|
|
195
|
+
return cachedPolicy;
|
|
196
|
+
lastCheckMs = now;
|
|
197
|
+
try {
|
|
198
|
+
const currentMtime = statSync(cachedPolicyPath).mtimeMs;
|
|
199
|
+
if (currentMtime !== cachedPolicyMtimeMs) {
|
|
200
|
+
cachedPolicy = loadPolicyFile(cachedPolicyPath);
|
|
201
|
+
cachedPolicyMtimeMs = currentMtime;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// File may have been removed; keep using cached policy
|
|
206
|
+
}
|
|
207
|
+
return cachedPolicy;
|
|
208
|
+
}
|
|
209
|
+
export function checkPolicy(action, policy, confirmCategories) {
|
|
210
|
+
const category = getActionCategory(action);
|
|
211
|
+
// Internal actions are always allowed
|
|
212
|
+
if (category === '_internal')
|
|
213
|
+
return 'allow';
|
|
214
|
+
// Explicit deny takes precedence over confirmation
|
|
215
|
+
if (policy?.deny?.includes(category))
|
|
216
|
+
return 'deny';
|
|
217
|
+
// Check if this category requires confirmation
|
|
218
|
+
if (confirmCategories.has(category))
|
|
219
|
+
return 'confirm';
|
|
220
|
+
if (!policy)
|
|
221
|
+
return 'allow';
|
|
222
|
+
// Explicit allow list
|
|
223
|
+
if (policy.allow?.includes(category))
|
|
224
|
+
return 'allow';
|
|
225
|
+
return policy.default;
|
|
226
|
+
}
|
|
227
|
+
export function describeAction(action, command) {
|
|
228
|
+
const category = getActionCategory(action);
|
|
229
|
+
switch (action) {
|
|
230
|
+
case 'navigate':
|
|
231
|
+
return `Navigate to ${command.url}`;
|
|
232
|
+
case 'evaluate':
|
|
233
|
+
case 'evalhandle':
|
|
234
|
+
return `Evaluate JavaScript: ${String(command.script ?? '').slice(0, 80)}`;
|
|
235
|
+
case 'fill':
|
|
236
|
+
return `Fill ${command.selector}`;
|
|
237
|
+
case 'type':
|
|
238
|
+
return `Type into ${command.selector}`;
|
|
239
|
+
case 'click':
|
|
240
|
+
return `Click ${command.selector}`;
|
|
241
|
+
case 'dblclick':
|
|
242
|
+
return `Double-click ${command.selector}`;
|
|
243
|
+
case 'tap':
|
|
244
|
+
return `Tap ${command.selector}`;
|
|
245
|
+
case 'download':
|
|
246
|
+
return `Download via ${command.selector} to ${command.path}`;
|
|
247
|
+
case 'upload':
|
|
248
|
+
return `Upload files to ${command.selector}`;
|
|
249
|
+
default:
|
|
250
|
+
return `${category}: ${action}`;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=action-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-policy.js","sourceRoot":"","sources":["../src/action-policy.ts"],"names":[],"mappings":"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"}
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BrowserManager, ScreencastFrame } from './browser.js';
|
|
2
|
-
import type { Command, Response } from './types.js';
|
|
2
|
+
import type { Command, Response, RiskSignal } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Set the callback for screencast frames
|
|
5
5
|
* This is called by the daemon to set up frame streaming
|
|
@@ -14,4 +14,8 @@ export declare function toAIFriendlyError(error: unknown, selector: string): Err
|
|
|
14
14
|
* Execute a command and return a response
|
|
15
15
|
*/
|
|
16
16
|
export declare function executeCommand(command: Command, browser: BrowserManager): Promise<Response>;
|
|
17
|
+
/**
|
|
18
|
+
* Detect verification/captcha interstitials and return structured risk evidence.
|
|
19
|
+
*/
|
|
20
|
+
export declare function detectRiskSignals(url: string, title: string): RiskSignal[];
|
|
17
21
|
//# sourceMappingURL=actions.d.ts.map
|
package/dist/actions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAUpE,OAAO,KAAK,EACV,OAAO,EACP,QAAQ,
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAUpE,OAAO,KAAK,EACV,OAAO,EACP,QAAQ,EAmIR,UAAU,EACX,MAAM,YAAY,CAAC;AAQpB;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC,GAAG,IAAI,GAClD,IAAI,CAEN;AAQD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAqDzE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuRjG;AAyGD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,CA8C1E"}
|
package/dist/actions.js
CHANGED
|
@@ -359,65 +359,114 @@ async function handleNavigate(command, browser) {
|
|
|
359
359
|
await page.goto(command.url, {
|
|
360
360
|
waitUntil: command.waitUntil ?? 'load',
|
|
361
361
|
});
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
const title = await page.title();
|
|
365
|
-
const captchaDetected = isCaptchaPage(finalUrl, title);
|
|
366
|
-
if (captchaDetected) {
|
|
367
|
-
const maxRetries = 2;
|
|
368
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
369
|
-
const backoff = 3000 + Math.random() * 4000;
|
|
370
|
-
await page.waitForTimeout(Math.round(backoff));
|
|
371
|
-
await page.goto(command.url, {
|
|
372
|
-
waitUntil: command.waitUntil ?? 'load',
|
|
373
|
-
});
|
|
374
|
-
const retryUrl = page.url();
|
|
375
|
-
const retryTitle = await page.title();
|
|
376
|
-
if (!isCaptchaPage(retryUrl, retryTitle)) {
|
|
377
|
-
return successResponse(command.id, {
|
|
378
|
-
url: retryUrl,
|
|
379
|
-
title: retryTitle,
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
// All retries exhausted -- return the page as-is with a warning
|
|
362
|
+
const riskMode = command.riskMode ?? 'warn';
|
|
363
|
+
if (riskMode === 'off') {
|
|
384
364
|
return successResponse(command.id, {
|
|
385
365
|
url: page.url(),
|
|
386
366
|
title: await page.title(),
|
|
387
|
-
warning: 'Captcha/verification page detected. Try --headed mode or use --session-name for state persistence.',
|
|
388
367
|
});
|
|
389
368
|
}
|
|
369
|
+
// Detect risk interstitials (captcha/verification) and handle by risk mode.
|
|
370
|
+
const finalUrl = page.url();
|
|
371
|
+
const title = await page.title();
|
|
372
|
+
let encounteredSignals = detectRiskSignals(finalUrl, title);
|
|
373
|
+
if (encounteredSignals.length === 0) {
|
|
374
|
+
return successResponse(command.id, {
|
|
375
|
+
url: finalUrl,
|
|
376
|
+
title,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
if (riskMode === 'block') {
|
|
380
|
+
const first = encounteredSignals[0];
|
|
381
|
+
return errorResponse(command.id, `Navigation blocked by risk-mode=block: ${first.code} (${first.source}="${first.evidence}")`);
|
|
382
|
+
}
|
|
383
|
+
const maxRetries = 2;
|
|
384
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
385
|
+
const backoff = 3000 + Math.random() * 4000;
|
|
386
|
+
await page.waitForTimeout(Math.round(backoff));
|
|
387
|
+
await page.goto(command.url, {
|
|
388
|
+
waitUntil: command.waitUntil ?? 'load',
|
|
389
|
+
});
|
|
390
|
+
const retryUrl = page.url();
|
|
391
|
+
const retryTitle = await page.title();
|
|
392
|
+
const retrySignals = detectRiskSignals(retryUrl, retryTitle);
|
|
393
|
+
if (retrySignals.length === 0) {
|
|
394
|
+
return successResponse(command.id, {
|
|
395
|
+
url: retryUrl,
|
|
396
|
+
title: retryTitle,
|
|
397
|
+
warning: 'Risk interstitial detected and recovered after retry. Review riskSignals for evidence.',
|
|
398
|
+
riskSignals: encounteredSignals,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
encounteredSignals = mergeRiskSignals(encounteredSignals, retrySignals);
|
|
402
|
+
}
|
|
403
|
+
// All retries exhausted -- return the page as-is with a warning and evidence.
|
|
390
404
|
return successResponse(command.id, {
|
|
391
|
-
url:
|
|
392
|
-
title,
|
|
405
|
+
url: page.url(),
|
|
406
|
+
title: await page.title(),
|
|
407
|
+
warning: 'Captcha/verification page detected. Try --headed mode or use --session-name for state persistence.',
|
|
408
|
+
riskSignals: encounteredSignals,
|
|
393
409
|
});
|
|
394
410
|
}
|
|
395
|
-
function
|
|
411
|
+
function mergeRiskSignals(current, next) {
|
|
412
|
+
const merged = new Map();
|
|
413
|
+
for (const signal of [...current, ...next]) {
|
|
414
|
+
const key = `${signal.code}|${signal.source}|${signal.evidence}`;
|
|
415
|
+
if (!merged.has(key) || (merged.get(key)?.confidence ?? 0) < signal.confidence) {
|
|
416
|
+
merged.set(key, signal);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return [...merged.values()];
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Detect verification/captcha interstitials and return structured risk evidence.
|
|
423
|
+
*/
|
|
424
|
+
export function detectRiskSignals(url, title) {
|
|
396
425
|
const lowerUrl = url.toLowerCase();
|
|
397
426
|
const lowerTitle = title.toLowerCase();
|
|
398
|
-
const
|
|
399
|
-
'/verify/captcha',
|
|
400
|
-
'/captcha',
|
|
401
|
-
'/challenge',
|
|
402
|
-
'scene=crawler',
|
|
403
|
-
'scene=anti_bot',
|
|
404
|
-
'recaptcha',
|
|
405
|
-
'hcaptcha',
|
|
427
|
+
const urlPatterns = [
|
|
428
|
+
{ pattern: '/verify/captcha', code: 'captcha_interstitial', confidence: 0.98 },
|
|
429
|
+
{ pattern: '/captcha', code: 'captcha_interstitial', confidence: 0.95 },
|
|
430
|
+
{ pattern: '/challenge', code: 'verification_interstitial', confidence: 0.93 },
|
|
431
|
+
{ pattern: 'scene=crawler', code: 'bot_challenge', confidence: 0.99 },
|
|
432
|
+
{ pattern: 'scene=anti_bot', code: 'bot_challenge', confidence: 0.99 },
|
|
433
|
+
{ pattern: 'recaptcha', code: 'captcha_interstitial', confidence: 0.97 },
|
|
434
|
+
{ pattern: 'hcaptcha', code: 'captcha_interstitial', confidence: 0.97 },
|
|
406
435
|
];
|
|
407
436
|
const titlePatterns = [
|
|
408
|
-
'verify',
|
|
409
|
-
'captcha',
|
|
410
|
-
'challenge',
|
|
411
|
-
'attention required',
|
|
412
|
-
'just a moment',
|
|
413
|
-
'checking your browser',
|
|
414
|
-
'access denied',
|
|
415
|
-
'驗證',
|
|
416
|
-
'验证',
|
|
417
|
-
'人机验证',
|
|
437
|
+
{ pattern: 'verify', code: 'verification_interstitial', confidence: 0.78 },
|
|
438
|
+
{ pattern: 'captcha', code: 'captcha_interstitial', confidence: 0.9 },
|
|
439
|
+
{ pattern: 'challenge', code: 'verification_interstitial', confidence: 0.8 },
|
|
440
|
+
{ pattern: 'attention required', code: 'verification_interstitial', confidence: 0.96 },
|
|
441
|
+
{ pattern: 'just a moment', code: 'verification_interstitial', confidence: 0.95 },
|
|
442
|
+
{ pattern: 'checking your browser', code: 'verification_interstitial', confidence: 0.97 },
|
|
443
|
+
{ pattern: 'access denied', code: 'access_gate', confidence: 0.86 },
|
|
444
|
+
{ pattern: '驗證', code: 'verification_interstitial', confidence: 0.88 },
|
|
445
|
+
{ pattern: '验证', code: 'verification_interstitial', confidence: 0.88 },
|
|
446
|
+
{ pattern: '人机验证', code: 'captcha_interstitial', confidence: 0.95 },
|
|
418
447
|
];
|
|
419
|
-
|
|
420
|
-
|
|
448
|
+
const signals = [];
|
|
449
|
+
for (const item of urlPatterns) {
|
|
450
|
+
if (lowerUrl.includes(item.pattern)) {
|
|
451
|
+
signals.push({
|
|
452
|
+
code: item.code,
|
|
453
|
+
source: 'url',
|
|
454
|
+
evidence: item.pattern,
|
|
455
|
+
confidence: item.confidence,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
for (const item of titlePatterns) {
|
|
460
|
+
if (lowerTitle.includes(item.pattern)) {
|
|
461
|
+
signals.push({
|
|
462
|
+
code: item.code,
|
|
463
|
+
source: 'title',
|
|
464
|
+
evidence: item.pattern,
|
|
465
|
+
confidence: item.confidence,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return mergeRiskSignals([], signals);
|
|
421
470
|
}
|
|
422
471
|
function bezierPoint(t, p0, p1, p2, p3) {
|
|
423
472
|
const u = 1 - t;
|
|
@@ -746,36 +795,36 @@ async function handleWait(command, browser) {
|
|
|
746
795
|
}
|
|
747
796
|
async function handleScroll(command, browser) {
|
|
748
797
|
const page = browser.getPage();
|
|
798
|
+
let deltaX = command.x ?? 0;
|
|
799
|
+
let deltaY = command.y ?? 0;
|
|
800
|
+
const hasExplicitDelta = command.x !== undefined || command.y !== undefined;
|
|
801
|
+
if (command.direction) {
|
|
802
|
+
const amount = command.amount ?? 100;
|
|
803
|
+
switch (command.direction) {
|
|
804
|
+
case 'up':
|
|
805
|
+
deltaY = -amount;
|
|
806
|
+
break;
|
|
807
|
+
case 'down':
|
|
808
|
+
deltaY = amount;
|
|
809
|
+
break;
|
|
810
|
+
case 'left':
|
|
811
|
+
deltaX = -amount;
|
|
812
|
+
break;
|
|
813
|
+
case 'right':
|
|
814
|
+
deltaX = amount;
|
|
815
|
+
break;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
749
818
|
if (command.selector) {
|
|
750
819
|
const element = browser.getLocator(command.selector);
|
|
751
820
|
await element.scrollIntoViewIfNeeded();
|
|
752
|
-
if (
|
|
821
|
+
if (hasExplicitDelta || deltaX !== 0 || deltaY !== 0) {
|
|
753
822
|
await element.evaluate((el, { x, y }) => {
|
|
754
|
-
el.scrollBy(x
|
|
755
|
-
}, { x:
|
|
823
|
+
el.scrollBy(x, y);
|
|
824
|
+
}, { x: deltaX, y: deltaY });
|
|
756
825
|
}
|
|
757
826
|
}
|
|
758
827
|
else {
|
|
759
|
-
// Scroll the page
|
|
760
|
-
let deltaX = command.x ?? 0;
|
|
761
|
-
let deltaY = command.y ?? 0;
|
|
762
|
-
if (command.direction) {
|
|
763
|
-
const amount = command.amount ?? 100;
|
|
764
|
-
switch (command.direction) {
|
|
765
|
-
case 'up':
|
|
766
|
-
deltaY = -amount;
|
|
767
|
-
break;
|
|
768
|
-
case 'down':
|
|
769
|
-
deltaY = amount;
|
|
770
|
-
break;
|
|
771
|
-
case 'left':
|
|
772
|
-
deltaX = -amount;
|
|
773
|
-
break;
|
|
774
|
-
case 'right':
|
|
775
|
-
deltaX = amount;
|
|
776
|
-
break;
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
828
|
await page.evaluate(`window.scrollBy(${deltaX}, ${deltaY})`);
|
|
780
829
|
}
|
|
781
830
|
return successResponse(command.id, { scrolled: true });
|
|
@@ -993,9 +1042,16 @@ async function handleCookiesGet(command, browser) {
|
|
|
993
1042
|
async function handleCookiesSet(command, browser) {
|
|
994
1043
|
const page = browser.getPage();
|
|
995
1044
|
const context = page.context();
|
|
996
|
-
//
|
|
1045
|
+
// Playwright requires either `url` or a complete `domain` + `path` pair.
|
|
1046
|
+
// If none are provided, we default to the current page URL.
|
|
997
1047
|
const pageUrl = page.url();
|
|
998
1048
|
const cookies = command.cookies.map((cookie) => {
|
|
1049
|
+
const hasUrl = Boolean(cookie.url);
|
|
1050
|
+
const hasDomain = Boolean(cookie.domain);
|
|
1051
|
+
const hasPath = Boolean(cookie.path);
|
|
1052
|
+
if (!hasUrl && hasDomain !== hasPath) {
|
|
1053
|
+
throw new Error(`Invalid cookie "${cookie.name}": provide either url, or both domain and path`);
|
|
1054
|
+
}
|
|
999
1055
|
if (!cookie.url && !cookie.domain && !cookie.path) {
|
|
1000
1056
|
return { ...cookie, url: pageUrl };
|
|
1001
1057
|
}
|