@jshookmcp/jshook 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -5
- package/README.zh.md +36 -5
- package/dist/{AntiCheatDetector-S8VRj-dD.mjs → AntiCheatDetector-CqGDXmfc.mjs} +160 -54
- package/dist/{CodeInjector-4Z3ngPoX.mjs → CodeInjector-BdjRfNx7.mjs} +5 -5
- package/dist/ConsoleMonitor-DykL3IAw.mjs +2269 -0
- package/dist/{DarwinAPI-B8hg_yhz.mjs → DarwinAPI-ETyy0xyo.mjs} +1 -1
- package/dist/DetailedDataManager-HT49OrvF.mjs +217 -0
- package/dist/EventBus-DFKvADm3.mjs +141 -0
- package/dist/EvidenceGraphBridge-318Oi0Lf.mjs +153 -0
- package/dist/{ExtensionManager-D5-bO9D8.mjs → ExtensionManager-BDMsY2Dz.mjs} +27 -13
- package/dist/{FingerprintManager-BVxFJL2-.mjs → FingerprintManager-BN4UQWnX.mjs} +1 -1
- package/dist/{HardwareBreakpoint-DK1yjWkV.mjs → HardwareBreakpoint-Cc2AFq1Y.mjs} +3 -3
- package/dist/{HeapAnalyzer-CEbo10xU.mjs → HeapAnalyzer-DruMgsgj.mjs} +21 -21
- package/dist/HookGeneratorBuilders.core.generators.storage-CTbB4Lcx.mjs +566 -0
- package/dist/InstrumentationSession-DLH0vd-z.mjs +244 -0
- package/dist/{MemoryController-DdtnBdD4.mjs → MemoryController-CMtviNW_.mjs} +3 -3
- package/dist/{MemoryScanSession-RMixN3bX.mjs → MemoryScanSession-ITgb_NMi.mjs} +81 -78
- package/dist/{MemoryScanner-QjK4ld0B.mjs → MemoryScanner-CiL7Z3ey.mjs} +50 -21
- package/dist/{NativeMemoryManager.impl-CB6gJ0NM.mjs → NativeMemoryManager.impl-D9Lkovvn.mjs} +20 -56
- package/dist/{NativeMemoryManager.utils-BML4q1ry.mjs → NativeMemoryManager.utils-BBlAixF5.mjs} +1 -1
- package/dist/{PEAnalyzer-CK0xe0Fs.mjs → PEAnalyzer-DMQ44gen.mjs} +16 -16
- package/dist/PageController-BPJNqqBN.mjs +431 -0
- package/dist/{PointerChainEngine-Cd73qu5b.mjs → PointerChainEngine-K7wN8Z-w.mjs} +10 -7
- package/dist/PrerequisiteError-TuyZIs6n.mjs +20 -0
- package/dist/ProcessRegistry-zGg12QbE.mjs +74 -0
- package/dist/ResponseBuilder-CJXWmWNw.mjs +143 -0
- package/dist/ReverseEvidenceGraph-C02-gXOh.mjs +269 -0
- package/dist/ScriptManager-ZuWD-0Jg.mjs +3003 -0
- package/dist/{Speedhack-CeF0XmEz.mjs → Speedhack-D-z0umeT.mjs} +2 -2
- package/dist/{StructureAnalyzer-D4GkMduU.mjs → StructureAnalyzer-Cav5AVSL.mjs} +9 -6
- package/dist/ToolCatalog-5OJdMiF0.mjs +582 -0
- package/dist/ToolError-jh9whhMd.mjs +15 -0
- package/dist/ToolProbe-DbCFGyrg.mjs +45 -0
- package/dist/ToolRegistry-B9krbTtI.mjs +180 -0
- package/dist/ToolRouter.policy-BGDAGyeH.mjs +344 -0
- package/dist/TraceRecorder-B41Z5XBj.mjs +1286 -0
- package/dist/{Win32API-Bc0QnQsN.mjs → Win32API-C2kjj0ze.mjs} +19 -13
- package/dist/{Win32Debug-DUHt9XUn.mjs → Win32Debug-CKrGOTpo.mjs} +3 -3
- package/dist/WorkflowEngine-DJ6M4opp.mjs +569 -0
- package/dist/analysis-BHeJW2Nb.mjs +1234 -0
- package/dist/antidebug-BRKeyt27.mjs +1081 -0
- package/dist/artifactRetention-CPXkUJXp.mjs +598 -0
- package/dist/artifacts-DkfosXH3.mjs +59 -0
- package/dist/authorization-schema-DRqyJMSk.mjs +31 -0
- package/dist/betterSqlite3-DLSBZodi.mjs +74 -0
- package/dist/binary-instrument--V3MAhJ4.mjs +971 -0
- package/dist/bind-helpers-ClV34xdn.mjs +42 -0
- package/dist/boringssl-inspector-Bo_LOLaS.mjs +180 -0
- package/dist/browser-Dx3_S2cG.mjs +4369 -0
- package/dist/capabilities-CcHlvWgK.mjs +33 -0
- package/dist/concurrency-Drev_Vz9.mjs +41 -0
- package/dist/{constants-CCvsN80K.mjs → constants-CDZLOoVv.mjs} +105 -48
- package/dist/coordination-DgItD9DL.mjs +259 -0
- package/dist/debugger-RS3RSAqs.mjs +1288 -0
- package/dist/definitions-BEoYofW5.mjs +47 -0
- package/dist/definitions-BRaefg3u.mjs +365 -0
- package/dist/definitions-BbkvZkiv.mjs +96 -0
- package/dist/definitions-BtWSHJ3o.mjs +17 -0
- package/dist/definitions-C1gCHO0i.mjs +43 -0
- package/dist/definitions-CDOg_b-l.mjs +138 -0
- package/dist/definitions-CVPD9hzZ.mjs +54 -0
- package/dist/definitions-Cea8Lgl7.mjs +94 -0
- package/dist/definitions-DAgIyjxM.mjs +10 -0
- package/dist/definitions-DJA27nsL.mjs +66 -0
- package/dist/definitions-DKPFU3LW.mjs +25 -0
- package/dist/definitions-DPRpZQ96.mjs +47 -0
- package/dist/definitions-DUE5gmdn.mjs +18 -0
- package/dist/definitions-DYVjOtxa.mjs +26 -0
- package/dist/definitions-DcYLVLCo.mjs +37 -0
- package/dist/definitions-Pp5LI2H4.mjs +27 -0
- package/dist/definitions-j9KdHVNR.mjs +14 -0
- package/dist/definitions-uzkjBwa7.mjs +258 -0
- package/dist/definitions-va-AnLuQ.mjs +28 -0
- package/dist/encoding-DJeqHmpd.mjs +1079 -0
- package/dist/evidence-graph-bridge-DcYizFk2.mjs +136 -0
- package/dist/{factory-CibqTNC8.mjs → factory-C90tBff6.mjs} +41 -56
- package/dist/flat-target-session-Dgax2Cy3.mjs +29 -0
- package/dist/graphql-CoHrhweh.mjs +1197 -0
- package/dist/handlers-4jmR0nMs.mjs +898 -0
- package/dist/handlers-BAHPxcch.mjs +789 -0
- package/dist/handlers-BOs9b907.mjs +2600 -0
- package/dist/handlers-BWXEy6ef.mjs +917 -0
- package/dist/handlers-Bndn6QvE.mjs +111 -0
- package/dist/handlers-BqC4bD4s.mjs +681 -0
- package/dist/handlers-BtYq60bM2.mjs +276 -0
- package/dist/handlers-BzgcB4iv.mjs +799 -0
- package/dist/handlers-CRyRWj2b.mjs +859 -0
- package/dist/handlers-CVv2H1uq.mjs +592 -0
- package/dist/handlers-Dl5a7JS4.mjs +572 -0
- package/dist/handlers-Dx2d7jt7.mjs +2537 -0
- package/dist/handlers-Dz9PYsCa.mjs +2805 -0
- package/dist/handlers-HujRKC3b.mjs +661 -0
- package/dist/handlers.impl-XWXkQfyi.mjs +807 -0
- package/dist/hooks-B1B8NRHL.mjs +898 -0
- package/dist/index.mjs +491 -259
- package/dist/{logger-BmWzC2lM.mjs → logger-Dh_xb7_2.mjs} +14 -6
- package/dist/maintenance-PRMkLVRW.mjs +835 -0
- package/dist/manifest-67Bok-Si.mjs +58 -0
- package/dist/manifest-6lNTMZAB2.mjs +87 -0
- package/dist/manifest-B2duEHiH.mjs +90 -0
- package/dist/manifest-B6EY9Vm8.mjs +57 -0
- package/dist/manifest-B6nKSbyY.mjs +95 -0
- package/dist/manifest-BL8AQNPF.mjs +106 -0
- package/dist/manifest-BSZvJJmV.mjs +47 -0
- package/dist/manifest-BU7qzUyX.mjs +418 -0
- package/dist/manifest-Bl62e8WK.mjs +49 -0
- package/dist/manifest-Bo5cXjdt.mjs +82 -0
- package/dist/manifest-BpS4gtUK.mjs +1347 -0
- package/dist/manifest-Bv65_e2W.mjs +101 -0
- package/dist/manifest-BytNIF4Z.mjs +117 -0
- package/dist/manifest-C-xtsjS3.mjs +81 -0
- package/dist/manifest-CDYl7OhA.mjs +66 -0
- package/dist/manifest-CRZ3xmkD.mjs +61 -0
- package/dist/manifest-CoW6u4Tp.mjs +132 -0
- package/dist/manifest-Cq5zN_8A.mjs +50 -0
- package/dist/manifest-D7YZM_2e.mjs +194 -0
- package/dist/manifest-DE_VrAeQ.mjs +314 -0
- package/dist/manifest-DGsXSCpT.mjs +39 -0
- package/dist/manifest-DJ2vfEuW.mjs +156 -0
- package/dist/manifest-DPXDYhEu.mjs +80 -0
- package/dist/manifest-Dd4fQb0a.mjs +322 -0
- package/dist/manifest-Deq6opGg.mjs +223 -0
- package/dist/manifest-DfJTafJK.mjs +37 -0
- package/dist/manifest-DgOdgN_j.mjs +50 -0
- package/dist/manifest-DlbMW4v4.mjs +47 -0
- package/dist/manifest-DmVfbH0w.mjs +374 -0
- package/dist/manifest-Dog6Ddjr.mjs +109 -0
- package/dist/manifest-DvgU5FWb.mjs +58 -0
- package/dist/manifest-HsfDBs7j.mjs +50 -0
- package/dist/manifest-I8oQHvCG.mjs +186 -0
- package/dist/manifest-NvH_a-av.mjs +786 -0
- package/dist/manifest-cEJU1v0Z.mjs +129 -0
- package/dist/manifest-wOl5XLB12.mjs +112 -0
- package/dist/modules-tZozf0LQ.mjs +10635 -0
- package/dist/mojo-ipc-DXNEXEqb.mjs +640 -0
- package/dist/network-CPVvwvFg.mjs +3852 -0
- package/dist/{artifacts-BbdOMET5.mjs → outputPaths-um7lCRY3.mjs} +219 -216
- package/dist/parse-args-B4cY5Vx5.mjs +39 -0
- package/dist/platform-CYeFoTWp.mjs +2161 -0
- package/dist/process-BTbgcVc6.mjs +1306 -0
- package/dist/proxy-r8YN6nP1.mjs +192 -0
- package/dist/registry-Bl8ZQW61.mjs +34 -0
- package/dist/response-CWhh2aLo.mjs +34 -0
- package/dist/server/plugin-api.mjs +2 -2
- package/dist/shared-state-board-BoZnSoj-.mjs +586 -0
- package/dist/sourcemap-BIDHUVXy.mjs +934 -0
- package/dist/ssrf-policy-Dsqd-DTX.mjs +166 -0
- package/dist/streaming-Dal6utPp.mjs +725 -0
- package/dist/tool-builder-BHJp32mV.mjs +186 -0
- package/dist/transform-DRVgGG90.mjs +1011 -0
- package/dist/types-Bx92KJfT.mjs +4 -0
- package/dist/wasm-BYx5UOeG.mjs +1044 -0
- package/dist/webcrack-Be0_FccV.mjs +747 -0
- package/dist/workflow-BpuKEtvn.mjs +725 -0
- package/package.json +82 -49
- package/dist/ExtensionManager-CPTJhHFg.mjs +0 -2
- package/dist/ToolCatalog-Bq4V2sbJ.mjs +0 -67201
- package/dist/{CacheAdapters-CzFNpD9a.mjs → CacheAdapters-jJFy20G-.mjs} +0 -0
- package/dist/{StealthVerifier-BzBCFiwx.mjs → StealthVerifier-BWmPgQsv.mjs} +0 -0
- package/dist/{VersionDetector-CNXcvD46.mjs → VersionDetector-K3V4vGsw.mjs} +0 -0
- package/dist/{formatAddress-ChCSIRWT.mjs → formatAddress-nnMvEohD.mjs} +0 -0
- package/dist/{types-BBjOqye-.mjs → types-DDBWs9UP.mjs} +1 -1
package/dist/{NativeMemoryManager.impl-CB6gJ0NM.mjs → NativeMemoryManager.impl-D9Lkovvn.mjs}
RENAMED
|
@@ -1,49 +1,10 @@
|
|
|
1
|
-
import { t as logger } from "./logger-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { t as logger } from "./logger-Dh_xb7_2.mjs";
|
|
2
|
+
import { v as isKoffiAvailable, y as isWindows } from "./Win32API-C2kjj0ze.mjs";
|
|
3
|
+
import { n as cpuLimit } from "./concurrency-Drev_Vz9.mjs";
|
|
4
|
+
import { t as createPlatformProvider } from "./factory-C90tBff6.mjs";
|
|
5
|
+
import { i as parsePattern, t as findPatternInBuffer } from "./NativeMemoryManager.utils-BBlAixF5.mjs";
|
|
5
6
|
import { exec } from "node:child_process";
|
|
6
7
|
import { promisify } from "node:util";
|
|
7
|
-
//#region src/utils/concurrency.ts
|
|
8
|
-
function pLimit(concurrency) {
|
|
9
|
-
if (concurrency < 1) throw new RangeError("concurrency must be >= 1");
|
|
10
|
-
let activeCount = 0;
|
|
11
|
-
const queue = [];
|
|
12
|
-
function next() {
|
|
13
|
-
if (queue.length > 0 && activeCount < concurrency) {
|
|
14
|
-
activeCount++;
|
|
15
|
-
queue.shift()();
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
function run(fn) {
|
|
19
|
-
return new Promise((resolve, reject) => {
|
|
20
|
-
const execute = async () => {
|
|
21
|
-
try {
|
|
22
|
-
resolve(await fn());
|
|
23
|
-
} catch (err) {
|
|
24
|
-
reject(err);
|
|
25
|
-
} finally {
|
|
26
|
-
activeCount--;
|
|
27
|
-
next();
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
if (activeCount < concurrency) {
|
|
31
|
-
activeCount++;
|
|
32
|
-
execute();
|
|
33
|
-
} else queue.push(() => {
|
|
34
|
-
execute();
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
return run;
|
|
39
|
-
}
|
|
40
|
-
/** External CLI calls, HAR export, large file I/O */
|
|
41
|
-
const ioLimit = pLimit(parseInt(process.env.jshook_IO_CONCURRENCY || "4", 10));
|
|
42
|
-
/** CPU-heavy: AST parsing, deobfuscation, binary decoding */
|
|
43
|
-
const cpuLimit = pLimit(parseInt(process.env.jshook_CPU_CONCURRENCY || "2", 10));
|
|
44
|
-
/** CDP-heavy: heap snapshots, traces, profiling */
|
|
45
|
-
const cdpLimit = pLimit(parseInt(process.env.jshook_CDP_CONCURRENCY || "2", 10));
|
|
46
|
-
//#endregion
|
|
47
8
|
//#region src/native/NativeMemoryManager.availability.ts
|
|
48
9
|
async function checkNativeMemoryAvailability(execAsync) {
|
|
49
10
|
if (process.platform === "darwin") return checkDarwinAvailability(execAsync);
|
|
@@ -135,11 +96,14 @@ function scanRegionInChunks(region, patternBytes, mask, readChunk, chunkSize = S
|
|
|
135
96
|
* Win32-only methods (injection, debug) are guarded by platform checks.
|
|
136
97
|
*/
|
|
137
98
|
var NativeMemoryManager = class {
|
|
138
|
-
|
|
99
|
+
providerCache = null;
|
|
139
100
|
/** Lazily create the platform memory provider */
|
|
140
101
|
get provider() {
|
|
141
|
-
if (!this.
|
|
142
|
-
return this.
|
|
102
|
+
if (!this.providerCache) this.providerCache = createPlatformProvider();
|
|
103
|
+
return this.providerCache;
|
|
104
|
+
}
|
|
105
|
+
set provider(value) {
|
|
106
|
+
this.providerCache = value;
|
|
143
107
|
}
|
|
144
108
|
async checkAvailability() {
|
|
145
109
|
return checkNativeMemoryAvailability(execAsync);
|
|
@@ -363,7 +327,7 @@ var NativeMemoryManager = class {
|
|
|
363
327
|
error: "DLL injection is only supported on Windows"
|
|
364
328
|
};
|
|
365
329
|
try {
|
|
366
|
-
const { openProcessForMemory, CloseHandle, WriteProcessMemory, VirtualAllocEx, CreateRemoteThread, GetModuleHandle, GetProcAddress, PAGE, MEM } = await import("./Win32API-
|
|
330
|
+
const { openProcessForMemory, CloseHandle, WriteProcessMemory, VirtualAllocEx, CreateRemoteThread, GetModuleHandle, GetProcAddress, PAGE, MEM } = await import("./Win32API-C2kjj0ze.mjs").then((n) => n.g);
|
|
367
331
|
const handle = openProcessForMemory(pid, true);
|
|
368
332
|
try {
|
|
369
333
|
const loadLibraryAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
|
|
@@ -413,7 +377,7 @@ var NativeMemoryManager = class {
|
|
|
413
377
|
let buffer;
|
|
414
378
|
if (encoding === "base64") buffer = Buffer.from(shellcode, "base64");
|
|
415
379
|
else buffer = Buffer.from(shellcode.replace(/\s/g, ""), "hex");
|
|
416
|
-
const { openProcessForMemory, CloseHandle, WriteProcessMemory, VirtualAllocEx, VirtualProtectEx, CreateRemoteThread, PAGE, MEM } = await import("./Win32API-
|
|
380
|
+
const { openProcessForMemory, CloseHandle, WriteProcessMemory, VirtualAllocEx, VirtualProtectEx, CreateRemoteThread, PAGE, MEM } = await import("./Win32API-C2kjj0ze.mjs").then((n) => n.g);
|
|
417
381
|
const handle = openProcessForMemory(pid, true);
|
|
418
382
|
try {
|
|
419
383
|
const remoteMem = VirtualAllocEx(handle, 0n, buffer.length, MEM.COMMIT | MEM.RESERVE, PAGE.READWRITE);
|
|
@@ -460,7 +424,7 @@ var NativeMemoryManager = class {
|
|
|
460
424
|
error: "Debug port check is only supported on Windows"
|
|
461
425
|
};
|
|
462
426
|
try {
|
|
463
|
-
const { openProcessForMemory, CloseHandle, NtQueryInformationProcess } = await import("./Win32API-
|
|
427
|
+
const { openProcessForMemory, CloseHandle, NtQueryInformationProcess } = await import("./Win32API-C2kjj0ze.mjs").then((n) => n.g);
|
|
464
428
|
const handle = openProcessForMemory(pid, false);
|
|
465
429
|
try {
|
|
466
430
|
const { status, debugPort } = NtQueryInformationProcess(handle, 7);
|
|
@@ -502,12 +466,12 @@ function regionInfoToMemoryRegion(info) {
|
|
|
502
466
|
}
|
|
503
467
|
/** Convert MemoryProtection flags to human-readable string */
|
|
504
468
|
function protectionToString(prot) {
|
|
505
|
-
if (prot ===
|
|
469
|
+
if (prot === 0) return "NOACCESS";
|
|
506
470
|
const parts = [];
|
|
507
|
-
const hasRead = (prot &
|
|
508
|
-
const hasWrite = (prot &
|
|
509
|
-
const hasExec = (prot &
|
|
510
|
-
const hasGuard = (prot &
|
|
471
|
+
const hasRead = (prot & 1) !== 0;
|
|
472
|
+
const hasWrite = (prot & 2) !== 0;
|
|
473
|
+
const hasExec = (prot & 4) !== 0;
|
|
474
|
+
const hasGuard = (prot & 8) !== 0;
|
|
511
475
|
if (hasRead && hasWrite && hasExec) parts.push("RWX");
|
|
512
476
|
else if (hasRead && hasExec) parts.push("RX");
|
|
513
477
|
else if (hasRead && hasWrite) parts.push("RW");
|
|
@@ -518,4 +482,4 @@ function protectionToString(prot) {
|
|
|
518
482
|
}
|
|
519
483
|
const nativeMemoryManager = new NativeMemoryManager();
|
|
520
484
|
//#endregion
|
|
521
|
-
export {
|
|
485
|
+
export { nativeMemoryManager as t };
|
package/dist/{NativeMemoryManager.utils-BML4q1ry.mjs → NativeMemoryManager.utils-BBlAixF5.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as PAGE, v as isKoffiAvailable$1 } from "./Win32API-
|
|
1
|
+
import { u as PAGE, v as isKoffiAvailable$1 } from "./Win32API-C2kjj0ze.mjs";
|
|
2
2
|
//#region src/native/NativeMemoryManager.utils.ts
|
|
3
3
|
function isKoffiAvailable() {
|
|
4
4
|
return isKoffiAvailable$1();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as logger } from "./logger-
|
|
2
|
-
import { a as GetModuleFileNameEx, b as openProcessForMemory, d as ReadProcessMemory, i as GetModuleBaseName, n as EnumProcessModules, s as GetModuleInformation, t as CloseHandle } from "./Win32API-
|
|
1
|
+
import { t as logger } from "./logger-Dh_xb7_2.mjs";
|
|
2
|
+
import { a as GetModuleFileNameEx, b as openProcessForMemory, d as ReadProcessMemory, i as GetModuleBaseName, n as EnumProcessModules, s as GetModuleInformation, t as CloseHandle } from "./Win32API-C2kjj0ze.mjs";
|
|
3
3
|
import { promises } from "node:fs";
|
|
4
4
|
//#region src/native/PEAnalyzer.types.ts
|
|
5
5
|
/** PE section characteristic flags */
|
|
@@ -104,7 +104,7 @@ var PEAnalyzer = class {
|
|
|
104
104
|
const base = BigInt(moduleBase);
|
|
105
105
|
const hProcess = openProcessForMemory(pid);
|
|
106
106
|
try {
|
|
107
|
-
const headers = await this.
|
|
107
|
+
const headers = await this.readCoreHeaders(hProcess, base);
|
|
108
108
|
const sections = [];
|
|
109
109
|
for (let i = 0; i < headers.numSections; i++) {
|
|
110
110
|
const off = headers.firstSectionOffset + i * SECTION_HEADER_SIZE;
|
|
@@ -138,7 +138,7 @@ var PEAnalyzer = class {
|
|
|
138
138
|
const base = BigInt(moduleBase);
|
|
139
139
|
const hProcess = openProcessForMemory(pid);
|
|
140
140
|
try {
|
|
141
|
-
const headers = await this.
|
|
141
|
+
const headers = await this.readCoreHeaders(hProcess, base);
|
|
142
142
|
const importRva = headers.dataDirectories[IMAGE_DIRECTORY_ENTRY.IMPORT];
|
|
143
143
|
if (!importRva || importRva.rva === 0) return [];
|
|
144
144
|
const imports = [];
|
|
@@ -151,7 +151,7 @@ var PEAnalyzer = class {
|
|
|
151
151
|
const nullIdx = nameData.indexOf(0);
|
|
152
152
|
const dllName = nameData.subarray(0, nullIdx > 0 ? nullIdx : 256).toString("ascii");
|
|
153
153
|
const originalFirstThunkRva = desc.readUInt32LE(0) || desc.readUInt32LE(16);
|
|
154
|
-
const functions = this.
|
|
154
|
+
const functions = this.readThunkArray(hProcess, base, originalFirstThunkRva, headers.isPE32Plus);
|
|
155
155
|
imports.push({
|
|
156
156
|
dllName,
|
|
157
157
|
functions
|
|
@@ -170,7 +170,7 @@ var PEAnalyzer = class {
|
|
|
170
170
|
const base = BigInt(moduleBase);
|
|
171
171
|
const hProcess = openProcessForMemory(pid);
|
|
172
172
|
try {
|
|
173
|
-
const exportDir = (await this.
|
|
173
|
+
const exportDir = (await this.readCoreHeaders(hProcess, base)).dataDirectories[IMAGE_DIRECTORY_ENTRY.EXPORT];
|
|
174
174
|
if (!exportDir || exportDir.rva === 0) return [];
|
|
175
175
|
const expData = ReadProcessMemory(hProcess, base + BigInt(exportDir.rva), 40);
|
|
176
176
|
const numberOfNames = expData.readUInt32LE(24);
|
|
@@ -213,7 +213,7 @@ var PEAnalyzer = class {
|
|
|
213
213
|
const hProcess = openProcessForMemory(pid);
|
|
214
214
|
const detections = [];
|
|
215
215
|
try {
|
|
216
|
-
const modules = this.
|
|
216
|
+
const modules = this.enumerateModulesInternal(hProcess);
|
|
217
217
|
const targets = moduleName ? modules.filter((m) => m.name.toLowerCase().includes(moduleName.toLowerCase())) : modules;
|
|
218
218
|
for (const mod of targets) try {
|
|
219
219
|
const diskData = await promises.readFile(mod.path);
|
|
@@ -222,12 +222,12 @@ var PEAnalyzer = class {
|
|
|
222
222
|
const funcRva = parseInt(exp.rva, 16);
|
|
223
223
|
if (funcRva === 0 || exp.forwardedTo) continue;
|
|
224
224
|
const memBytes = ReadProcessMemory(hProcess, BigInt(mod.base) + BigInt(funcRva), COMPARE_BYTES);
|
|
225
|
-
const diskOffset = this.
|
|
225
|
+
const diskOffset = this.rvaToFileOffset(diskData, funcRva);
|
|
226
226
|
if (diskOffset < 0 || diskOffset + COMPARE_BYTES > diskData.length) continue;
|
|
227
227
|
const diskBytes = diskData.subarray(diskOffset, diskOffset + COMPARE_BYTES);
|
|
228
228
|
if (!memBytes.equals(diskBytes)) {
|
|
229
|
-
const hookType = this.
|
|
230
|
-
const jumpTarget = this.
|
|
229
|
+
const hookType = this.classifyHook(memBytes);
|
|
230
|
+
const jumpTarget = this.decodeJumpTarget(memBytes, BigInt(mod.base) + BigInt(funcRva));
|
|
231
231
|
detections.push({
|
|
232
232
|
address: `0x${(BigInt(mod.base) + BigInt(funcRva)).toString(16)}`,
|
|
233
233
|
moduleName: mod.name,
|
|
@@ -273,7 +273,7 @@ var PEAnalyzer = class {
|
|
|
273
273
|
});
|
|
274
274
|
return anomalies;
|
|
275
275
|
}
|
|
276
|
-
async
|
|
276
|
+
async readCoreHeaders(hProcess, base) {
|
|
277
277
|
const e_lfanew = ReadProcessMemory(hProcess, base, 64).readUInt32LE(60);
|
|
278
278
|
const ntData = ReadProcessMemory(hProcess, base + BigInt(e_lfanew), 264);
|
|
279
279
|
const numSections = ntData.readUInt16LE(6);
|
|
@@ -296,7 +296,7 @@ var PEAnalyzer = class {
|
|
|
296
296
|
dataDirectories
|
|
297
297
|
};
|
|
298
298
|
}
|
|
299
|
-
|
|
299
|
+
readThunkArray(hProcess, base, thunkRva, isPE32Plus) {
|
|
300
300
|
const thunkSize = isPE32Plus ? 8 : 4;
|
|
301
301
|
const functions = [];
|
|
302
302
|
const IMAGE_ORDINAL_FLAG = isPE32Plus ? 9223372036854775808n : 2147483648n;
|
|
@@ -326,7 +326,7 @@ var PEAnalyzer = class {
|
|
|
326
326
|
}
|
|
327
327
|
return functions;
|
|
328
328
|
}
|
|
329
|
-
|
|
329
|
+
enumerateModulesInternal(hProcess) {
|
|
330
330
|
const modules = [];
|
|
331
331
|
try {
|
|
332
332
|
const { modules: modHandles, count } = EnumProcessModules(hProcess);
|
|
@@ -347,7 +347,7 @@ var PEAnalyzer = class {
|
|
|
347
347
|
}
|
|
348
348
|
return modules;
|
|
349
349
|
}
|
|
350
|
-
|
|
350
|
+
rvaToFileOffset(peData, rva) {
|
|
351
351
|
const e_lfanew = peData.readUInt32LE(60);
|
|
352
352
|
const numSections = peData.readUInt16LE(e_lfanew + 6);
|
|
353
353
|
const sizeOfOptionalHeader = peData.readUInt16LE(e_lfanew + 20);
|
|
@@ -362,13 +362,13 @@ var PEAnalyzer = class {
|
|
|
362
362
|
}
|
|
363
363
|
return -1;
|
|
364
364
|
}
|
|
365
|
-
|
|
365
|
+
classifyHook(memBytes) {
|
|
366
366
|
if (memBytes[0] === 233) return "jmp_rel32";
|
|
367
367
|
if (memBytes[0] === 255 && memBytes[1] === 37) return "jmp_abs64";
|
|
368
368
|
if (memBytes[0] === 104 && memBytes[5] === 195) return "push_ret";
|
|
369
369
|
return "unknown";
|
|
370
370
|
}
|
|
371
|
-
|
|
371
|
+
decodeJumpTarget(memBytes, funcAddr) {
|
|
372
372
|
if (memBytes[0] === 233) {
|
|
373
373
|
const rel32 = memBytes.readInt32LE(1);
|
|
374
374
|
return `0x${(funcAddr + 5n + BigInt(rel32)).toString(16)}`;
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import { t as logger } from "./logger-Dh_xb7_2.mjs";
|
|
2
|
+
import { Jt as PAGE_NETWORK_IDLE_TIMEOUT_MS, qt as PAGE_FRAME_SELECTOR_TIMEOUT_MS } from "./constants-CDZLOoVv.mjs";
|
|
3
|
+
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
4
|
+
//#region src/modules/collector/PageController.ts
|
|
5
|
+
var PageController = class {
|
|
6
|
+
constructor(collector) {
|
|
7
|
+
this.collector = collector;
|
|
8
|
+
}
|
|
9
|
+
async getBrowser() {
|
|
10
|
+
return this.collector.getBrowser();
|
|
11
|
+
}
|
|
12
|
+
hasAttachedTargetSession() {
|
|
13
|
+
return this.collector.getAttachedTargetSession() !== null;
|
|
14
|
+
}
|
|
15
|
+
getAttachedTargetInfo() {
|
|
16
|
+
return this.collector.getAttachedTargetInfo();
|
|
17
|
+
}
|
|
18
|
+
async evaluateAttachedTarget(code, options) {
|
|
19
|
+
return await this.collector.getBrowserTargetSessionManager().evaluate(code, options);
|
|
20
|
+
}
|
|
21
|
+
async addScriptToAttachedTarget(source) {
|
|
22
|
+
return await this.collector.getBrowserTargetSessionManager().addScriptToEvaluateOnNewDocument(source);
|
|
23
|
+
}
|
|
24
|
+
async navigate(url, options) {
|
|
25
|
+
const page = await this.collector.getActivePage();
|
|
26
|
+
const startTime = Date.now();
|
|
27
|
+
await page.goto(url, {
|
|
28
|
+
waitUntil: options?.waitUntil || "networkidle2",
|
|
29
|
+
timeout: options?.timeout || 3e4
|
|
30
|
+
});
|
|
31
|
+
const loadTime = Date.now() - startTime;
|
|
32
|
+
const title = await page.title();
|
|
33
|
+
const currentUrl = page.url();
|
|
34
|
+
logger.info(`Navigated to: ${url}`);
|
|
35
|
+
return {
|
|
36
|
+
url: currentUrl,
|
|
37
|
+
title,
|
|
38
|
+
loadTime
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async reload(options) {
|
|
42
|
+
await (await this.collector.getActivePage()).reload({
|
|
43
|
+
waitUntil: options?.waitUntil || "networkidle2",
|
|
44
|
+
timeout: options?.timeout || 3e4
|
|
45
|
+
});
|
|
46
|
+
logger.info("Page reloaded");
|
|
47
|
+
}
|
|
48
|
+
async goBack(timeout = 1e4) {
|
|
49
|
+
await (await this.collector.getActivePage()).goBack({
|
|
50
|
+
waitUntil: "domcontentloaded",
|
|
51
|
+
timeout
|
|
52
|
+
});
|
|
53
|
+
logger.info("Navigated back");
|
|
54
|
+
}
|
|
55
|
+
async goForward(timeout = 1e4) {
|
|
56
|
+
await (await this.collector.getActivePage()).goForward({
|
|
57
|
+
waitUntil: "domcontentloaded",
|
|
58
|
+
timeout
|
|
59
|
+
});
|
|
60
|
+
logger.info("Navigated forward");
|
|
61
|
+
}
|
|
62
|
+
async click(selector, options, frameOptions) {
|
|
63
|
+
const page = await this.collector.getActivePage();
|
|
64
|
+
const context = await this.resolveFrame(page, frameOptions);
|
|
65
|
+
const timeout = options?.timeout;
|
|
66
|
+
const clickOptions = {
|
|
67
|
+
button: options?.button || "left",
|
|
68
|
+
clickCount: options?.clickCount || 1,
|
|
69
|
+
delay: options?.delay
|
|
70
|
+
};
|
|
71
|
+
if (options?.offset) clickOptions.offset = options.offset;
|
|
72
|
+
if (typeof timeout === "number" && Number.isFinite(timeout) && timeout > 0) {
|
|
73
|
+
page.setDefaultTimeout(timeout);
|
|
74
|
+
try {
|
|
75
|
+
await context.click(selector, clickOptions);
|
|
76
|
+
} finally {
|
|
77
|
+
page.setDefaultTimeout(this.collector["config"]?.timeout ?? 3e4);
|
|
78
|
+
}
|
|
79
|
+
} else await context.click(selector, clickOptions);
|
|
80
|
+
logger.info(`Clicked: ${selector}${frameOptions?.frameUrl || frameOptions?.frameSelector ? " (in frame)" : ""}`);
|
|
81
|
+
}
|
|
82
|
+
async type(selector, text, options, frameOptions) {
|
|
83
|
+
const page = await this.collector.getActivePage();
|
|
84
|
+
await (await this.resolveFrame(page, frameOptions)).type(selector, text, { delay: options?.delay });
|
|
85
|
+
logger.info(`Typed into ${selector}: ${text.substring(0, 20)}...`);
|
|
86
|
+
}
|
|
87
|
+
async select(selector, values, frameOptions) {
|
|
88
|
+
const page = await this.collector.getActivePage();
|
|
89
|
+
await (await this.resolveFrame(page, frameOptions)).select(selector, ...values);
|
|
90
|
+
logger.info(`Selected in ${selector}: ${values.join(", ")}`);
|
|
91
|
+
}
|
|
92
|
+
async hover(selector, frameOptions) {
|
|
93
|
+
const page = await this.collector.getActivePage();
|
|
94
|
+
await (await this.resolveFrame(page, frameOptions)).hover(selector);
|
|
95
|
+
logger.info(`Hovered: ${selector}`);
|
|
96
|
+
}
|
|
97
|
+
async scroll(options) {
|
|
98
|
+
await (await this.collector.getActivePage()).evaluate((opts) => {
|
|
99
|
+
window.scrollTo(opts.x || 0, opts.y || 0);
|
|
100
|
+
}, options);
|
|
101
|
+
logger.info(`Scrolled to: x=${options.x || 0}, y=${options.y || 0}`);
|
|
102
|
+
}
|
|
103
|
+
async waitForSelector(selector, timeout) {
|
|
104
|
+
try {
|
|
105
|
+
const page = await this.collector.getActivePage();
|
|
106
|
+
await page.waitForSelector(selector, { timeout: timeout || 3e4 });
|
|
107
|
+
const element = await page.evaluate((sel) => {
|
|
108
|
+
const el = document.querySelector(sel);
|
|
109
|
+
if (!el) return null;
|
|
110
|
+
return {
|
|
111
|
+
tagName: el.tagName.toLowerCase(),
|
|
112
|
+
id: el.id || void 0,
|
|
113
|
+
className: el.className || void 0,
|
|
114
|
+
textContent: el.textContent?.trim().substring(0, 100) || void 0,
|
|
115
|
+
attributes: Array.from(el.attributes).reduce((acc, attr) => {
|
|
116
|
+
acc[attr.name] = attr.value;
|
|
117
|
+
return acc;
|
|
118
|
+
}, {})
|
|
119
|
+
};
|
|
120
|
+
}, selector);
|
|
121
|
+
logger.info(`Selector appeared: ${selector}`);
|
|
122
|
+
return {
|
|
123
|
+
success: true,
|
|
124
|
+
element,
|
|
125
|
+
message: `Selector appeared: ${selector}`
|
|
126
|
+
};
|
|
127
|
+
} catch (error) {
|
|
128
|
+
logger.error(`waitForSelector timeout for ${selector}:`, error);
|
|
129
|
+
return {
|
|
130
|
+
success: false,
|
|
131
|
+
message: `Timeout waiting for selector: ${selector}`
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async waitForNavigation(timeout) {
|
|
136
|
+
await (await this.collector.getActivePage()).waitForNavigation({
|
|
137
|
+
waitUntil: "networkidle2",
|
|
138
|
+
timeout: timeout || 3e4
|
|
139
|
+
});
|
|
140
|
+
logger.info("Navigation completed");
|
|
141
|
+
}
|
|
142
|
+
async evaluate(code, frameOptions) {
|
|
143
|
+
const page = await this.collector.getActivePage();
|
|
144
|
+
if (frameOptions?.frameUrl || frameOptions?.frameSelector) {
|
|
145
|
+
const result = await evaluateOnContextWithTimeout(page, await this.resolveFrame(page, frameOptions), code);
|
|
146
|
+
logger.info("JavaScript executed (in frame)");
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
const result = await evaluateWithTimeout(page, code);
|
|
150
|
+
logger.info("JavaScript executed");
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Resolve a child frame from the active page.
|
|
155
|
+
* When no options are provided (or both fields are undefined), returns page.mainFrame().
|
|
156
|
+
*/
|
|
157
|
+
async resolveFrame(page, options) {
|
|
158
|
+
if (!options) return page.mainFrame();
|
|
159
|
+
if (options.frameUrl) {
|
|
160
|
+
const frames = page.frames();
|
|
161
|
+
const frame = frames.find((f) => f.url().includes(options.frameUrl));
|
|
162
|
+
if (!frame) {
|
|
163
|
+
const available = frames.map((f) => f.url()).filter((u) => u && u !== "about:blank");
|
|
164
|
+
throw new Error(`No frame matching URL substring "${options.frameUrl}". Available frames: ${available.join(", ") || "(none)"}`);
|
|
165
|
+
}
|
|
166
|
+
return frame;
|
|
167
|
+
}
|
|
168
|
+
if (options.frameSelector) {
|
|
169
|
+
await page.waitForSelector(options.frameSelector, { timeout: PAGE_FRAME_SELECTOR_TIMEOUT_MS }).catch(() => null);
|
|
170
|
+
const handle = await page.$(options.frameSelector);
|
|
171
|
+
if (!handle) throw new Error(`No element found for iframe selector: ${options.frameSelector}`);
|
|
172
|
+
const frame = await handle.contentFrame();
|
|
173
|
+
if (!frame) throw new Error(`Element "${options.frameSelector}" exists but has no content frame (not an iframe or not yet loaded).`);
|
|
174
|
+
return frame;
|
|
175
|
+
}
|
|
176
|
+
return page.mainFrame();
|
|
177
|
+
}
|
|
178
|
+
/** List all frames in the active page with URL and name info. */
|
|
179
|
+
async listFrames() {
|
|
180
|
+
const page = await this.collector.getActivePage();
|
|
181
|
+
const mainFrame = page.mainFrame();
|
|
182
|
+
return page.frames().map((frame) => ({
|
|
183
|
+
url: frame.url(),
|
|
184
|
+
name: frame.name() || "",
|
|
185
|
+
id: frame["_id"] || "",
|
|
186
|
+
isMainFrame: frame === mainFrame
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
async getURL() {
|
|
190
|
+
return (await this.collector.getActivePage()).url();
|
|
191
|
+
}
|
|
192
|
+
async getTitle() {
|
|
193
|
+
return await (await this.collector.getActivePage()).title();
|
|
194
|
+
}
|
|
195
|
+
async getContent() {
|
|
196
|
+
return await (await this.collector.getActivePage()).content();
|
|
197
|
+
}
|
|
198
|
+
async screenshot(options) {
|
|
199
|
+
const page = await this.collector.getActivePage();
|
|
200
|
+
const screenshotOpts = {
|
|
201
|
+
path: options?.path,
|
|
202
|
+
type: options?.type || "png",
|
|
203
|
+
quality: options?.quality,
|
|
204
|
+
fullPage: options?.fullPage || false
|
|
205
|
+
};
|
|
206
|
+
if (options?.clip) {
|
|
207
|
+
screenshotOpts.clip = options.clip;
|
|
208
|
+
screenshotOpts.fullPage = false;
|
|
209
|
+
}
|
|
210
|
+
const buffer = await page.screenshot(screenshotOpts);
|
|
211
|
+
logger.info(`Screenshot taken${options?.path ? `: ${options.path}` : ""}`);
|
|
212
|
+
return buffer;
|
|
213
|
+
}
|
|
214
|
+
async getPerformanceMetrics() {
|
|
215
|
+
const metrics = await evaluateWithTimeout(await this.collector.getActivePage(), () => {
|
|
216
|
+
const perf = performance.getEntriesByType("navigation")[0];
|
|
217
|
+
return {
|
|
218
|
+
domContentLoaded: perf.domContentLoadedEventEnd - perf.domContentLoadedEventStart,
|
|
219
|
+
loadComplete: perf.loadEventEnd - perf.loadEventStart,
|
|
220
|
+
dns: perf.domainLookupEnd - perf.domainLookupStart,
|
|
221
|
+
tcp: perf.connectEnd - perf.connectStart,
|
|
222
|
+
request: perf.responseStart - perf.requestStart,
|
|
223
|
+
response: perf.responseEnd - perf.responseStart,
|
|
224
|
+
total: perf.loadEventEnd - perf.fetchStart,
|
|
225
|
+
resources: performance.getEntriesByType("resource").length
|
|
226
|
+
};
|
|
227
|
+
});
|
|
228
|
+
logger.info("Performance metrics retrieved");
|
|
229
|
+
return metrics;
|
|
230
|
+
}
|
|
231
|
+
async injectScript(scriptContent) {
|
|
232
|
+
await evaluateWithTimeout(await this.collector.getActivePage(), (script) => {
|
|
233
|
+
const scriptElement = document.createElement("script");
|
|
234
|
+
scriptElement.textContent = script;
|
|
235
|
+
document.head.appendChild(scriptElement);
|
|
236
|
+
}, scriptContent);
|
|
237
|
+
logger.info("Script injected into page");
|
|
238
|
+
}
|
|
239
|
+
async setCookies(cookies) {
|
|
240
|
+
await (await this.collector.getActivePage()).setCookie(...cookies);
|
|
241
|
+
logger.info(`Set ${cookies.length} cookies`);
|
|
242
|
+
}
|
|
243
|
+
async getCookies() {
|
|
244
|
+
const cookies = await (await this.collector.getActivePage()).cookies();
|
|
245
|
+
logger.info(`Retrieved ${cookies.length} cookies`);
|
|
246
|
+
return cookies;
|
|
247
|
+
}
|
|
248
|
+
async clearCookies() {
|
|
249
|
+
const page = await this.collector.getActivePage();
|
|
250
|
+
const cookies = await page.cookies();
|
|
251
|
+
await page.deleteCookie(...cookies);
|
|
252
|
+
logger.info("All cookies cleared");
|
|
253
|
+
}
|
|
254
|
+
async setViewport(width, height) {
|
|
255
|
+
await (await this.collector.getActivePage()).setViewport({
|
|
256
|
+
width,
|
|
257
|
+
height
|
|
258
|
+
});
|
|
259
|
+
logger.info(`Viewport set to ${width}x${height}`);
|
|
260
|
+
}
|
|
261
|
+
async emulateDevice(deviceName) {
|
|
262
|
+
const page = await this.collector.getActivePage();
|
|
263
|
+
const devices = {
|
|
264
|
+
iPhone: {
|
|
265
|
+
viewport: {
|
|
266
|
+
width: 375,
|
|
267
|
+
height: 812,
|
|
268
|
+
isMobile: true
|
|
269
|
+
},
|
|
270
|
+
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15"
|
|
271
|
+
},
|
|
272
|
+
iPad: {
|
|
273
|
+
viewport: {
|
|
274
|
+
width: 768,
|
|
275
|
+
height: 1024,
|
|
276
|
+
isMobile: true
|
|
277
|
+
},
|
|
278
|
+
userAgent: "Mozilla/5.0 (iPad; CPU OS 14_0 like Mac OS X) AppleWebKit/605.1.15"
|
|
279
|
+
},
|
|
280
|
+
Android: {
|
|
281
|
+
viewport: {
|
|
282
|
+
width: 360,
|
|
283
|
+
height: 640,
|
|
284
|
+
isMobile: true
|
|
285
|
+
},
|
|
286
|
+
userAgent: "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 Chrome/91.0.4472.120"
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
const normalized = String(deviceName || "").trim().toLowerCase();
|
|
290
|
+
let resolvedDevice = null;
|
|
291
|
+
if (normalized.includes("iphone")) resolvedDevice = "iPhone";
|
|
292
|
+
else if (normalized.includes("ipad")) resolvedDevice = "iPad";
|
|
293
|
+
else if (normalized.includes("android") || normalized.includes("pixel")) resolvedDevice = "Android";
|
|
294
|
+
if (!resolvedDevice) throw new Error(`Unsupported device "${deviceName}". Supported values include: iPhone, iPad, Android (aliases like "iPhone 13" are accepted).`);
|
|
295
|
+
const device = devices[resolvedDevice];
|
|
296
|
+
await page.setViewport(device.viewport);
|
|
297
|
+
await page.setUserAgent(device.userAgent);
|
|
298
|
+
logger.info(`Emulating ${resolvedDevice} (input: ${deviceName})`);
|
|
299
|
+
return resolvedDevice;
|
|
300
|
+
}
|
|
301
|
+
async waitForNetworkIdle(timeout = PAGE_NETWORK_IDLE_TIMEOUT_MS) {
|
|
302
|
+
await (await this.collector.getActivePage()).waitForNetworkIdle({ timeout });
|
|
303
|
+
logger.info("Network is idle");
|
|
304
|
+
}
|
|
305
|
+
async getLocalStorage() {
|
|
306
|
+
const storage = await (await this.collector.getActivePage()).evaluate(() => {
|
|
307
|
+
const items = {};
|
|
308
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
309
|
+
const key = localStorage.key(i);
|
|
310
|
+
if (key) items[key] = localStorage.getItem(key) || "";
|
|
311
|
+
}
|
|
312
|
+
return items;
|
|
313
|
+
});
|
|
314
|
+
logger.info(`Retrieved ${Object.keys(storage).length} localStorage items`);
|
|
315
|
+
return storage;
|
|
316
|
+
}
|
|
317
|
+
async setLocalStorage(key, value) {
|
|
318
|
+
await (await this.collector.getActivePage()).evaluate((k, v) => {
|
|
319
|
+
localStorage.setItem(k, v);
|
|
320
|
+
}, key, value);
|
|
321
|
+
logger.info(`Set localStorage: ${key}`);
|
|
322
|
+
}
|
|
323
|
+
async clearLocalStorage() {
|
|
324
|
+
await (await this.collector.getActivePage()).evaluate(() => {
|
|
325
|
+
localStorage.clear();
|
|
326
|
+
});
|
|
327
|
+
logger.info("LocalStorage cleared");
|
|
328
|
+
}
|
|
329
|
+
async pressKey(key) {
|
|
330
|
+
await (await this.collector.getActivePage()).keyboard.press(key);
|
|
331
|
+
logger.info(`Pressed key: ${key}`);
|
|
332
|
+
}
|
|
333
|
+
async uploadFile(selector, filePath) {
|
|
334
|
+
const input = await (await this.collector.getActivePage()).$(selector);
|
|
335
|
+
if (!input) throw new Error(`File input not found: ${selector}`);
|
|
336
|
+
await input.uploadFile(filePath);
|
|
337
|
+
logger.info(`File uploaded: ${filePath}`);
|
|
338
|
+
}
|
|
339
|
+
async getAllLinks() {
|
|
340
|
+
const links = await (await this.collector.getActivePage()).evaluate(() => {
|
|
341
|
+
const anchors = document.querySelectorAll("a[href]");
|
|
342
|
+
const result = [];
|
|
343
|
+
for (let i = 0; i < anchors.length; i++) {
|
|
344
|
+
const anchor = anchors[i];
|
|
345
|
+
result.push({
|
|
346
|
+
text: anchor.textContent?.trim() || "",
|
|
347
|
+
href: anchor.href
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
return result;
|
|
351
|
+
});
|
|
352
|
+
logger.info(`Found ${links.length} links`);
|
|
353
|
+
return links;
|
|
354
|
+
}
|
|
355
|
+
async getPage() {
|
|
356
|
+
return await this.collector.getActivePage();
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
/**
|
|
360
|
+
* Pre-flight CDP health check: verify the page CDP target is responsive.
|
|
361
|
+
* After debugger enable + pause/resume, the Playwright CDP session can enter
|
|
362
|
+
* a zombie state where Runtime.evaluate hangs indefinitely without firing
|
|
363
|
+
* 'disconnected'. Without this check, page.evaluate() blocks for the full 30 s
|
|
364
|
+
* timeout — with this check we fail fast (~3 s) with a clear message.
|
|
365
|
+
*/
|
|
366
|
+
async function checkPageCDPHealth(page, timeoutMs = 500) {
|
|
367
|
+
const ac = new AbortController();
|
|
368
|
+
const timer = setTimeout$1(timeoutMs, void 0, { signal: ac.signal }).then(() => {
|
|
369
|
+
throw new Error("cdp_unreachable");
|
|
370
|
+
});
|
|
371
|
+
try {
|
|
372
|
+
const cdp = await Promise.race([page.createCDPSession(), timer]);
|
|
373
|
+
await Promise.race([cdp.send("Runtime.evaluate", {
|
|
374
|
+
expression: "1",
|
|
375
|
+
returnByValue: true
|
|
376
|
+
}), timer]);
|
|
377
|
+
} catch (err) {
|
|
378
|
+
if ((err instanceof Error ? err.message : String(err)) === "cdp_unreachable") throw new Error("CDP session unresponsive — the debugger may be blocking page evaluation. Call debugger_lifecycle({ action: 'disable' })() before this tool, or run it before debugger_lifecycle({ action: 'enable' })().", { cause: err });
|
|
379
|
+
throw err;
|
|
380
|
+
} finally {
|
|
381
|
+
ac.abort();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
async function evaluateOnContextWithTimeout(page, context, pageFunction, ...args) {
|
|
385
|
+
const timeoutMs = 3e4;
|
|
386
|
+
await checkPageCDPHealth(page);
|
|
387
|
+
return Promise.race([context.evaluate(pageFunction, ...[...args]), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`page.evaluate timed out after ${timeoutMs}ms`)), timeoutMs))]);
|
|
388
|
+
}
|
|
389
|
+
async function evaluateWithTimeout(page, pageFunction, ...args) {
|
|
390
|
+
return evaluateOnContextWithTimeout(page, page, pageFunction, ...args);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Wrap a page.evaluateOnNewDocument() call with:
|
|
394
|
+
* 1. A CDP pre-flight health check
|
|
395
|
+
* 2. A hard timeout (30 s) as a backstop
|
|
396
|
+
*/
|
|
397
|
+
async function evaluateOnNewDocumentWithTimeout(page, pageFunction, ...args) {
|
|
398
|
+
const timeoutMs = 3e4;
|
|
399
|
+
await checkPageCDPHealth(page);
|
|
400
|
+
return Promise.race([page.evaluateOnNewDocument(pageFunction, ...[...args]), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`page.evaluateOnNewDocument timed out after ${timeoutMs}ms`)), timeoutMs))]);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Wrap page.coverage.startJSCoverage() with a timeout.
|
|
404
|
+
*/
|
|
405
|
+
async function coverageStartJSWithTimeout(page, options) {
|
|
406
|
+
const timeoutMs = 3e4;
|
|
407
|
+
return Promise.race([page.coverage.startJSCoverage(options), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`coverage.startJSCoverage timed out after ${timeoutMs}ms`)), timeoutMs))]);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Wrap page.coverage.startCSSCoverage() with a timeout.
|
|
411
|
+
*/
|
|
412
|
+
async function coverageStartCSSWithTimeout(page, options) {
|
|
413
|
+
const timeoutMs = 3e4;
|
|
414
|
+
return Promise.race([page.coverage.startCSSCoverage(options), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`coverage.startCSSCoverage timed out after ${timeoutMs}ms`)), timeoutMs))]);
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Wrap page.coverage.stopJSCoverage() with a timeout.
|
|
418
|
+
*/
|
|
419
|
+
async function coverageStopJSWithTimeout(page) {
|
|
420
|
+
const timeoutMs = 3e4;
|
|
421
|
+
return Promise.race([page.coverage.stopJSCoverage(), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`coverage.stopJSCoverage timed out after ${timeoutMs}ms`)), timeoutMs))]);
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Wrap page.coverage.stopCSSCoverage() with a timeout.
|
|
425
|
+
*/
|
|
426
|
+
async function coverageStopCSSWithTimeout(page) {
|
|
427
|
+
const timeoutMs = 3e4;
|
|
428
|
+
return Promise.race([page.coverage.stopCSSCoverage(), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`coverage.stopCSSCoverage timed out after ${timeoutMs}ms`)), timeoutMs))]);
|
|
429
|
+
}
|
|
430
|
+
//#endregion
|
|
431
|
+
export { coverageStopJSWithTimeout as a, coverageStopCSSWithTimeout as i, coverageStartCSSWithTimeout as n, evaluateOnNewDocumentWithTimeout as o, coverageStartJSWithTimeout as r, evaluateWithTimeout as s, PageController as t };
|