@jshookmcp/jshook 0.1.6 → 0.1.8
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/LICENSE +661 -661
- package/README.md +145 -100
- package/README.zh.md +81 -36
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +3 -1
- package/dist/index.js +0 -0
- package/dist/modules/analyzer/QualityAnalyzer.js +1 -1
- package/dist/modules/browser/BrowserDiscovery.js +2 -2
- package/dist/modules/browser/BrowserModeManager.js +3 -3
- package/dist/modules/captcha/AICaptchaDetector.d.ts +12 -16
- package/dist/modules/captcha/AICaptchaDetector.js +229 -209
- package/dist/modules/captcha/CaptchaDetector.constants.d.ts +2 -0
- package/dist/modules/captcha/CaptchaDetector.constants.js +116 -25
- package/dist/modules/captcha/CaptchaDetector.d.ts +2 -11
- package/dist/modules/captcha/CaptchaDetector.js +102 -51
- package/dist/modules/captcha/types.d.ts +46 -0
- package/dist/modules/captcha/types.js +52 -0
- package/dist/modules/deobfuscator/AdvancedDeobfuscator.d.ts +15 -20
- package/dist/modules/deobfuscator/AdvancedDeobfuscator.js +66 -234
- package/dist/modules/deobfuscator/Deobfuscator.d.ts +3 -10
- package/dist/modules/deobfuscator/Deobfuscator.js +125 -404
- package/dist/modules/deobfuscator/webcrack.d.ts +13 -0
- package/dist/modules/deobfuscator/webcrack.js +164 -0
- package/dist/modules/detector/ObfuscationDetector.d.ts +6 -0
- package/dist/modules/detector/ObfuscationDetector.js +53 -2
- package/dist/modules/hook/AIHookGenerator.js +1 -1
- package/dist/modules/process/MacProcessManager.js +25 -25
- package/dist/modules/process/memory/availability.js +49 -49
- package/dist/modules/process/memory/injector.js +185 -185
- package/dist/modules/process/memory/reader.js +50 -50
- package/dist/modules/process/memory/scanner.js +165 -165
- package/dist/modules/process/memory/writer.js +55 -55
- package/dist/native/scripts/linux/enum-windows.sh +12 -12
- package/dist/native/scripts/macos/enum-windows.applescript +22 -22
- package/dist/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
- package/dist/native/scripts/windows/enum-windows.ps1 +44 -44
- package/dist/native/scripts/windows/inject-dll.ps1 +21 -21
- package/dist/server/domains/analysis/definitions.js +223 -2
- package/dist/server/domains/analysis/handlers.impl.d.ts +2 -3
- package/dist/server/domains/analysis/handlers.impl.js +60 -15
- package/dist/server/domains/analysis/manifest.js +2 -5
- package/dist/server/domains/browser/definitions.tools.behavior.js +36 -24
- package/dist/server/domains/browser/definitions.tools.page-core.js +53 -53
- package/dist/server/domains/browser/definitions.tools.runtime.js +40 -40
- package/dist/server/domains/browser/definitions.tools.security.js +80 -77
- package/dist/server/domains/browser/handlers/camoufox-flow.js +0 -1
- package/dist/server/domains/browser/handlers/captcha-solver.d.ts +1 -1
- package/dist/server/domains/browser/handlers/captcha-solver.js +121 -54
- package/dist/server/domains/browser/handlers/page-navigation.js +0 -2
- package/dist/server/domains/browser/handlers.impl.d.ts +1 -1
- package/dist/server/domains/browser/handlers.impl.js +3 -3
- package/dist/server/domains/browser/manifest.js +1 -1
- package/dist/server/domains/shared/modules.d.ts +1 -0
- package/dist/server/domains/transform/handlers.impl.transform-base.js +102 -102
- package/dist/server/domains/workflow/handlers.impl.workflow-base.js +51 -51
- package/dist/types/deobfuscator.d.ts +43 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/config.js +19 -10
- package/package.json +30 -44
- package/scripts/postinstall.cjs +37 -0
- package/src/native/scripts/linux/enum-windows.sh +12 -12
- package/src/native/scripts/macos/enum-windows.applescript +22 -22
- package/src/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
- package/src/native/scripts/windows/enum-windows.ps1 +44 -44
- package/src/native/scripts/windows/inject-dll.ps1 +21 -21
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logger } from '../../../../utils/logger.js';
|
|
2
|
-
import {
|
|
2
|
+
import { CAPTCHA_SOLVER_BASE_URL, CAPTCHA_SUBMIT_TIMEOUT_MS, CAPTCHA_POLL_INTERVAL_MS, CAPTCHA_RESULT_TIMEOUT_MS, CAPTCHA_DEFAULT_TIMEOUT_MS, CAPTCHA_MIN_TIMEOUT_MS, CAPTCHA_MAX_TIMEOUT_MS, CAPTCHA_MAX_RETRIES, CAPTCHA_DEFAULT_RETRIES, } from '../../../../constants.js';
|
|
3
3
|
function sleep(ms) {
|
|
4
4
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
5
5
|
}
|
|
@@ -14,15 +14,64 @@ function toErrorResponse(tool, error, extra = {}) {
|
|
|
14
14
|
...extra,
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
|
+
function normalizeSolverMode(rawMode) {
|
|
18
|
+
const value = typeof rawMode === 'string' ? rawMode.toLowerCase() : '';
|
|
19
|
+
if (value === 'hook')
|
|
20
|
+
return 'hook';
|
|
21
|
+
if (value === 'external_service')
|
|
22
|
+
return 'external_service';
|
|
23
|
+
if (value === '2captcha' || value === 'anticaptcha' || value === 'capsolver') {
|
|
24
|
+
return 'external_service';
|
|
25
|
+
}
|
|
26
|
+
return 'manual';
|
|
27
|
+
}
|
|
28
|
+
function normalizeChallengeTypeHint(rawType) {
|
|
29
|
+
const value = typeof rawType === 'string' ? rawType.toLowerCase() : '';
|
|
30
|
+
if (value === 'image')
|
|
31
|
+
return 'image';
|
|
32
|
+
if (value === 'widget' ||
|
|
33
|
+
value === 'recaptcha_v2' ||
|
|
34
|
+
value === 'recaptcha_v3' ||
|
|
35
|
+
value === 'hcaptcha' ||
|
|
36
|
+
value === 'funcaptcha' ||
|
|
37
|
+
value === 'turnstile') {
|
|
38
|
+
return 'widget';
|
|
39
|
+
}
|
|
40
|
+
if (value === 'browser_check' || value === 'managed_widget') {
|
|
41
|
+
return 'browser_check';
|
|
42
|
+
}
|
|
43
|
+
return 'auto';
|
|
44
|
+
}
|
|
45
|
+
function resolveLegacyServiceOverride(rawProvider) {
|
|
46
|
+
if (typeof rawProvider !== 'string' || !rawProvider.trim()) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
return rawProvider.trim().toLowerCase();
|
|
50
|
+
}
|
|
51
|
+
function resolveExternalServiceName(args) {
|
|
52
|
+
const legacyOverride = resolveLegacyServiceOverride(args.provider);
|
|
53
|
+
const configured = (process.env.CAPTCHA_PROVIDER || '').trim().toLowerCase();
|
|
54
|
+
return legacyOverride || configured || '2captcha';
|
|
55
|
+
}
|
|
17
56
|
async function solveWith2Captcha(apiKey, params, timeoutMs) {
|
|
18
57
|
const start = Date.now();
|
|
19
|
-
const baseUrl =
|
|
58
|
+
const baseUrl = CAPTCHA_SOLVER_BASE_URL;
|
|
59
|
+
if (!baseUrl) {
|
|
60
|
+
throw new Error('CAPTCHA_SOLVER_BASE_URL must be configured before using external_service mode.');
|
|
61
|
+
}
|
|
20
62
|
const submitBody = {
|
|
21
63
|
key: apiKey,
|
|
22
64
|
json: 1,
|
|
23
65
|
};
|
|
24
|
-
if (params.
|
|
25
|
-
|
|
66
|
+
if (params.taskKind === 'turnstile' ||
|
|
67
|
+
params.taskKind === 'recaptcha_v2' ||
|
|
68
|
+
params.taskKind === 'hcaptcha') {
|
|
69
|
+
submitBody.method =
|
|
70
|
+
params.taskKind === 'turnstile'
|
|
71
|
+
? 'turnstile'
|
|
72
|
+
: params.taskKind === 'hcaptcha'
|
|
73
|
+
? 'hcaptcha'
|
|
74
|
+
: 'userrecaptcha';
|
|
26
75
|
submitBody.sitekey = params.siteKey;
|
|
27
76
|
submitBody.pageurl = params.pageUrl;
|
|
28
77
|
}
|
|
@@ -61,8 +110,8 @@ async function solveWith2Captcha(apiKey, params, timeoutMs) {
|
|
|
61
110
|
if (resultData.status === 1) {
|
|
62
111
|
return {
|
|
63
112
|
token: resultData.request,
|
|
64
|
-
|
|
65
|
-
|
|
113
|
+
challengeType: params.taskKind === 'image' ? 'image' : 'widget',
|
|
114
|
+
mode: 'external_service',
|
|
66
115
|
durationMs: Date.now() - start,
|
|
67
116
|
};
|
|
68
117
|
}
|
|
@@ -76,73 +125,87 @@ export async function handleCaptchaVisionSolve(args, collector) {
|
|
|
76
125
|
const page = await collector.getActivePage();
|
|
77
126
|
if (!page)
|
|
78
127
|
throw new Error('No active page.');
|
|
79
|
-
const
|
|
128
|
+
const mode = normalizeSolverMode(args.mode ?? args.provider ?? process.env.CAPTCHA_PROVIDER);
|
|
129
|
+
const externalService = resolveExternalServiceName(args);
|
|
80
130
|
const apiKey = args.apiKey || process.env.CAPTCHA_API_KEY || '';
|
|
81
|
-
const
|
|
131
|
+
const challengeTypeHint = normalizeChallengeTypeHint(args.challengeType ?? args.typeHint);
|
|
82
132
|
const timeoutMs = Math.min(Math.max(args.timeoutMs ?? CAPTCHA_DEFAULT_TIMEOUT_MS, CAPTCHA_MIN_TIMEOUT_MS), CAPTCHA_MAX_TIMEOUT_MS);
|
|
83
133
|
const maxRetries = Math.min(Math.max(args.maxRetries ?? CAPTCHA_DEFAULT_RETRIES, 0), CAPTCHA_MAX_RETRIES);
|
|
84
|
-
let
|
|
134
|
+
let challengeType = challengeTypeHint;
|
|
135
|
+
let taskKind = challengeTypeHint === 'image' ? 'image' : 'recaptcha_v2';
|
|
85
136
|
let siteKey = args.siteKey;
|
|
86
137
|
const pageUrl = args.pageUrl || page.url();
|
|
87
|
-
if (
|
|
138
|
+
if (challengeType === 'auto') {
|
|
88
139
|
const detected = await page.evaluate(() => {
|
|
89
140
|
if (document.querySelector('[data-sitekey]')) {
|
|
90
141
|
const el = document.querySelector('[data-sitekey]');
|
|
91
142
|
const sk = el?.getAttribute('data-sitekey') || '';
|
|
92
|
-
if (document.querySelector('.cf-turnstile'))
|
|
93
|
-
return {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
143
|
+
if (document.querySelector('.cf-turnstile')) {
|
|
144
|
+
return { challengeType: 'widget', taskKind: 'turnstile', siteKey: sk };
|
|
145
|
+
}
|
|
146
|
+
if (document.querySelector('.h-captcha')) {
|
|
147
|
+
return { challengeType: 'widget', taskKind: 'hcaptcha', siteKey: sk };
|
|
148
|
+
}
|
|
149
|
+
return { challengeType: 'widget', taskKind: 'recaptcha_v2', siteKey: sk };
|
|
150
|
+
}
|
|
151
|
+
if (document.querySelector('iframe[src*="recaptcha"]')) {
|
|
152
|
+
return { challengeType: 'widget', taskKind: 'recaptcha_v2', siteKey: '' };
|
|
153
|
+
}
|
|
154
|
+
if (document.querySelector('iframe[src*="hcaptcha"]')) {
|
|
155
|
+
return { challengeType: 'widget', taskKind: 'hcaptcha', siteKey: '' };
|
|
97
156
|
}
|
|
98
|
-
if (document.querySelector('
|
|
99
|
-
return {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (document.querySelector('.cf-turnstile'))
|
|
103
|
-
return { type: 'turnstile', siteKey: '' };
|
|
104
|
-
return { type: 'image', siteKey: '' };
|
|
157
|
+
if (document.querySelector('.cf-turnstile')) {
|
|
158
|
+
return { challengeType: 'widget', taskKind: 'turnstile', siteKey: '' };
|
|
159
|
+
}
|
|
160
|
+
return { challengeType: 'image', taskKind: 'image', siteKey: '' };
|
|
105
161
|
});
|
|
106
|
-
|
|
162
|
+
challengeType = detected.challengeType;
|
|
163
|
+
taskKind = detected.taskKind;
|
|
107
164
|
if (!siteKey && detected.siteKey)
|
|
108
165
|
siteKey = detected.siteKey;
|
|
109
166
|
}
|
|
110
|
-
if (
|
|
167
|
+
else if (challengeType === 'image') {
|
|
168
|
+
taskKind = 'image';
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
taskKind = 'recaptcha_v2';
|
|
172
|
+
}
|
|
173
|
+
if (mode === 'manual') {
|
|
111
174
|
return toTextResponse({
|
|
112
175
|
success: true,
|
|
113
176
|
mode: 'manual',
|
|
114
|
-
|
|
177
|
+
challengeType,
|
|
115
178
|
siteKey: siteKey ?? null,
|
|
116
179
|
instruction: 'Please solve the CAPTCHA manually in the browser, then continue.',
|
|
117
|
-
hint: '
|
|
180
|
+
hint: 'Configure an external solver service and CAPTCHA_API_KEY to automate this flow.',
|
|
118
181
|
});
|
|
119
182
|
}
|
|
120
183
|
if (!apiKey) {
|
|
121
|
-
return toErrorResponse('captcha_vision_solve', new Error(
|
|
184
|
+
return toErrorResponse('captcha_vision_solve', new Error('External solver credentials are required. Set CAPTCHA_API_KEY.'));
|
|
122
185
|
}
|
|
123
186
|
let lastError = null;
|
|
124
187
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
125
188
|
try {
|
|
126
189
|
let result;
|
|
127
|
-
if (
|
|
190
|
+
if (externalService === '2captcha') {
|
|
128
191
|
result = await solveWith2Captcha(apiKey, {
|
|
129
|
-
|
|
192
|
+
taskKind,
|
|
130
193
|
siteKey,
|
|
131
194
|
pageUrl,
|
|
132
195
|
}, timeoutMs);
|
|
133
196
|
}
|
|
134
|
-
else if (
|
|
135
|
-
throw new Error(
|
|
136
|
-
|
|
197
|
+
else if (externalService === 'anticaptcha' || externalService === 'capsolver') {
|
|
198
|
+
throw new Error('The selected external solver service is not yet implemented. ' +
|
|
199
|
+
'Currently only the configured primary service and manual mode are supported.');
|
|
137
200
|
}
|
|
138
201
|
else {
|
|
139
|
-
throw new Error(
|
|
202
|
+
throw new Error('Unsupported external solver service.');
|
|
140
203
|
}
|
|
141
204
|
return toTextResponse({
|
|
142
205
|
success: true,
|
|
143
206
|
token: result.token,
|
|
144
|
-
|
|
145
|
-
|
|
207
|
+
challengeType: result.challengeType,
|
|
208
|
+
mode: result.mode,
|
|
146
209
|
durationMs: result.durationMs,
|
|
147
210
|
attempt: attempt + 1,
|
|
148
211
|
});
|
|
@@ -153,17 +216,18 @@ export async function handleCaptchaVisionSolve(args, collector) {
|
|
|
153
216
|
}
|
|
154
217
|
}
|
|
155
218
|
return toErrorResponse('captcha_vision_solve', lastError ?? new Error('All attempts failed'), {
|
|
156
|
-
|
|
157
|
-
|
|
219
|
+
challengeType,
|
|
220
|
+
mode,
|
|
158
221
|
maxRetries,
|
|
159
|
-
suggestion: 'Try
|
|
222
|
+
suggestion: 'Try manual mode or adjust the external solver configuration.',
|
|
160
223
|
});
|
|
161
224
|
}
|
|
162
|
-
export async function
|
|
225
|
+
export async function handleWidgetChallengeSolve(args, collector) {
|
|
163
226
|
const page = await collector.getActivePage();
|
|
164
227
|
if (!page)
|
|
165
228
|
throw new Error('No active page.');
|
|
166
|
-
const
|
|
229
|
+
const mode = normalizeSolverMode(args.mode ?? args.provider ?? process.env.CAPTCHA_PROVIDER);
|
|
230
|
+
const externalService = resolveExternalServiceName(args);
|
|
167
231
|
const apiKey = args.apiKey || process.env.CAPTCHA_API_KEY || '';
|
|
168
232
|
const timeoutMs = Math.min(Math.max(args.timeoutMs ?? 120_000, 5_000), 600_000);
|
|
169
233
|
const injectToken = args.injectToken ?? true;
|
|
@@ -176,9 +240,9 @@ export async function handleTurnstileSolve(args, collector) {
|
|
|
176
240
|
}) || undefined;
|
|
177
241
|
}
|
|
178
242
|
if (!siteKey) {
|
|
179
|
-
return toErrorResponse('
|
|
243
|
+
return toErrorResponse('widget_challenge_solve', new Error('Could not detect the widget siteKey. Provide it manually or ensure the page exposes a site key.'));
|
|
180
244
|
}
|
|
181
|
-
if (
|
|
245
|
+
if (mode === 'hook') {
|
|
182
246
|
const hookTimeoutMs = Math.min(timeoutMs, 30_000);
|
|
183
247
|
const token = await page.evaluate((hookTimeout) => {
|
|
184
248
|
return new Promise((resolve, reject) => {
|
|
@@ -195,7 +259,7 @@ export async function handleTurnstileSolve(args, collector) {
|
|
|
195
259
|
}
|
|
196
260
|
else {
|
|
197
261
|
clearTimeout(timeout);
|
|
198
|
-
reject(new Error('No
|
|
262
|
+
reject(new Error('No widget callbacks found. Try external_service mode instead.'));
|
|
199
263
|
}
|
|
200
264
|
});
|
|
201
265
|
}, hookTimeoutMs).catch(() => null);
|
|
@@ -204,29 +268,31 @@ export async function handleTurnstileSolve(args, collector) {
|
|
|
204
268
|
success: true,
|
|
205
269
|
token,
|
|
206
270
|
method: 'hook',
|
|
271
|
+
challengeType: 'widget',
|
|
207
272
|
siteKey,
|
|
208
273
|
});
|
|
209
274
|
}
|
|
210
275
|
}
|
|
211
|
-
if (
|
|
276
|
+
if (mode === 'manual') {
|
|
212
277
|
return toTextResponse({
|
|
213
278
|
success: true,
|
|
214
279
|
mode: 'manual',
|
|
280
|
+
challengeType: 'widget',
|
|
215
281
|
siteKey,
|
|
216
282
|
pageUrl,
|
|
217
|
-
instruction: 'Please complete the
|
|
283
|
+
instruction: 'Please complete the widget challenge manually.',
|
|
218
284
|
});
|
|
219
285
|
}
|
|
220
|
-
if (
|
|
221
|
-
return toErrorResponse('
|
|
222
|
-
|
|
286
|
+
if (externalService !== '2captcha') {
|
|
287
|
+
return toErrorResponse('widget_challenge_solve', new Error('The selected external solver service is not implemented for this widget flow. ' +
|
|
288
|
+
'Currently only the configured primary service, manual mode, and hook mode are supported.'));
|
|
223
289
|
}
|
|
224
290
|
if (!apiKey) {
|
|
225
|
-
return toErrorResponse('
|
|
291
|
+
return toErrorResponse('widget_challenge_solve', new Error('External solver credentials are required.'));
|
|
226
292
|
}
|
|
227
293
|
try {
|
|
228
294
|
const result = await solveWith2Captcha(apiKey, {
|
|
229
|
-
|
|
295
|
+
taskKind: 'turnstile',
|
|
230
296
|
siteKey,
|
|
231
297
|
pageUrl,
|
|
232
298
|
}, timeoutMs);
|
|
@@ -244,17 +310,18 @@ export async function handleTurnstileSolve(args, collector) {
|
|
|
244
310
|
return toTextResponse({
|
|
245
311
|
success: true,
|
|
246
312
|
token: result.token,
|
|
313
|
+
challengeType: result.challengeType,
|
|
247
314
|
siteKey,
|
|
248
|
-
|
|
315
|
+
mode: result.mode,
|
|
249
316
|
durationMs: result.durationMs,
|
|
250
317
|
injected: injectToken,
|
|
251
318
|
});
|
|
252
319
|
}
|
|
253
320
|
catch (error) {
|
|
254
|
-
return toErrorResponse('
|
|
321
|
+
return toErrorResponse('widget_challenge_solve', error, {
|
|
255
322
|
siteKey,
|
|
256
|
-
|
|
257
|
-
suggestion: 'Try
|
|
323
|
+
mode,
|
|
324
|
+
suggestion: 'Try manual mode or hook mode.',
|
|
258
325
|
});
|
|
259
326
|
}
|
|
260
327
|
}
|
|
@@ -23,7 +23,6 @@ export class PageNavigationHandlers {
|
|
|
23
23
|
text: JSON.stringify({
|
|
24
24
|
success: true,
|
|
25
25
|
driver: 'camoufox',
|
|
26
|
-
captcha_detected: false,
|
|
27
26
|
url: page.url(),
|
|
28
27
|
title: await page.title(),
|
|
29
28
|
network_monitoring: {
|
|
@@ -51,7 +50,6 @@ export class PageNavigationHandlers {
|
|
|
51
50
|
type: 'text',
|
|
52
51
|
text: JSON.stringify({
|
|
53
52
|
success: true,
|
|
54
|
-
captcha_detected: false,
|
|
55
53
|
url: currentUrl,
|
|
56
54
|
title,
|
|
57
55
|
network_monitoring: {
|
|
@@ -394,6 +394,6 @@ export declare class BrowserToolHandlers {
|
|
|
394
394
|
handleHumanScroll(args: Record<string, unknown>): Promise<unknown>;
|
|
395
395
|
handleHumanTyping(args: Record<string, unknown>): Promise<unknown>;
|
|
396
396
|
handleCaptchaVisionSolve(args: Record<string, unknown>): Promise<unknown>;
|
|
397
|
-
|
|
397
|
+
handleWidgetChallengeSolve(args: Record<string, unknown>): Promise<unknown>;
|
|
398
398
|
}
|
|
399
399
|
export { BrowserControlHandlers, CamoufoxBrowserHandlers, PageNavigationHandlers, PageInteractionHandlers, PageEvaluationHandlers, PageDataHandlers, DOMQueryHandlers, DOMStyleHandlers, DOMSearchHandlers, ConsoleHandlers, ScriptManagementHandlers, CaptchaHandlers, StealthInjectionHandlers, FrameworkStateHandlers, IndexedDBDumpHandlers, DetailedDataHandlers };
|
|
@@ -20,7 +20,7 @@ import { IndexedDBDumpHandlers } from '../../domains/browser/handlers/indexeddb-
|
|
|
20
20
|
import { DetailedDataHandlers } from '../../domains/browser/handlers/detailed-data.js';
|
|
21
21
|
import { initializeBrowserHandlerModules } from '../../domains/browser/handlers/facade-initializer.js';
|
|
22
22
|
import { handleHumanMouse, handleHumanScroll, handleHumanTyping, } from '../../domains/browser/handlers/human-behavior.js';
|
|
23
|
-
import { handleCaptchaVisionSolve,
|
|
23
|
+
import { handleCaptchaVisionSolve, handleWidgetChallengeSolve, } from '../../domains/browser/handlers/captcha-solver.js';
|
|
24
24
|
import { handleCamoufoxLaunchFlow, handleCamoufoxNavigateFlow, } from '../../domains/browser/handlers/camoufox-flow.js';
|
|
25
25
|
export class BrowserToolHandlers {
|
|
26
26
|
collector;
|
|
@@ -381,8 +381,8 @@ export class BrowserToolHandlers {
|
|
|
381
381
|
async handleCaptchaVisionSolve(args) {
|
|
382
382
|
return handleCaptchaVisionSolve(args, this.collector);
|
|
383
383
|
}
|
|
384
|
-
async
|
|
385
|
-
return
|
|
384
|
+
async handleWidgetChallengeSolve(args) {
|
|
385
|
+
return handleWidgetChallengeSolve(args, this.collector);
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
388
|
export { BrowserControlHandlers, CamoufoxBrowserHandlers, PageNavigationHandlers, PageInteractionHandlers, PageEvaluationHandlers, PageDataHandlers, DOMQueryHandlers, DOMStyleHandlers, DOMSearchHandlers, ConsoleHandlers, ScriptManagementHandlers, CaptchaHandlers, StealthInjectionHandlers, FrameworkStateHandlers, IndexedDBDumpHandlers, DetailedDataHandlers };
|
|
@@ -79,7 +79,7 @@ const manifest = {
|
|
|
79
79
|
{ tool: t('human_scroll'), domain: DOMAIN, bind: b((h, a) => h.handleHumanScroll(a)) },
|
|
80
80
|
{ tool: t('human_typing'), domain: DOMAIN, bind: b((h, a) => h.handleHumanTyping(a)) },
|
|
81
81
|
{ tool: t('captcha_vision_solve'), domain: DOMAIN, bind: b((h, a) => h.handleCaptchaVisionSolve(a)) },
|
|
82
|
-
{ tool: t('
|
|
82
|
+
{ tool: t('widget_challenge_solve'), domain: DOMAIN, bind: b((h, a) => h.handleWidgetChallengeSolve(a)) },
|
|
83
83
|
],
|
|
84
84
|
};
|
|
85
85
|
export default manifest;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { CodeAnalyzer } from '../../../modules/analyzer/CodeAnalyzer.js';
|
|
2
2
|
export { CamoufoxBrowserManager } from '../../../modules/browser/CamoufoxBrowserManager.js';
|
|
3
3
|
export { AICaptchaDetector } from '../../../modules/captcha/AICaptchaDetector.js';
|
|
4
|
+
export type { CaptchaType, CaptchaProviderHint, CaptchaDetectionResult, AICaptchaDetectionResult, CaptchaDetectionConfig, } from '../../../modules/captcha/types.js';
|
|
4
5
|
export { CodeCollector } from '../../../modules/collector/CodeCollector.js';
|
|
5
6
|
export { DOMInspector } from '../../../modules/collector/DOMInspector.js';
|
|
6
7
|
export { PageController } from '../../../modules/collector/PageController.js';
|
|
@@ -28,98 +28,98 @@ export const CRYPTO_KEYWORDS = [
|
|
|
28
28
|
'aes',
|
|
29
29
|
'rsa',
|
|
30
30
|
];
|
|
31
|
-
const CRYPTO_TEST_WORKER_SCRIPT = `
|
|
32
|
-
const __bootstrap = async () => {
|
|
33
|
-
const [workerThreads, vm, perfHooks] = await Promise.all([
|
|
34
|
-
import('node:worker_threads'),
|
|
35
|
-
import('node:vm'),
|
|
36
|
-
import('node:perf_hooks'),
|
|
37
|
-
]);
|
|
38
|
-
|
|
39
|
-
const parentPort = workerThreads.parentPort;
|
|
40
|
-
const performance = perfHooks.performance;
|
|
41
|
-
|
|
42
|
-
if (!parentPort) {
|
|
43
|
-
throw new Error('worker parentPort is unavailable');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function normalizeOutput(value) {
|
|
47
|
-
if (value === undefined) return '__undefined__';
|
|
48
|
-
if (value === null) return 'null';
|
|
49
|
-
if (typeof value === 'string') return value;
|
|
50
|
-
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') return String(value);
|
|
51
|
-
try { return JSON.stringify(value); } catch { return String(value); }
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
parentPort.on('message', async (msg) => {
|
|
55
|
-
const { jobId, payload } = msg;
|
|
56
|
-
try {
|
|
57
|
-
const { code, functionName, testInputs } = payload;
|
|
58
|
-
const sandbox = {
|
|
59
|
-
console: { log() {}, warn() {}, error() {} },
|
|
60
|
-
Buffer,
|
|
61
|
-
TextEncoder,
|
|
62
|
-
TextDecoder,
|
|
63
|
-
atob: (v) => Buffer.from(String(v), 'base64').toString('binary'),
|
|
64
|
-
btoa: (v) => Buffer.from(String(v), 'binary').toString('base64'),
|
|
65
|
-
};
|
|
66
|
-
sandbox.globalThis = sandbox;
|
|
67
|
-
const context = vm.createContext(sandbox);
|
|
68
|
-
|
|
69
|
-
const isValidIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(functionName);
|
|
70
|
-
const bindCode = isValidIdentifier
|
|
71
|
-
? "\\n;globalThis.__targetFn = (typeof " + functionName + " !== 'undefined' ? " + functionName + " : globalThis[" + JSON.stringify(functionName) + "]);"
|
|
72
|
-
: "\\n;globalThis.__targetFn = globalThis[" + JSON.stringify(functionName) + "];";
|
|
73
|
-
|
|
74
|
-
const script = new vm.Script(code + bindCode, { timeout: 5000 });
|
|
75
|
-
script.runInContext(context, { timeout: 5000 });
|
|
76
|
-
|
|
77
|
-
const targetFn = context.__targetFn;
|
|
78
|
-
if (typeof targetFn !== 'function') {
|
|
79
|
-
throw new Error("Function not found or not callable: " + functionName);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const rows = [];
|
|
83
|
-
for (const input of testInputs) {
|
|
84
|
-
const started = performance.now();
|
|
85
|
-
try {
|
|
86
|
-
const raw = targetFn(input);
|
|
87
|
-
const resolved = raw && typeof raw.then === 'function' ? await raw : raw;
|
|
88
|
-
rows.push({
|
|
89
|
-
input,
|
|
90
|
-
output: normalizeOutput(resolved),
|
|
91
|
-
duration: Number((performance.now() - started).toFixed(3)),
|
|
92
|
-
});
|
|
93
|
-
} catch (err) {
|
|
94
|
-
rows.push({
|
|
95
|
-
input,
|
|
96
|
-
output: '',
|
|
97
|
-
error: err && err.message ? err.message : String(err),
|
|
98
|
-
duration: Number((performance.now() - started).toFixed(3)),
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
parentPort.postMessage({ jobId, ok: true, result: { ok: true, results: rows } });
|
|
104
|
-
} catch (error) {
|
|
105
|
-
parentPort.postMessage({
|
|
106
|
-
jobId,
|
|
107
|
-
ok: true,
|
|
108
|
-
result: {
|
|
109
|
-
ok: false,
|
|
110
|
-
error: error && error.message ? error.message : String(error),
|
|
111
|
-
results: [],
|
|
112
|
-
},
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
__bootstrap().catch((error) => {
|
|
119
|
-
if (typeof console !== 'undefined' && typeof console.error === 'function') {
|
|
120
|
-
console.error('crypto harness worker bootstrap failed:', error && error.message ? error.message : String(error));
|
|
121
|
-
}
|
|
122
|
-
});
|
|
31
|
+
const CRYPTO_TEST_WORKER_SCRIPT = `
|
|
32
|
+
const __bootstrap = async () => {
|
|
33
|
+
const [workerThreads, vm, perfHooks] = await Promise.all([
|
|
34
|
+
import('node:worker_threads'),
|
|
35
|
+
import('node:vm'),
|
|
36
|
+
import('node:perf_hooks'),
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const parentPort = workerThreads.parentPort;
|
|
40
|
+
const performance = perfHooks.performance;
|
|
41
|
+
|
|
42
|
+
if (!parentPort) {
|
|
43
|
+
throw new Error('worker parentPort is unavailable');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizeOutput(value) {
|
|
47
|
+
if (value === undefined) return '__undefined__';
|
|
48
|
+
if (value === null) return 'null';
|
|
49
|
+
if (typeof value === 'string') return value;
|
|
50
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') return String(value);
|
|
51
|
+
try { return JSON.stringify(value); } catch { return String(value); }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
parentPort.on('message', async (msg) => {
|
|
55
|
+
const { jobId, payload } = msg;
|
|
56
|
+
try {
|
|
57
|
+
const { code, functionName, testInputs } = payload;
|
|
58
|
+
const sandbox = {
|
|
59
|
+
console: { log() {}, warn() {}, error() {} },
|
|
60
|
+
Buffer,
|
|
61
|
+
TextEncoder,
|
|
62
|
+
TextDecoder,
|
|
63
|
+
atob: (v) => Buffer.from(String(v), 'base64').toString('binary'),
|
|
64
|
+
btoa: (v) => Buffer.from(String(v), 'binary').toString('base64'),
|
|
65
|
+
};
|
|
66
|
+
sandbox.globalThis = sandbox;
|
|
67
|
+
const context = vm.createContext(sandbox);
|
|
68
|
+
|
|
69
|
+
const isValidIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(functionName);
|
|
70
|
+
const bindCode = isValidIdentifier
|
|
71
|
+
? "\\n;globalThis.__targetFn = (typeof " + functionName + " !== 'undefined' ? " + functionName + " : globalThis[" + JSON.stringify(functionName) + "]);"
|
|
72
|
+
: "\\n;globalThis.__targetFn = globalThis[" + JSON.stringify(functionName) + "];";
|
|
73
|
+
|
|
74
|
+
const script = new vm.Script(code + bindCode, { timeout: 5000 });
|
|
75
|
+
script.runInContext(context, { timeout: 5000 });
|
|
76
|
+
|
|
77
|
+
const targetFn = context.__targetFn;
|
|
78
|
+
if (typeof targetFn !== 'function') {
|
|
79
|
+
throw new Error("Function not found or not callable: " + functionName);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const rows = [];
|
|
83
|
+
for (const input of testInputs) {
|
|
84
|
+
const started = performance.now();
|
|
85
|
+
try {
|
|
86
|
+
const raw = targetFn(input);
|
|
87
|
+
const resolved = raw && typeof raw.then === 'function' ? await raw : raw;
|
|
88
|
+
rows.push({
|
|
89
|
+
input,
|
|
90
|
+
output: normalizeOutput(resolved),
|
|
91
|
+
duration: Number((performance.now() - started).toFixed(3)),
|
|
92
|
+
});
|
|
93
|
+
} catch (err) {
|
|
94
|
+
rows.push({
|
|
95
|
+
input,
|
|
96
|
+
output: '',
|
|
97
|
+
error: err && err.message ? err.message : String(err),
|
|
98
|
+
duration: Number((performance.now() - started).toFixed(3)),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
parentPort.postMessage({ jobId, ok: true, result: { ok: true, results: rows } });
|
|
104
|
+
} catch (error) {
|
|
105
|
+
parentPort.postMessage({
|
|
106
|
+
jobId,
|
|
107
|
+
ok: true,
|
|
108
|
+
result: {
|
|
109
|
+
ok: false,
|
|
110
|
+
error: error && error.message ? error.message : String(error),
|
|
111
|
+
results: [],
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
__bootstrap().catch((error) => {
|
|
119
|
+
if (typeof console !== 'undefined' && typeof console.error === 'function') {
|
|
120
|
+
console.error('crypto harness worker bootstrap failed:', error && error.message ? error.message : String(error));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
123
|
`;
|
|
124
124
|
export class TransformToolHandlersBase {
|
|
125
125
|
collector;
|
|
@@ -333,16 +333,16 @@ export class TransformToolHandlersBase {
|
|
|
333
333
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value);
|
|
334
334
|
}
|
|
335
335
|
buildCryptoPolyfills() {
|
|
336
|
-
return `
|
|
337
|
-
const __textEncoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null;
|
|
338
|
-
const __textDecoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
|
|
339
|
-
|
|
340
|
-
if (typeof globalThis.atob === 'undefined') {
|
|
341
|
-
globalThis.atob = (value) => Buffer.from(String(value), 'base64').toString('binary');
|
|
342
|
-
}
|
|
343
|
-
if (typeof globalThis.btoa === 'undefined') {
|
|
344
|
-
globalThis.btoa = (value) => Buffer.from(String(value), 'binary').toString('base64');
|
|
345
|
-
}
|
|
336
|
+
return `
|
|
337
|
+
const __textEncoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null;
|
|
338
|
+
const __textDecoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
|
|
339
|
+
|
|
340
|
+
if (typeof globalThis.atob === 'undefined') {
|
|
341
|
+
globalThis.atob = (value) => Buffer.from(String(value), 'base64').toString('binary');
|
|
342
|
+
}
|
|
343
|
+
if (typeof globalThis.btoa === 'undefined') {
|
|
344
|
+
globalThis.btoa = (value) => Buffer.from(String(value), 'binary').toString('base64');
|
|
345
|
+
}
|
|
346
346
|
`.trim();
|
|
347
347
|
}
|
|
348
348
|
async runCryptoHarness(code, functionName, testInputs) {
|