@jshookmcp/jshook 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/LICENSE +661 -661
  2. package/README.md +72 -40
  3. package/README.zh.md +77 -40
  4. package/dist/constants.d.ts +1 -0
  5. package/dist/constants.js +13 -1
  6. package/dist/modules/analyzer/IntelligentAnalyzer.js +19 -11
  7. package/dist/modules/browser/BrowserModeManager.d.ts +5 -0
  8. package/dist/modules/browser/BrowserModeManager.js +96 -10
  9. package/dist/modules/browser/CamoufoxBrowserManager.d.ts +4 -0
  10. package/dist/modules/browser/CamoufoxBrowserManager.js +64 -3
  11. package/dist/modules/browser/TabRegistry.js +3 -2
  12. package/dist/modules/browser/UnifiedBrowserManager.d.ts +5 -0
  13. package/dist/modules/browser/UnifiedBrowserManager.js +62 -9
  14. package/dist/modules/captcha/AICaptchaDetector.js +185 -185
  15. package/dist/modules/debugger/DebuggerSessionManager.d.ts +4 -0
  16. package/dist/modules/debugger/DebuggerSessionManager.js +29 -19
  17. package/dist/modules/debugger/ScriptManager.impl.class.d.ts +4 -0
  18. package/dist/modules/debugger/ScriptManager.impl.class.js +46 -21
  19. package/dist/modules/emulator/EnvironmentEmulator.js +2 -2
  20. package/dist/modules/monitor/NetworkMonitor.impl.d.ts +1 -0
  21. package/dist/modules/monitor/NetworkMonitor.impl.js +22 -15
  22. package/dist/modules/monitor/PerformanceMonitor.js +64 -32
  23. package/dist/modules/process/LinuxProcessManager.d.ts +3 -1
  24. package/dist/modules/process/LinuxProcessManager.js +7 -3
  25. package/dist/modules/process/MacProcessManager.d.ts +3 -1
  26. package/dist/modules/process/MacProcessManager.js +32 -28
  27. package/dist/modules/process/ProcessManager.impl.d.ts +5 -1
  28. package/dist/modules/process/ProcessManager.impl.js +54 -13
  29. package/dist/modules/process/index.d.ts +3 -1
  30. package/dist/modules/process/index.js +2 -2
  31. package/dist/modules/process/memory/AuditTrail.d.ts +25 -0
  32. package/dist/modules/process/memory/AuditTrail.js +44 -0
  33. package/dist/modules/process/memory/availability.js +49 -49
  34. package/dist/modules/process/memory/injector.js +185 -185
  35. package/dist/modules/process/memory/linux/mapsParser.d.ts +16 -0
  36. package/dist/modules/process/memory/linux/mapsParser.js +28 -0
  37. package/dist/modules/process/memory/reader.js +50 -50
  38. package/dist/modules/process/memory/regions.enumerate.js +45 -1
  39. package/dist/modules/process/memory/regions.protection.js +48 -2
  40. package/dist/modules/process/memory/scanner.d.ts +4 -1
  41. package/dist/modules/process/memory/scanner.js +383 -182
  42. package/dist/modules/process/memory/writer.js +54 -54
  43. package/dist/native/NativeMemoryManager.impl.d.ts +4 -0
  44. package/dist/native/NativeMemoryManager.impl.js +72 -24
  45. package/dist/native/NativeMemoryManager.utils.d.ts +1 -0
  46. package/dist/native/NativeMemoryManager.utils.js +44 -1
  47. package/dist/native/scripts/linux/enum-windows.sh +12 -12
  48. package/dist/native/scripts/macos/enum-windows.applescript +22 -22
  49. package/dist/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
  50. package/dist/native/scripts/windows/enum-windows.ps1 +44 -44
  51. package/dist/native/scripts/windows/inject-dll.ps1 +21 -21
  52. package/dist/server/MCPServer.search.d.ts +3 -0
  53. package/dist/server/MCPServer.search.js +21 -2
  54. package/dist/server/ToolCallContextGuard.d.ts +2 -0
  55. package/dist/server/ToolCallContextGuard.js +29 -14
  56. package/dist/server/ToolSearch.js +11 -5
  57. package/dist/server/domains/browser/definitions.tools.page-core.js +53 -53
  58. package/dist/server/domains/browser/definitions.tools.runtime.js +40 -40
  59. package/dist/server/domains/browser/definitions.tools.security.js +76 -76
  60. package/dist/server/domains/browser/handlers/tab-workflow.js +6 -4
  61. package/dist/server/domains/maintenance/handlers.extensions.js +46 -26
  62. package/dist/server/domains/process/definitions.js +20 -7
  63. package/dist/server/domains/process/handlers.impl.core.runtime.base.d.ts +35 -0
  64. package/dist/server/domains/process/handlers.impl.core.runtime.base.js +107 -1
  65. package/dist/server/domains/process/handlers.impl.core.runtime.inject.js +111 -2
  66. package/dist/server/domains/process/handlers.impl.core.runtime.memory.d.ts +9 -0
  67. package/dist/server/domains/process/handlers.impl.core.runtime.memory.js +282 -31
  68. package/dist/server/domains/process/manifest.js +1 -0
  69. package/dist/server/domains/transform/handlers.impl.transform-base.js +102 -102
  70. package/dist/server/domains/workflow/handlers.impl.workflow-api.js +14 -4
  71. package/dist/server/domains/workflow/handlers.impl.workflow-base.js +51 -51
  72. package/dist/server/registry/discovery.js +17 -12
  73. package/dist/server/registry/index.js +10 -2
  74. package/dist/utils/TokenBudgetManager.d.ts +1 -0
  75. package/dist/utils/TokenBudgetManager.js +22 -0
  76. package/package.json +5 -1
  77. package/src/native/scripts/linux/enum-windows.sh +12 -12
  78. package/src/native/scripts/macos/enum-windows.applescript +22 -22
  79. package/src/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
  80. package/src/native/scripts/windows/enum-windows.ps1 +44 -44
  81. package/src/native/scripts/windows/inject-dll.ps1 +21 -21
