windows-exe-decompiler-mcp-server 0.1.0 → 0.1.3

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 (134) hide show
  1. package/CLAUDE_INSTALLATION.md +129 -0
  2. package/CODEX_INSTALLATION.md +25 -1
  3. package/COPILOT_INSTALLATION.md +29 -1
  4. package/LICENSE +21 -21
  5. package/README.md +232 -110
  6. package/dist/analysis-provenance.d.ts +50 -2
  7. package/dist/analysis-provenance.js +1 -0
  8. package/dist/analysis-task-runner.js +92 -0
  9. package/dist/config.d.ts +35 -16
  10. package/dist/config.js +37 -3
  11. package/dist/database.js +115 -115
  12. package/dist/decompiler-worker.d.ts +10 -5
  13. package/dist/decompiler-worker.js +240 -58
  14. package/dist/dynamic-trace.d.ts +6 -0
  15. package/dist/dynamic-trace.js +52 -1
  16. package/dist/ghidra-config.js +17 -1
  17. package/dist/index.js +38 -8
  18. package/dist/pe-runtime-functions.d.ts +90 -0
  19. package/dist/pe-runtime-functions.js +452 -0
  20. package/dist/prompts/module-reconstruction-review.d.ts +5 -0
  21. package/dist/prompts/module-reconstruction-review.js +65 -0
  22. package/dist/runtime-correlation.d.ts +5 -0
  23. package/dist/runtime-correlation.js +179 -2
  24. package/dist/sample-workspace.d.ts +28 -0
  25. package/dist/sample-workspace.js +186 -0
  26. package/dist/semantic-name-suggestion-artifacts.d.ts +56 -0
  27. package/dist/semantic-name-suggestion-artifacts.js +87 -0
  28. package/dist/server.js +1 -1
  29. package/dist/setup-guidance.d.ts +73 -0
  30. package/dist/setup-guidance.js +211 -0
  31. package/dist/tools/artifact-read.d.ts +10 -10
  32. package/dist/tools/artifacts-diff.d.ts +8 -8
  33. package/dist/tools/artifacts-list.d.ts +16 -16
  34. package/dist/tools/attack-map.d.ts +8 -8
  35. package/dist/tools/binary-role-profile.d.ts +1197 -0
  36. package/dist/tools/binary-role-profile.js +575 -0
  37. package/dist/tools/code-function-explain-apply.d.ts +12 -12
  38. package/dist/tools/code-function-explain-prepare.d.ts +12 -12
  39. package/dist/tools/code-function-explain-review.d.ts +58 -58
  40. package/dist/tools/code-function-rename-apply.d.ts +12 -12
  41. package/dist/tools/code-function-rename-prepare.d.ts +14 -14
  42. package/dist/tools/code-function-rename-review.d.ts +66 -66
  43. package/dist/tools/code-functions-define.d.ts +337 -0
  44. package/dist/tools/code-functions-define.js +337 -0
  45. package/dist/tools/code-functions-list.js +1 -1
  46. package/dist/tools/code-functions-rank.js +1 -1
  47. package/dist/tools/code-functions-reconstruct.d.ts +368 -58
  48. package/dist/tools/code-functions-reconstruct.js +130 -10
  49. package/dist/tools/code-functions-smart-recover.d.ts +352 -0
  50. package/dist/tools/code-functions-smart-recover.js +190 -0
  51. package/dist/tools/code-module-review-apply.d.ts +233 -0
  52. package/dist/tools/code-module-review-apply.js +209 -0
  53. package/dist/tools/code-module-review-prepare.d.ts +756 -0
  54. package/dist/tools/code-module-review-prepare.js +343 -0
  55. package/dist/tools/code-module-review.d.ts +474 -0
  56. package/dist/tools/code-module-review.js +566 -0
  57. package/dist/tools/code-reconstruct-export.d.ts +193 -21
  58. package/dist/tools/code-reconstruct-export.js +578 -21
  59. package/dist/tools/code-reconstruct-plan.d.ts +20 -20
  60. package/dist/tools/com-role-profile.d.ts +353 -0
  61. package/dist/tools/com-role-profile.js +186 -0
  62. package/dist/tools/dll-export-profile.d.ts +625 -0
  63. package/dist/tools/dll-export-profile.js +230 -0
  64. package/dist/tools/dotnet-metadata-extract.d.ts +122 -122
  65. package/dist/tools/dotnet-reconstruct-export.d.ts +49 -49
  66. package/dist/tools/dotnet-types-list.d.ts +66 -66
  67. package/dist/tools/dynamic-dependencies.d.ts +144 -8
  68. package/dist/tools/dynamic-dependencies.js +20 -1
  69. package/dist/tools/dynamic-memory-import.js +105 -2
  70. package/dist/tools/ghidra-analyze.d.ts +20 -0
  71. package/dist/tools/ghidra-analyze.js +10 -2
  72. package/dist/tools/ghidra-health.d.ts +177 -0
  73. package/dist/tools/ghidra-health.js +28 -0
  74. package/dist/tools/ioc-export.d.ts +8 -8
  75. package/dist/tools/packer-detect.d.ts +14 -14
  76. package/dist/tools/packer-detect.js +6 -9
  77. package/dist/tools/pe-exports-extract.d.ts +8 -8
  78. package/dist/tools/pe-exports-extract.js +6 -9
  79. package/dist/tools/pe-fingerprint.d.ts +36 -36
  80. package/dist/tools/pe-imports-extract.d.ts +8 -8
  81. package/dist/tools/pe-imports-extract.js +6 -9
  82. package/dist/tools/pe-pdata-extract.d.ts +448 -0
  83. package/dist/tools/pe-pdata-extract.js +219 -0
  84. package/dist/tools/pe-symbols-recover.d.ts +227 -0
  85. package/dist/tools/pe-symbols-recover.js +316 -0
  86. package/dist/tools/report-generate.d.ts +15 -1
  87. package/dist/tools/report-generate.js +139 -16
  88. package/dist/tools/report-summarize.d.ts +2242 -8
  89. package/dist/tools/report-summarize.js +246 -10
  90. package/dist/tools/runtime-detect.d.ts +8 -8
  91. package/dist/tools/runtime-detect.js +2 -12
  92. package/dist/tools/rust-binary-analyze.d.ts +2047 -0
  93. package/dist/tools/rust-binary-analyze.js +443 -0
  94. package/dist/tools/sample-profile-get.d.ts +135 -16
  95. package/dist/tools/sample-profile-get.js +35 -32
  96. package/dist/tools/sandbox-execute.d.ts +10 -10
  97. package/dist/tools/strings-extract.d.ts +8 -8
  98. package/dist/tools/strings-extract.js +2 -12
  99. package/dist/tools/strings-floss-decode.d.ts +8 -8
  100. package/dist/tools/system-health.d.ts +172 -36
  101. package/dist/tools/system-health.js +14 -0
  102. package/dist/tools/system-setup-guide.d.ts +179 -0
  103. package/dist/tools/system-setup-guide.js +62 -0
  104. package/dist/tools/tool-help.d.ts +16 -16
  105. package/dist/tools/tool-help.js +172 -0
  106. package/dist/tools/yara-scan.d.ts +8 -8
  107. package/dist/types.d.ts +2 -0
  108. package/dist/workflows/function-explanation-review.d.ts +3034 -76
  109. package/dist/workflows/function-explanation-review.js +172 -42
  110. package/dist/workflows/function-index-recover.d.ts +333 -0
  111. package/dist/workflows/function-index-recover.js +291 -0
  112. package/dist/workflows/module-reconstruction-review.d.ts +3537 -0
  113. package/dist/workflows/module-reconstruction-review.js +354 -0
  114. package/dist/workflows/reconstruct.d.ts +2182 -223
  115. package/dist/workflows/reconstruct.js +575 -32
  116. package/dist/workflows/semantic-name-review.d.ts +1436 -85
  117. package/dist/workflows/semantic-name-review.js +150 -58
  118. package/dist/workflows/triage.d.ts +8 -8
  119. package/ghidra_scripts/DecompileFunction.java +487 -487
  120. package/ghidra_scripts/ExtractCFG.java +256 -256
  121. package/ghidra_scripts/ExtractFunctions.java +442 -442
  122. package/ghidra_scripts/README.md +112 -112
  123. package/ghidra_scripts/SearchFunctionReferences.java +380 -380
  124. package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -9
  125. package/helpers/DotNetMetadataProbe/Program.cs +566 -566
  126. package/install-to-claude.ps1 +432 -0
  127. package/install-to-codex.ps1 +256 -178
  128. package/install-to-copilot.ps1 +389 -303
  129. package/package.json +4 -2
  130. package/requirements.txt +9 -9
  131. package/workers/requirements-dynamic.txt +11 -11
  132. package/workers/requirements.txt +8 -8
  133. package/workers/speakeasy_compat.py +175 -175
  134. package/workers/static_worker.py +3474 -3474