@@ -14,14 +14,67 @@ function normalizePatternType(value) {
14
14
  }
15
15
  return 'hex';
16
16
  }
17
+ function getOptionalPid(value) {
18
+ const pid = Number(value);
19
+ return Number.isInteger(pid) && pid > 0 ? pid : undefined;
20
+ }
21
+ function getOptionalString(value) {
22
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
23
+ }
24
+ function getOptionalPositiveNumber(value) {
25
+ const size = Number(value);
26
+ return Number.isFinite(size) && size > 0 ? size : undefined;
27
+ }
28
+ function getWriteSize(data, encoding) {
29
+ if (encoding === 'hex') {
30
+ const normalized = data.replace(/\s+/g, '');
31
+ return Math.ceil(normalized.length / 2);
32
+ }
33
+ return Buffer.from(data, 'base64').length;
34
+ }
17
35
  export class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
36
+ async safeBuildMemoryDiagnostics(input) {
37
+ try {
38
+ return await this.buildMemoryDiagnostics(input);
39
+ }
40
+ catch (diagnosticError) {
41
+ logger.warn('Memory diagnostics generation failed:', diagnosticError);
42
+ return undefined;
43
+ }
44
+ }
45
+ recordMemoryAudit(entry) {
46
+ try {
47
+ this.auditTrail.record(entry);
48
+ }
49
+ catch (auditError) {
50
+ logger.warn('Memory audit trail recording failed:', auditError);
51
+ }
52
+ }
18
53
  async handleMemoryRead(args) {
54
+ const startedAt = Date.now();
19
55
  try {
20
56
  const pid = validatePid(args.pid);
21
57
  const address = requireString(args.address, 'address');
22
58
  const size = requirePositiveNumber(args.size, 'size');
23
59
  const availability = await this.memoryManager.checkAvailability();
24
60
  if (!availability.available) {
61
+ const errorMessage = availability.reason ?? 'Memory operations not available';
62
+ const diagnostics = await this.safeBuildMemoryDiagnostics({
63
+ pid,
64
+ address,
65
+ size,
66
+ operation: 'memory_read',
67
+ error: errorMessage,
68
+ });
69
+ this.recordMemoryAudit({
70
+ operation: 'memory_read',
71
+ pid,
72
+ address,
73
+ size,
74
+ result: 'failure',
75
+ error: errorMessage,
76
+ durationMs: Date.now() - startedAt,
77
+ });
25
78
  return {
26
79
  content: [
27
80
  {
@@ -34,38 +87,82 @@ export class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
34
87
  requestedAddress: address,
35
88
  requestedSize: size,
36
89
  pid,
90
+ diagnostics,
37
91
  }, null, 2),
38
92
  },
39
93
  ],
40
94
  };
41
95
  }
42
96
  const result = await this.memoryManager.readMemory(pid, address, size);
97
+ const diagnostics = !result.success
98
+ ? await this.safeBuildMemoryDiagnostics({
99
+ pid,
100
+ address,
101
+ size,
102
+ operation: 'memory_read',
103
+ error: result.error,
104
+ })
105
+ : undefined;
106
+ this.recordMemoryAudit({
107
+ operation: 'memory_read',
108
+ pid,
109
+ address,
110
+ size,
111
+ result: result.success ? 'success' : 'failure',
112
+ error: result.error,
113
+ durationMs: Date.now() - startedAt,
114
+ });
115
+ const payload = {
116
+ success: result.success,
117
+ data: result.data,
118
+ error: result.error,
119
+ pid,
120
+ address,
121
+ size,
122
+ platform: this.platform,
123
+ };
124
+ if (!result.success) {
125
+ payload.diagnostics = diagnostics;
126
+ }
43
127
  return {
44
128
  content: [
45
129
  {
46
130
  type: 'text',
47
- text: JSON.stringify({
48
- success: result.success,
49
- data: result.data,
50
- error: result.error,
51
- pid,
52
- address,
53
- size,
54
- platform: this.platform,
55
- }, null, 2),
131
+ text: JSON.stringify(payload, null, 2),
56
132
  },
57
133
  ],
58
134
  };
59
135
  }
60
136
  catch (error) {
61
137
  logger.error('Memory read failed:', error);
138
+ const errorMessage = error instanceof Error ? error.message : String(error);
139
+ const pid = getOptionalPid(args.pid);
140
+ const address = getOptionalString(args.address);
141
+ const size = getOptionalPositiveNumber(args.size);
142
+ const diagnostics = await this.safeBuildMemoryDiagnostics({
143
+ pid,
144
+ address,
145
+ size,
146
+ operation: 'memory_read',
147
+ error: errorMessage,
148
+ });
149
+ this.recordMemoryAudit({
150
+ operation: 'memory_read',
151
+ pid: pid ?? null,
152
+ address: address ?? null,
153
+ size: size ?? null,
154
+ result: 'failure',
155
+ error: errorMessage,
156
+ durationMs: Date.now() - startedAt,
157
+ });
62
158
  return {
63
159
  content: [
64
160
  {
65
161
  type: 'text',
66
162
  text: JSON.stringify({
67
163
  success: false,
68
- error: error instanceof Error ? error.message : String(error),
164
+ error: errorMessage,
165
+ diagnostics,
69
166
  }, null, 2),
70
167
  },
71
168
  ],
@@ -73,13 +170,32 @@ export class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
73
170
  }
74
171
  }
75
172
  async handleMemoryWrite(args) {
173
+ const startedAt = Date.now();
76
174
  try {
77
175
  const pid = validatePid(args.pid);
78
176
  const address = requireString(args.address, 'address');
79
177
  const data = requireString(args.data, 'data');
80
178
  const encoding = args.encoding || 'hex';
179
+ const size = getWriteSize(data, encoding);
81
180
  const availability = await this.memoryManager.checkAvailability();
82
181
  if (!availability.available) {
182
+ const errorMessage = availability.reason ?? 'Memory operations not available';
183
+ const diagnostics = await this.safeBuildMemoryDiagnostics({
184
+ pid,
185
+ address,
186
+ size,
187
+ operation: 'memory_write',
188
+ error: errorMessage,
189
+ });
190
+ this.recordMemoryAudit({
191
+ operation: 'memory_write',
192
+ pid,
193
+ address,
194
+ size,
195
+ result: 'failure',
196
+ error: errorMessage,
197
+ durationMs: Date.now() - startedAt,
198
+ });
83
199
  return {
84
200
  content: [
85
201
  {
@@ -93,39 +209,85 @@ export class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
93
209
  dataLength: data != null ? data.length : 0,
94
210
  encoding,
95
211
  pid,
212
+ diagnostics,
96
213
  }, null, 2),
97
214
  },
98
215
  ],
99
216
  };
100
217
  }
101
218
  const result = await this.memoryManager.writeMemory(pid, address, data, encoding);