@@ -3,6 +3,8 @@
3
3
  * Generates quick triage report with summary, confidence, IOCs, evidence, and recommendations.
4
4
  */
5
5
  import { z } from 'zod';
6
+ import { BinaryRoleProfileDataSchema, createBinaryRoleProfileHandler, } from './binary-role-profile.js';
7
+ import { RustBinaryAnalyzeDataSchema, createRustBinaryAnalyzeHandler, } from './rust-binary-analyze.js';
6
8
  import { createTriageWorkflowHandler } from '../workflows/triage.js';
7
9
  import { loadDynamicTraceEvidence } from '../dynamic-trace.js';
8
10
  import { loadSemanticFunctionExplanationIndex, } from '../semantic-name-suggestion-artifacts.js';
@@ -144,6 +146,8 @@ export const ReportSummarizeOutputSchema = z.object({
144
146
  evidence: z.array(z.string()).describe('Evidence supporting the assessment'),
145
147
  evidence_lineage: EvidenceLineageSchema.optional().describe('Explicit evidence layers separating static-only, simulated, memory/hybrid, and executed runtime evidence.'),
146
148
  confidence_semantics: ReportAssessmentConfidenceSchema.optional().describe('Explains how to interpret confidence scores. These are heuristic evidence scores, not calibrated probabilities.'),
149
+ binary_profile: BinaryRoleProfileDataSchema.optional().describe('Optional binary role profile summarizing EXE/DLL/COM/service/plugin/export characteristics.'),
150
+ rust_profile: RustBinaryAnalyzeDataSchema.optional().describe('Optional Rust-oriented binary analysis summary, including crate hints, recovered symbols, and recovery priorities.'),
147
151
  provenance: AnalysisProvenanceSchema.optional().describe('Explicit runtime/semantic artifact selection used to produce this report, including scope, session selector, and selected artifact IDs.'),
148
152
  selection_diffs: AnalysisSelectionDiffSchema.optional().describe('Optional comparison between the current artifact selection and a caller-provided baseline runtime/semantic selection.'),
149
153
  function_explanations: z
@@ -208,6 +212,176 @@ export const reportSummarizeToolDefinition = {
208
212
  inputSchema: ReportSummarizeInputSchema,
209
213
  outputSchema: ReportSummarizeOutputSchema,
210
214
  };
215
+ function buildBinaryProfileSummary(binaryProfile) {
216
+ const lifecycleSurface = binaryProfile.lifecycle_surface || [];
217
+ const classFactorySurface = binaryProfile.com_profile.class_factory_surface || [];
218
+ const callbackSurface = binaryProfile.host_interaction_profile.callback_surface || [];
219
+ const parts = [
220
+ `Binary role profile suggests ${binaryProfile.binary_role} (confidence=${binaryProfile.role_confidence.toFixed(2)})`,
221
+ ];
222
+ if (binaryProfile.export_surface.total_exports > 0) {
223
+ parts.push(`exports=${binaryProfile.export_surface.total_exports}`);
224
+ }
225
+ if (binaryProfile.indicators.com_server.likely) {
226
+ parts.push('COM-like surface detected');
227
+ }
228
+ if (binaryProfile.indicators.service_binary.likely) {
229
+ parts.push('service indicators detected');
230
+ }
231
+ if (binaryProfile.export_dispatch_profile.likely_dispatch_model !== 'none') {
232
+ parts.push(`dispatch_model=${binaryProfile.export_dispatch_profile.likely_dispatch_model}`);
233
+ }
234
+ if (binaryProfile.export_dispatch_profile.registration_exports.length > 0) {
235
+ parts.push(`registration_exports=${binaryProfile.export_dispatch_profile.registration_exports.slice(0, 2).join(', ')}`);
236
+ }
237
+ if (lifecycleSurface.length > 0) {
238
+ parts.push(`dll_lifecycle=${lifecycleSurface.slice(0, 2).join(', ')}`);
239
+ }
240
+ if (binaryProfile.com_profile.class_factory_exports.length > 0) {
241
+ parts.push(`class_factory_exports=${binaryProfile.com_profile.class_factory_exports.slice(0, 2).join(', ')}`);
242
+ }
243
+ if (classFactorySurface.length > 0) {
244
+ parts.push(`class_factory_surface=${classFactorySurface.slice(0, 2).join(', ')}`);
245
+ }
246
+ if (binaryProfile.host_interaction_profile.callback_exports.length > 0) {
247
+ parts.push(`callback_exports=${binaryProfile.host_interaction_profile.callback_exports.slice(0, 2).join(', ')}`);
248
+ }
249
+ if (callbackSurface.length > 0) {
250
+ parts.push(`callback_surface=${callbackSurface.slice(0, 2).join(', ')}`);
251
+ }
252
+ if (binaryProfile.host_interaction_profile.likely_hosted) {
253
+ parts.push('host/plugin interaction surface detected');
254
+ }
255
+ if (binaryProfile.host_interaction_profile.host_hints.length > 0) {
256
+ parts.push(`host_hints=${binaryProfile.host_interaction_profile.host_hints.slice(0, 2).join(', ')}`);
257
+ }
258
+ if (binaryProfile.packed) {
259
+ parts.push('packing signals present');
260
+ }
261
+ return `${parts.join(', ')}.`;
262
+ }
263
+ function augmentWithBinaryProfile(triageData, binaryProfile) {
264
+ if (!binaryProfile) {
265
+ return triageData;
266
+ }
267
+ const summaryLine = buildBinaryProfileSummary(binaryProfile);
268
+ const lifecycleSurface = binaryProfile.lifecycle_surface || [];
269
+ const classFactorySurface = binaryProfile.com_profile.class_factory_surface || [];
270
+ const callbackSurface = binaryProfile.host_interaction_profile.callback_surface || [];
271
+ const evidenceLines = dedupe([
272
+ summaryLine,
273
+ ...binaryProfile.analysis_priorities.map((item) => `binary_profile_priority: ${item}`),
274
+ ...binaryProfile.export_dispatch_profile.registration_exports.map((item) => `binary_profile_surface: registration_export=${item}`),
275
+ ...lifecycleSurface.map((item) => `binary_profile_surface: dll_lifecycle=${item}`),
276
+ ...binaryProfile.com_profile.class_factory_exports.map((item) => `binary_profile_surface: class_factory_export=${item}`),
277
+ ...classFactorySurface.map((item) => `binary_profile_surface: class_factory_surface=${item}`),
278
+ ...binaryProfile.host_interaction_profile.callback_exports.map((item) => `binary_profile_surface: callback_export=${item}`),
279
+ ...callbackSurface.map((item) => `binary_profile_surface: callback_surface=${item}`),
280
+ ...binaryProfile.host_interaction_profile.host_hints.map((item) => `binary_profile_surface: host_hint=${item}`),
281
+ ]);
282
+ const recommendationSuffix = binaryProfile.analysis_priorities.length > 0
283
+ ? ` Binary role priorities: ${binaryProfile.analysis_priorities.join(', ')}.`
284
+ : '';
285
+ return {
286
+ ...triageData,
287
+ summary: `${triageData.summary} ${summaryLine}`,
288
+ evidence: dedupe([...triageData.evidence, ...evidenceLines]),
289
+ binary_profile: binaryProfile,
290
+ recommendation: `${triageData.recommendation}${recommendationSuffix}`.trim(),
291
+ inference: triageData.inference
292
+ ? {
293
+ ...triageData.inference,
294
+ hypotheses: dedupe([
295
+ ...triageData.inference.hypotheses,
296
+ `Binary role profile suggests ${binaryProfile.binary_role}.`,
297
+ ]),
298
+ }
299
+ : triageData.inference,
300
+ };
301
+ }
302
+ function buildRustProfileSummary(rustProfile) {
303
+ const parts = [
304
+ `Rust-focused analysis ${rustProfile.suspected_rust ? 'suggests a Rust-oriented binary' : 'did not strongly confirm Rust'} (confidence=${rustProfile.confidence.toFixed(2)})`,
305
+ ];
306
+ if (rustProfile.primary_runtime) {
307
+ parts.push(`runtime=${rustProfile.primary_runtime}`);
308
+ }
309
+ if (rustProfile.crate_hints.length > 0) {
310
+ parts.push(`crate_hints=${rustProfile.crate_hints.slice(0, 4).join(', ')}`);
311
+ }
312
+ if (rustProfile.recovered_function_count > 0) {
313
+ parts.push(`recovered_functions=${rustProfile.recovered_function_count}`);
314
+ }
315
+ if (rustProfile.recovered_symbol_count > 0) {
316
+ parts.push(`recovered_symbols=${rustProfile.recovered_symbol_count}`);
317
+ }
318
+ return `${parts.join(', ')}.`;
319
+ }
320
+ function augmentWithRustProfile(triageData, rustProfile) {
321
+ if (!rustProfile) {
322
+ return triageData;
323
+ }
324
+ const summaryLine = buildRustProfileSummary(rustProfile);
325
+ const rustPriorityLines = rustProfile.analysis_priorities.map((item) => `rust_analysis_priority: ${item}`);
326
+ const compilerArtifacts = {
327
+ ...(triageData.iocs.compiler_artifacts || {}),
328
+ cargo_paths: dedupe([
329
+ ...(triageData.iocs.compiler_artifacts?.cargo_paths || []),
330
+ ...rustProfile.cargo_paths,
331
+ ]),
332
+ rust_markers: dedupe([
333
+ ...(triageData.iocs.compiler_artifacts?.rust_markers || []),
334
+ ...rustProfile.rust_markers,
335
+ ]),
336
+ library_profile: rustProfile.library_profile || triageData.iocs.compiler_artifacts?.library_profile,
337
+ };
338
+ const recommendationSuffix = rustProfile.analysis_priorities.length > 0
339
+ ? ` Rust recovery priorities: ${rustProfile.analysis_priorities.join(', ')}.`
340
+ : '';
341
+ return {
342
+ ...triageData,
343
+ summary: `${triageData.summary} ${summaryLine}`.trim(),
344
+ iocs: {
345
+ ...triageData.iocs,
346
+ compiler_artifacts: compilerArtifacts,
347
+ },
348
+ evidence: dedupe([summaryLine, ...triageData.evidence, ...rustProfile.evidence, ...rustPriorityLines]),
349
+ rust_profile: rustProfile,
350
+ recommendation: `${triageData.recommendation}${recommendationSuffix}`.trim(),
351
+ inference: triageData.inference
352
+ ? {
353
+ ...triageData.inference,
354
+ hypotheses: dedupe([
355
+ ...triageData.inference.hypotheses,
356
+ rustProfile.suspected_rust
357
+ ? 'Rust/toolchain evidence is strong enough to prioritize non-Ghidra function recovery paths.'
358
+ : 'Rust/toolchain evidence remains weak; keep recovery paths heuristic.',
359
+ ]),
360
+ tooling_assessment: triageData.inference.tooling_assessment
361
+ ? {
362
+ ...triageData.inference.tooling_assessment,
363
+ framework_hints: dedupe([
364
+ ...triageData.inference.tooling_assessment.framework_hints,
365
+ ...rustProfile.crate_hints,
366
+ ]),
367
+ toolchain_markers: dedupe([
368
+ ...triageData.inference.tooling_assessment.toolchain_markers,
369
+ ...rustProfile.runtime_hints,
370
+ ]),
371
+ library_profile: triageData.inference.tooling_assessment.library_profile ||
372
+ rustProfile.library_profile,
373
+ }
374
+ : {
375
+ help_text_detected: false,
376
+ cli_surface_detected: false,
377
+ framework_hints: rustProfile.crate_hints,
378
+ toolchain_markers: rustProfile.runtime_hints,
379
+ library_profile: rustProfile.library_profile,
380
+ },
381
+ }
382
+ : triageData.inference,
383
+ };
384
+ }
211
385
  function toolMetrics(startTime) {
212
386
  return {
213
387
  elapsed_ms: Date.now() - startTime,
@@ -334,6 +508,18 @@ function augmentWithDynamicEvidence(triageData, dynamicEvidence) {
334
508
  buildEvidenceLayerHeadline(evidenceLineage),
335
509
  ...triageData.evidence,
336
510
  ...dynamicEvidence.evidence,
511
+ ...(dynamicEvidence.protections || []).length > 0
512
+ ? [`Runtime protections: ${(dynamicEvidence.protections || []).slice(0, 4).join(', ')}.`]
513
+ : [],
514
+ ...(dynamicEvidence.region_owners || []).length > 0
515
+ ? [`Runtime region owners: ${(dynamicEvidence.region_owners || []).slice(0, 4).join(', ')}.`]
516
+ : [],
517
+ ...(dynamicEvidence.observed_modules || []).length > 0
518
+ ? [`Runtime observed modules: ${(dynamicEvidence.observed_modules || []).slice(0, 4).join(', ')}.`]
519
+ : [],
520
+ ...(dynamicEvidence.segment_names || []).length > 0
521
+ ? [`Runtime segment names: ${(dynamicEvidence.segment_names || []).slice(0, 4).join(', ')}.`]
522
+ : [],
337
523
  ]),
338
524
  evidence_lineage: evidenceLineage,
339
525
  evidence_weights: {
@@ -348,6 +534,12 @@ function augmentWithDynamicEvidence(triageData, dynamicEvidence) {
348
534
  hypotheses: dedupe([
349
535
  ...triageData.inference.hypotheses,
350
536
  ...dynamicEvidence.stages.map((item) => `Imported runtime evidence indicates stage: ${item}.`),
537
+ ...(dynamicEvidence.observed_modules || [])
538
+ .slice(0, 3)
539
+ .map((item) => `Imported runtime evidence observed module: ${item}.`),
540
+ ...(dynamicEvidence.segment_names || [])
541
+ .slice(0, 3)
542
+ .map((item) => `Imported runtime evidence observed segment: ${item}.`),
351
543
  ]),
352
544
  false_positive_risks: dedupe([
353
545
  ...triageData.inference.false_positive_risks,
@@ -367,7 +559,7 @@ function augmentWithDynamicEvidence(triageData, dynamicEvidence) {
367
559
  },
368
560
  };
369
561
  }
370
- function createMinimalDotnetFallback(triageResult, startTime, functionExplanations = [], provenance, evidenceScope = 'all') {
562
+ function createMinimalDotnetFallback(triageResult, startTime, functionExplanations = [], provenance, evidenceScope = 'all', binaryProfile, rustProfile) {
371
563
  const triageErrors = triageResult.errors || [];
372
564
  const warnings = [
373
565
  'report.summarize(mode=dotnet) is not fully implemented; returned degraded fallback output.',
@@ -390,8 +582,12 @@ function createMinimalDotnetFallback(triageResult, startTime, functionExplanatio
390
582
  evidence: [
391
583
  'Dotnet mode unavailable; triage fallback failed.',
392
584
  ...triageErrors.map((item) => `triage_error: ${item}`),
585
+ ...(binaryProfile ? [buildBinaryProfileSummary(binaryProfile)] : []),
586
+ ...(rustProfile ? [buildRustProfileSummary(rustProfile)] : []),
393
587
  ],
394
588
  confidence_semantics: buildAssessmentConfidencePayload(0.2, evidenceScope),
589
+ binary_profile: binaryProfile,
590
+ rust_profile: rustProfile,
395
591
  provenance,
396
592
  function_explanations: functionExplanations.length > 0 ? functionExplanations : undefined,
397
593
  inference: {
@@ -401,20 +597,20 @@ function createMinimalDotnetFallback(triageResult, startTime, functionExplanatio
401
597
  ],
402
598
  false_positive_risks: ['No triage evidence is available in this degraded fallback result.'],
403
599
  },
404
- recommendation: 'Re-run after ensuring workspace/original sample file exists, then use workflow.reconstruct or dotnet.reconstruct.export for .NET-specific structure.',
600
+ recommendation: `Re-run after ensuring workspace/original sample file exists, then use workflow.reconstruct or dotnet.reconstruct.export for .NET-specific structure.${binaryProfile?.analysis_priorities?.length ? ` Binary role priorities: ${binaryProfile.analysis_priorities.join(', ')}.` : ''}${rustProfile?.analysis_priorities?.length ? ` Rust recovery priorities: ${rustProfile.analysis_priorities.join(', ')}.` : ''}`,
405
601
  },
406
602
  warnings,
407
603
  errors: triageErrors.length > 0 ? triageErrors : undefined,
408
604
  metrics: toolMetrics(startTime),
409
605
  };
410
606
  }
411
- function createDynamicEvidenceFallback(dynamicEvidence, triageResult, startTime, functionExplanations = [], provenance, evidenceScope = 'all') {
607
+ function createDynamicEvidenceFallback(dynamicEvidence, triageResult, startTime, functionExplanations = [], provenance, evidenceScope = 'all', binaryProfile, rustProfile) {
412
608
  const evidenceLineage = buildEvidenceLineage(dynamicEvidence);
413
609
  const threatLevel = dynamicEvidence.high_signal_apis.length > 0 ? 'suspicious' : dynamicEvidence.executed ? 'suspicious' : 'unknown';
414
610
  return {
415
611
  ok: true,
416
612
  data: {
417
- summary: `Triage pipeline failed, but imported runtime evidence is available. ${buildEvidenceLayerHeadline(evidenceLineage)} ${dynamicEvidence.summary}`,
613
+ summary: `Triage pipeline failed, but imported runtime evidence is available. ${buildEvidenceLayerHeadline(evidenceLineage)} ${dynamicEvidence.summary}${binaryProfile ? ` ${buildBinaryProfileSummary(binaryProfile)}` : ''}${rustProfile ? ` ${buildRustProfileSummary(rustProfile)}` : ''}`,
418
614
  confidence: dynamicEvidence.executed ? 0.66 : 0.5,
419
615
  threat_level: threatLevel,
420
616
  iocs: {
@@ -425,9 +621,16 @@ function createDynamicEvidenceFallback(dynamicEvidence, triageResult, startTime,
425
621
  suspicious_apis: dynamicEvidence.high_signal_apis,
426
622
  },
427
623
  },
428
- evidence: dedupe([buildEvidenceLayerHeadline(evidenceLineage), ...dynamicEvidence.evidence]),
624
+ evidence: dedupe([
625
+ buildEvidenceLayerHeadline(evidenceLineage),
626
+ ...dynamicEvidence.evidence,
627
+ ...(binaryProfile ? [buildBinaryProfileSummary(binaryProfile)] : []),
628
+ ...(rustProfile ? [buildRustProfileSummary(rustProfile)] : []),
629
+ ]),
429
630
  evidence_lineage: evidenceLineage,
430
631
  confidence_semantics: buildAssessmentConfidencePayload(dynamicEvidence.executed ? 0.66 : 0.5, evidenceScope, evidenceLineage),
632
+ binary_profile: binaryProfile,
633
+ rust_profile: rustProfile,
431
634
  provenance,
432
635
  function_explanations: functionExplanations.length > 0 ? functionExplanations : undefined,
433
636
  evidence_weights: {
@@ -444,7 +647,7 @@ function createDynamicEvidenceFallback(dynamicEvidence, triageResult, startTime,
444
647
  : 'Memory/hybrid evidence is suggestive but not equivalent to a fully executed trace.',
445
648
  ],
446
649
  },
447
- recommendation: 'Correlate imported runtime evidence with code.functions.search, code.functions.reconstruct, and code.reconstruct.export to assign concrete function ownership.',
650
+ recommendation: `Correlate imported runtime evidence with code.functions.search, code.functions.reconstruct, and code.reconstruct.export to assign concrete function ownership.${binaryProfile?.analysis_priorities?.length ? ` Binary role priorities: ${binaryProfile.analysis_priorities.join(', ')}.` : ''}${rustProfile?.analysis_priorities?.length ? ` Rust recovery priorities: ${rustProfile.analysis_priorities.join(', ')}.` : ''}`,
448
651
  },
449
652
  warnings: [
450
653
  'Triage pipeline failed; returned imported runtime-evidence fallback.',
@@ -456,6 +659,10 @@ function createDynamicEvidenceFallback(dynamicEvidence, triageResult, startTime,
456
659
  }
457
660
  export function createReportSummarizeHandler(workspaceManager, database, cacheManager, deps) {
458
661
  const triageHandler = deps?.triageHandler || createTriageWorkflowHandler(workspaceManager, database, cacheManager);
662
+ const binaryRoleProfileHandler = deps?.binaryRoleProfileHandler ||
663
+ createBinaryRoleProfileHandler(workspaceManager, database, cacheManager);
664
+ const rustBinaryAnalyzeHandler = deps?.rustBinaryAnalyzeHandler ||
665
+ createRustBinaryAnalyzeHandler(workspaceManager, database, cacheManager);
459
666
  return async (args) => {
460
667
  const startTime = Date.now();
461
668
  try {
@@ -467,10 +674,31 @@ export function createReportSummarizeHandler(workspaceManager, database, cacheMa
467
674
  errors: [`Sample not found: ${input.sample_id}`],
468
675
  };
469
676
  }
677
+ const warnings = [];
470
678
  const dynamicEvidence = await loadDynamicTraceEvidence(workspaceManager, database, input.sample_id, {
471
679
  evidenceScope: input.evidence_scope,
472
680
  sessionTag: input.evidence_session_tag,
473
681
  });
682
+ const binaryRoleProfileResult = await binaryRoleProfileHandler({ sample_id: input.sample_id });
683
+ const binaryProfile = binaryRoleProfileResult.ok && binaryRoleProfileResult.data
684
+ ? binaryRoleProfileResult.data
685
+ : undefined;
686
+ if (!binaryRoleProfileResult.ok) {
687
+ warnings.push(`binary.role.profile unavailable: ${(binaryRoleProfileResult.errors || ['unknown error']).join('; ')}`);
688
+ }
689
+ else if (binaryRoleProfileResult.warnings?.length) {
690
+ warnings.push(...binaryRoleProfileResult.warnings.map((item) => `binary.role.profile: ${item}`));
691
+ }
692
+ const rustBinaryAnalyzeResult = await rustBinaryAnalyzeHandler({ sample_id: input.sample_id });
693
+ const rustProfile = rustBinaryAnalyzeResult.ok && rustBinaryAnalyzeResult.data
694
+ ? rustBinaryAnalyzeResult.data
695
+ : undefined;
696
+ if (!rustBinaryAnalyzeResult.ok) {
697
+ warnings.push(`rust_binary.analyze unavailable: ${(rustBinaryAnalyzeResult.errors || ['unknown error']).join('; ')}`);
698
+ }
699
+ else if (rustBinaryAnalyzeResult.warnings?.length) {
700
+ warnings.push(...rustBinaryAnalyzeResult.warnings.map((item) => `rust_binary.analyze: ${item}`));
701
+ }
474
702
  const functionExplanationBundle = await loadFunctionExplanationSummaries(workspaceManager, database, input.sample_id, {
475
703
  scope: input.semantic_scope,
476
704
  sessionTag: input.semantic_session_tag,
@@ -502,7 +730,7 @@ export function createReportSummarizeHandler(workspaceManager, database, cacheMa
502
730
  if (input.mode === 'triage') {
503
731
  if (!triageResult.ok || !triageResult.data) {
504
732
  if (dynamicEvidence) {
505
- return createDynamicEvidenceFallback(dynamicEvidence, triageResult, startTime, functionExplanations, provenance, input.evidence_scope);
733
+ return createDynamicEvidenceFallback(dynamicEvidence, triageResult, startTime, functionExplanations, provenance, input.evidence_scope, binaryProfile, rustProfile);
506
734
  }
507
735
  return {
508
736
  ok: false,
@@ -515,7 +743,9 @@ export function createReportSummarizeHandler(workspaceManager, database, cacheMa
515
743
  const triageData = dynamicEvidence
516
744
  ? augmentWithDynamicEvidence(triageDataBase, dynamicEvidence)
517
745
  : triageDataBase;
518
- const enrichedTriageData = augmentWithFunctionExplanations(triageData, functionExplanations);
746
+ const binaryEnrichedTriageData = augmentWithBinaryProfile(triageData, binaryProfile);
747
+ const rustEnrichedTriageData = augmentWithRustProfile(binaryEnrichedTriageData, rustProfile);
748
+ const enrichedTriageData = augmentWithFunctionExplanations(rustEnrichedTriageData, functionExplanations);
519
749
  return {
520
750
  ok: true,
521
751
  data: {
@@ -526,6 +756,8 @@ export function createReportSummarizeHandler(workspaceManager, database, cacheMa
526
756
  evidence: enrichedTriageData.evidence,
527
757
  evidence_lineage: enrichedTriageData.evidence_lineage || buildEvidenceLineage(dynamicEvidence),
528
758
  confidence_semantics: buildAssessmentConfidencePayload(enrichedTriageData.confidence, input.evidence_scope, enrichedTriageData.evidence_lineage || buildEvidenceLineage(dynamicEvidence)),
759
+ binary_profile: enrichedTriageData.binary_profile,
760
+ rust_profile: enrichedTriageData.rust_profile,
529
761
  provenance,
530
762
  selection_diffs: Object.keys(selectionDiffs).length > 0 ? selectionDiffs : undefined,
531
763
  function_explanations: enrichedTriageData.function_explanations,
@@ -535,18 +767,19 @@ export function createReportSummarizeHandler(workspaceManager, database, cacheMa
535
767
  },
536
768
  warnings: dynamicEvidence
537
769
  ? dedupe([
770
+ ...warnings,
538
771
  ...(triageResult.warnings || []),
539
772
  `Merged imported runtime evidence from ${dynamicEvidence.artifact_count} artifact(s) using scope=${input.evidence_scope}${input.evidence_session_tag ? ` selector=${input.evidence_session_tag}` : ''}.`,
540
773
  dynamicEvidence.scope_note || '',
541
774
  ])
542
- : triageResult.warnings,
775
+ : dedupe([...warnings, ...(triageResult.warnings || [])]),
543
776
  errors: triageResult.errors,
544
777
  metrics: toolMetrics(startTime),
545
778
  };
546
779
  }
547
780
  if (input.mode === 'dotnet') {
548
781
  if (!triageResult.ok || !triageResult.data) {
549
- return createMinimalDotnetFallback(triageResult, startTime, functionExplanations, provenance, input.evidence_scope);
782
+ return createMinimalDotnetFallback(triageResult, startTime, functionExplanations, provenance, input.evidence_scope, binaryProfile, rustProfile);
550
783
  }
551
784
  const triageData = triageResult.data;
552
785
  return {
@@ -563,6 +796,8 @@ export function createReportSummarizeHandler(workspaceManager, database, cacheMa
563
796
  ],
564
797
  evidence_lineage: triageData.evidence_lineage || buildEvidenceLineage(dynamicEvidence),
565
798
  confidence_semantics: buildAssessmentConfidencePayload(Math.max(0.05, Math.min(0.95, triageData.confidence * 0.92)), input.evidence_scope, triageData.evidence_lineage || buildEvidenceLineage(dynamicEvidence)),
799
+ binary_profile: binaryProfile,
800
+ rust_profile: rustProfile,
566
801
  provenance,
567
802
  selection_diffs: Object.keys(selectionDiffs).length > 0 ? selectionDiffs : undefined,
568
803
  function_explanations: functionExplanations.length > 0 ? functionExplanations : undefined,
@@ -572,6 +807,7 @@ export function createReportSummarizeHandler(workspaceManager, database, cacheMa
572
807
  'dotnet.reconstruct.export / workflow.reconstruct for .NET-specific structure.',
573
808
  },
574
809
  warnings: [
810
+ ...warnings,
575
811
  'report.summarize(mode=dotnet) not fully implemented; returned triage fallback.',
576
812
  ...(triageResult.warnings || []),
577
813
  ],
@@ -83,10 +83,6 @@ export declare const RuntimeDetectOutputSchema: z.ZodObject<{
83
83
  }>>;
84
84
  }, "strip", z.ZodTypeAny, {
85
85
  ok: boolean;
86
- metrics?: {
87
- elapsed_ms: number;
88
- tool: string;
89
- } | undefined;
90
86
  data?: {
91
87
  is_dotnet: boolean;
92
88
  dotnet_version: string | null;
@@ -98,15 +94,15 @@ export declare const RuntimeDetectOutputSchema: z.ZodObject<{
98
94
  }[];
99
95
  import_dlls: string[];
100
96
  } | undefined;
97
+ metrics?: {
98
+ elapsed_ms: number;
99
+ tool: string;
100
+ } | undefined;
101
101
  warnings?: string[] | undefined;
102
102
  errors?: string[] | undefined;
103
103
  artifacts?: any[] | undefined;
104
104
  }, {
105
105
  ok: boolean;
106
- metrics?: {
107
- elapsed_ms: number;
108
- tool: string;
109
- } | undefined;
110
106
  data?: {
111
107
  is_dotnet: boolean;
112
108
  dotnet_version: string | null;
@@ -118,6 +114,10 @@ export declare const RuntimeDetectOutputSchema: z.ZodObject<{
118
114
  }[];
119
115
  import_dlls: string[];
120
116
  } | undefined;
117
+ metrics?: {
118
+ elapsed_ms: number;
119
+ tool: string;
120
+ } | undefined;
121
121
  warnings?: string[] | undefined;
122
122
  errors?: string[] | undefined;
123
123
  artifacts?: any[] | undefined;
@@ -5,11 +5,11 @@
5
5
  */
6
6
  import { z } from 'zod';
7
7
  import { spawn } from 'child_process';
8
- import path from 'path';
9
8
  import { v4 as uuidv4 } from 'uuid';
10
9
  import { generateCacheKey } from '../cache-manager.js';
11
10
  import { resolvePackagePath } from '../runtime-paths.js';
12
11
  import { lookupCachedResult, formatCacheWarning } from './cache-observability.js';
12
+ import { resolvePrimarySamplePath } from '../sample-workspace.js';
13
13
  // ============================================================================
14
14
  // Constants
15
15
  // ============================================================================
@@ -174,17 +174,7 @@ export function createRuntimeDetectHandler(workspaceManager, database, cacheMana
174
174
  }
175
175
  }
176
176
  // 3. Get sample path from workspace
177
- const workspace = await workspaceManager.getWorkspace(input.sample_id);
178
- // Find the sample file in the original directory
179
- const fs = await import('fs/promises');
180
- const files = await fs.readdir(workspace.original);
181
- if (files.length === 0) {
182
- return {
183
- ok: false,
184
- errors: ['Sample file not found in workspace'],
185
- };
186
- }
187
- const samplePath = path.join(workspace.original, files[0]);
177
+ const { samplePath } = await resolvePrimarySamplePath(workspaceManager, input.sample_id);
188
178
  // 4. Prepare worker request
189
179
  const workerRequest = {
190
180
  job_id: uuidv4(),