219
+ const diagnostics = !result.success
220
+ ? await this.safeBuildMemoryDiagnostics({
221
+ pid,
222
+ address,
223
+ size,
224
+ operation: 'memory_write',
225
+ error: result.error,
226
+ })
227
+ : undefined;
228
+ this.recordMemoryAudit({
229
+ operation: 'memory_write',
230
+ pid,
231
+ address,
232
+ size,
233
+ result: result.success ? 'success' : 'failure',
234
+ error: result.error,
235
+ durationMs: Date.now() - startedAt,
236
+ });
237
+ const payload = {
238
+ success: result.success,
239
+ bytesWritten: result.bytesWritten,
240
+ error: result.error,
241
+ pid,
242
+ address,
243
+ dataLength: data.length,
244
+ encoding,
245
+ platform: this.platform,
246
+ };
247
+ if (!result.success) {
248
+ payload.diagnostics = diagnostics;
249
+ }
102
250
  return {
103
251
  content: [
104
252
  {
105
253
  type: 'text',
106
- text: JSON.stringify({
107
- success: result.success,
108
- bytesWritten: result.bytesWritten,
109
- error: result.error,
110
- pid,
111
- address,
112
- dataLength: data.length,
113
- encoding,
114
- platform: this.platform,
115
- }, null, 2),
254
+ text: JSON.stringify(payload, null, 2),
116
255
  },
117
256
  ],
118
257
  };
119
258
  }
120
259
  catch (error) {
121
260
  logger.error('Memory write failed:', error);
261
+ const errorMessage = error instanceof Error ? error.message : String(error);
262
+ const pid = getOptionalPid(args.pid);
263
+ const address = getOptionalString(args.address);
264
+ const data = getOptionalString(args.data);
265
+ const encoding = args.encoding || 'hex';
266
+ const size = data ? getWriteSize(data, encoding) : undefined;
267
+ const diagnostics = await this.safeBuildMemoryDiagnostics({
268
+ pid,
269
+ address,
270
+ size,
271
+ operation: 'memory_write',
272
+ error: errorMessage,
273
+ });
274
+ this.recordMemoryAudit({
275
+ operation: 'memory_write',
276
+ pid: pid ?? null,
277
+ address: address ?? null,
278
+ size: size ?? null,
279
+ result: 'failure',
280
+ error: errorMessage,
281
+ durationMs: Date.now() - startedAt,
282
+ });
122
283
  return {
123
284
  content: [
124
285
  {
125
286
  type: 'text',
126
287
  text: JSON.stringify({
127
288
  success: false,
128
- error: error instanceof Error ? error.message : String(error),
289
+ error: errorMessage,
290
+ diagnostics,
129
291
  }, null, 2),
130
292
  },
131
293
  ],
@@ -133,12 +295,28 @@ export class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
133
295
  }
134
296
  }
135
297
  async handleMemoryScan(args) {
298
+ const startedAt = Date.now();
136
299
  try {
137
300
  const pid = validatePid(args.pid);
138
301
  const pattern = requireString(args.pattern, 'pattern');
139
302
  const patternType = normalizePatternType(args.patternType);
140
303
  const availability = await this.memoryManager.checkAvailability();
141
304
  if (!availability.available) {
305
+ const errorMessage = availability.reason ?? 'Memory operations not available';
306
+ const diagnostics = await this.safeBuildMemoryDiagnostics({
307
+ pid,
308
+ operation: 'memory_scan',
309
+ error: errorMessage,
310
+ });
311
+ this.recordMemoryAudit({
312
+ operation: 'memory_scan',
313
+ pid,
314
+ address: null,
315
+ size: null,
316
+ result: 'failure',
317
+ error: errorMessage,
318
+ durationMs: Date.now() - startedAt,
319
+ });
142
320
  return {
143
321
  content: [
144
322
  {
@@ -151,43 +329,116 @@ export class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
151
329
  requestedPattern: pattern,
152
330
  patternType,
153
331
  pid,
332
+ diagnostics,
154
333
  }, null, 2),
155
334
  },
156
335
  ],
157
336
  };
158
337
  }
159
338
  const result = await this.memoryManager.scanMemory(pid, pattern, patternType);
339
+ const diagnostics = !result.success
340
+ ? await this.safeBuildMemoryDiagnostics({
341
+ pid,
342
+ operation: 'memory_scan',
343
+ error: result.error,
344
+ })
345
+ : undefined;
346
+ this.recordMemoryAudit({
347
+ operation: 'memory_scan',
348
+ pid,
349
+ address: null,
350
+ size: null,
351
+ result: result.success ? 'success' : 'failure',
352
+ error: result.error,
353
+ durationMs: Date.now() - startedAt,
354
+ });
355
+ const payload = {
356
+ success: result.success,
357
+ addresses: result.addresses,
358
+ error: result.error,
359
+ pid,
360
+ pattern,
361
+ patternType,
362
+ platform: this.platform,
363
+ };
364
+ if (!result.success) {
365
+ payload.diagnostics = diagnostics;
366
+ }
160
367
  return {
161
368
  content: [
162
369
  {
163
370
  type: 'text',
164
- text: JSON.stringify({
165
- success: result.success,
166
- addresses: result.addresses,
167
- error: result.error,
168
- pid,
169
- pattern,
170
- patternType,
171
- platform: this.platform,
172
- }, null, 2),
371
+ text: JSON.stringify(payload, null, 2),
173
372
  },
174
373
  ],
175
374
  };
176
375
  }
177
376
  catch (error) {
178
377
  logger.error('Memory scan failed:', error);
378
+ const errorMessage = error instanceof Error ? error.message : String(error);
379
+ const pid = getOptionalPid(args.pid);
380
+ const diagnostics = await this.safeBuildMemoryDiagnostics({
381
+ pid,
382
+ operation: 'memory_scan',
383
+ error: errorMessage,
384
+ });
385
+ this.recordMemoryAudit({
386
+ operation: 'memory_scan',
387
+ pid: pid ?? null,
388
+ address: null,
389
+ size: null,
390
+ result: 'failure',
391
+ error: errorMessage,
392
+ durationMs: Date.now() - startedAt,
393
+ });
179
394
  return {
180
395
  content: [
181
396
  {
182
397
  type: 'text',
183
398
  text: JSON.stringify({
184
399
  success: false,
185
- error: error instanceof Error ? error.message : String(error),
400
+ error: errorMessage,
401
+ diagnostics,
402
+ }, null, 2),
403
+ },
404
+ ],
405
+ };
406
+ }
407
+ }
408
+ async handleMemoryAuditExport(args) {
409
+ try {
410
+ const exportedJson = this.auditTrail.exportJson();
411
+ const entries = JSON.parse(exportedJson);
412
+ const clear = args.clear === true;
413
+ const count = this.auditTrail.size();
414
+ if (clear) {
415
+ this.auditTrail.clear();
416
+ }
417
+ return {
418
+ content: [
419
+ {
420
+ type: 'text',
421
+ text: JSON.stringify({
422
+ success: true,
423
+ count,
424
+ cleared: clear,
425
+ entries,
186
426
  }, null, 2),
187
427
  },
188
428
  ],
189
429
  };
190
430
  }
431
+ catch (error) {
432
+ logger.error('Memory audit export failed:', error);
433
+ return {
434
+ content: [
435
+ {
436
+ type: 'text',
437
+ text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2),
438
+ },
439
+ ],
440
+ };
441
+ }
191
442
  }
192
443
  async handleMemoryCheckProtection(args) {
193
444
  try {
@@ -36,6 +36,7 @@ const manifest = {
36
36
  { tool: t('memory_batch_write'), domain: DOMAIN, bind: b((h, a) => h.handleMemoryBatchWrite(a)) },
37
37
  { tool: t('memory_dump_region'), domain: DOMAIN, bind: b((h, a) => h.handleMemoryDumpRegion(a)) },
38
38
  { tool: t('memory_list_regions'), domain: DOMAIN, bind: b((h, a) => h.handleMemoryListRegions(a)) },
39
+ { tool: t('memory_audit_export'), domain: DOMAIN, bind: b((h, a) => h.handleMemoryAuditExport(a)) },
39
40
  { tool: t('inject_dll'), domain: DOMAIN, bind: b((h, a) => h.handleInjectDll(a)) },
40
41
  { tool: t('module_inject_dll'), domain: DOMAIN, bind: b((h, a) => h.handleInjectDll(a)) },
41
42
  { tool: t('inject_shellcode'), domain: DOMAIN, bind: b((h, a) => h.handleInjectShellcode(a)) },
@@ -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) {