windows-exe-decompiler-mcp-server 0.1.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.
Files changed (190) hide show
  1. package/CODEX_INSTALLATION.md +69 -0
  2. package/COPILOT_INSTALLATION.md +77 -0
  3. package/LICENSE +21 -0
  4. package/README.md +314 -0
  5. package/bin/windows-exe-decompiler-mcp-server.js +3 -0
  6. package/dist/analysis-provenance.d.ts +184 -0
  7. package/dist/analysis-provenance.js +74 -0
  8. package/dist/analysis-task-runner.d.ts +31 -0
  9. package/dist/analysis-task-runner.js +160 -0
  10. package/dist/artifact-inventory.d.ts +23 -0
  11. package/dist/artifact-inventory.js +175 -0
  12. package/dist/cache-manager.d.ts +128 -0
  13. package/dist/cache-manager.js +454 -0
  14. package/dist/confidence-semantics.d.ts +66 -0
  15. package/dist/confidence-semantics.js +122 -0
  16. package/dist/config.d.ts +335 -0
  17. package/dist/config.js +193 -0
  18. package/dist/database.d.ts +227 -0
  19. package/dist/database.js +601 -0
  20. package/dist/decompiler-worker.d.ts +441 -0
  21. package/dist/decompiler-worker.js +1962 -0
  22. package/dist/dynamic-trace.d.ts +95 -0
  23. package/dist/dynamic-trace.js +629 -0
  24. package/dist/env-validator.d.ts +15 -0
  25. package/dist/env-validator.js +249 -0
  26. package/dist/error-handler.d.ts +28 -0
  27. package/dist/error-handler.example.d.ts +22 -0
  28. package/dist/error-handler.example.js +141 -0
  29. package/dist/error-handler.js +139 -0
  30. package/dist/ghidra-analysis-status.d.ts +49 -0
  31. package/dist/ghidra-analysis-status.js +178 -0
  32. package/dist/ghidra-config.d.ts +134 -0
  33. package/dist/ghidra-config.js +464 -0
  34. package/dist/index.d.ts +9 -0
  35. package/dist/index.js +200 -0
  36. package/dist/job-queue.d.ts +169 -0
  37. package/dist/job-queue.js +407 -0
  38. package/dist/logger.d.ts +106 -0
  39. package/dist/logger.js +176 -0
  40. package/dist/policy-guard.d.ts +115 -0
  41. package/dist/policy-guard.js +243 -0
  42. package/dist/process-output.d.ts +15 -0
  43. package/dist/process-output.js +90 -0
  44. package/dist/prompts/function-explanation-review.d.ts +5 -0
  45. package/dist/prompts/function-explanation-review.js +64 -0
  46. package/dist/prompts/semantic-name-review.d.ts +5 -0
  47. package/dist/prompts/semantic-name-review.js +63 -0
  48. package/dist/runtime-correlation.d.ts +34 -0
  49. package/dist/runtime-correlation.js +279 -0
  50. package/dist/runtime-paths.d.ts +3 -0
  51. package/dist/runtime-paths.js +11 -0
  52. package/dist/selection-diff.d.ts +667 -0
  53. package/dist/selection-diff.js +53 -0
  54. package/dist/semantic-name-suggestion-artifacts.d.ts +116 -0
  55. package/dist/semantic-name-suggestion-artifacts.js +314 -0
  56. package/dist/server.d.ts +129 -0
  57. package/dist/server.js +578 -0
  58. package/dist/tools/artifact-read.d.ts +235 -0
  59. package/dist/tools/artifact-read.js +317 -0
  60. package/dist/tools/artifacts-diff.d.ts +728 -0
  61. package/dist/tools/artifacts-diff.js +304 -0
  62. package/dist/tools/artifacts-list.d.ts +515 -0
  63. package/dist/tools/artifacts-list.js +389 -0
  64. package/dist/tools/attack-map.d.ts +290 -0
  65. package/dist/tools/attack-map.js +519 -0
  66. package/dist/tools/cache-observability.d.ts +4 -0
  67. package/dist/tools/cache-observability.js +36 -0
  68. package/dist/tools/code-function-cfg.d.ts +50 -0
  69. package/dist/tools/code-function-cfg.js +102 -0
  70. package/dist/tools/code-function-decompile.d.ts +55 -0
  71. package/dist/tools/code-function-decompile.js +103 -0
  72. package/dist/tools/code-function-disassemble.d.ts +43 -0
  73. package/dist/tools/code-function-disassemble.js +185 -0
  74. package/dist/tools/code-function-explain-apply.d.ts +255 -0
  75. package/dist/tools/code-function-explain-apply.js +225 -0
  76. package/dist/tools/code-function-explain-prepare.d.ts +535 -0
  77. package/dist/tools/code-function-explain-prepare.js +276 -0
  78. package/dist/tools/code-function-explain-review.d.ts +397 -0
  79. package/dist/tools/code-function-explain-review.js +589 -0
  80. package/dist/tools/code-function-rename-apply.d.ts +248 -0
  81. package/dist/tools/code-function-rename-apply.js +220 -0
  82. package/dist/tools/code-function-rename-prepare.d.ts +506 -0
  83. package/dist/tools/code-function-rename-prepare.js +279 -0
  84. package/dist/tools/code-function-rename-review.d.ts +574 -0
  85. package/dist/tools/code-function-rename-review.js +761 -0
  86. package/dist/tools/code-functions-list.d.ts +37 -0
  87. package/dist/tools/code-functions-list.js +91 -0
  88. package/dist/tools/code-functions-rank.d.ts +34 -0
  89. package/dist/tools/code-functions-rank.js +90 -0
  90. package/dist/tools/code-functions-reconstruct.d.ts +2725 -0
  91. package/dist/tools/code-functions-reconstruct.js +2807 -0
  92. package/dist/tools/code-functions-search.d.ts +39 -0
  93. package/dist/tools/code-functions-search.js +90 -0
  94. package/dist/tools/code-reconstruct-export.d.ts +1212 -0
  95. package/dist/tools/code-reconstruct-export.js +4002 -0
  96. package/dist/tools/code-reconstruct-plan.d.ts +274 -0
  97. package/dist/tools/code-reconstruct-plan.js +342 -0
  98. package/dist/tools/dotnet-metadata-extract.d.ts +541 -0
  99. package/dist/tools/dotnet-metadata-extract.js +355 -0
  100. package/dist/tools/dotnet-reconstruct-export.d.ts +567 -0
  101. package/dist/tools/dotnet-reconstruct-export.js +1151 -0
  102. package/dist/tools/dotnet-types-list.d.ts +325 -0
  103. package/dist/tools/dotnet-types-list.js +201 -0
  104. package/dist/tools/dynamic-dependencies.d.ts +115 -0
  105. package/dist/tools/dynamic-dependencies.js +213 -0
  106. package/dist/tools/dynamic-memory-import.d.ts +10 -0
  107. package/dist/tools/dynamic-memory-import.js +567 -0
  108. package/dist/tools/dynamic-trace-import.d.ts +10 -0
  109. package/dist/tools/dynamic-trace-import.js +235 -0
  110. package/dist/tools/entrypoint-fallback-disasm.d.ts +30 -0
  111. package/dist/tools/entrypoint-fallback-disasm.js +89 -0
  112. package/dist/tools/ghidra-analyze.d.ts +88 -0
  113. package/dist/tools/ghidra-analyze.js +208 -0
  114. package/dist/tools/ghidra-health.d.ts +37 -0
  115. package/dist/tools/ghidra-health.js +212 -0
  116. package/dist/tools/ioc-export.d.ts +209 -0
  117. package/dist/tools/ioc-export.js +542 -0
  118. package/dist/tools/packer-detect.d.ts +165 -0
  119. package/dist/tools/packer-detect.js +284 -0
  120. package/dist/tools/pe-exports-extract.d.ts +175 -0
  121. package/dist/tools/pe-exports-extract.js +253 -0
  122. package/dist/tools/pe-fingerprint.d.ts +234 -0
  123. package/dist/tools/pe-fingerprint.js +269 -0
  124. package/dist/tools/pe-imports-extract.d.ts +105 -0
  125. package/dist/tools/pe-imports-extract.js +245 -0
  126. package/dist/tools/report-generate.d.ts +157 -0
  127. package/dist/tools/report-generate.js +457 -0
  128. package/dist/tools/report-summarize.d.ts +2131 -0
  129. package/dist/tools/report-summarize.js +596 -0
  130. package/dist/tools/runtime-detect.d.ts +135 -0
  131. package/dist/tools/runtime-detect.js +247 -0
  132. package/dist/tools/sample-ingest.d.ts +94 -0
  133. package/dist/tools/sample-ingest.js +327 -0
  134. package/dist/tools/sample-profile-get.d.ts +183 -0
  135. package/dist/tools/sample-profile-get.js +121 -0
  136. package/dist/tools/sandbox-execute.d.ts +441 -0
  137. package/dist/tools/sandbox-execute.js +392 -0
  138. package/dist/tools/strings-extract.d.ts +375 -0
  139. package/dist/tools/strings-extract.js +314 -0
  140. package/dist/tools/strings-floss-decode.d.ts +143 -0
  141. package/dist/tools/strings-floss-decode.js +259 -0
  142. package/dist/tools/system-health.d.ts +434 -0
  143. package/dist/tools/system-health.js +446 -0
  144. package/dist/tools/task-cancel.d.ts +21 -0
  145. package/dist/tools/task-cancel.js +70 -0
  146. package/dist/tools/task-status.d.ts +27 -0
  147. package/dist/tools/task-status.js +106 -0
  148. package/dist/tools/task-sweep.d.ts +22 -0
  149. package/dist/tools/task-sweep.js +77 -0
  150. package/dist/tools/tool-help.d.ts +340 -0
  151. package/dist/tools/tool-help.js +261 -0
  152. package/dist/tools/yara-scan.d.ts +554 -0
  153. package/dist/tools/yara-scan.js +313 -0
  154. package/dist/types.d.ts +266 -0
  155. package/dist/types.js +41 -0
  156. package/dist/worker-pool.d.ts +204 -0
  157. package/dist/worker-pool.js +650 -0
  158. package/dist/workflows/deep-static.d.ts +104 -0
  159. package/dist/workflows/deep-static.js +276 -0
  160. package/dist/workflows/function-explanation-review.d.ts +655 -0
  161. package/dist/workflows/function-explanation-review.js +440 -0
  162. package/dist/workflows/reconstruct.d.ts +2053 -0
  163. package/dist/workflows/reconstruct.js +666 -0
  164. package/dist/workflows/semantic-name-review.d.ts +2418 -0
  165. package/dist/workflows/semantic-name-review.js +521 -0
  166. package/dist/workflows/triage.d.ts +659 -0
  167. package/dist/workflows/triage.js +1374 -0
  168. package/dist/workspace-manager.d.ts +150 -0
  169. package/dist/workspace-manager.js +411 -0
  170. package/ghidra_scripts/DecompileFunction.java +487 -0
  171. package/ghidra_scripts/DecompileFunction.py +150 -0
  172. package/ghidra_scripts/ExtractCFG.java +256 -0
  173. package/ghidra_scripts/ExtractCFG.py +233 -0
  174. package/ghidra_scripts/ExtractFunctions.java +442 -0
  175. package/ghidra_scripts/ExtractFunctions.py +101 -0
  176. package/ghidra_scripts/README.md +125 -0
  177. package/ghidra_scripts/SearchFunctionReferences.java +380 -0
  178. package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -0
  179. package/helpers/DotNetMetadataProbe/Program.cs +566 -0
  180. package/install-to-codex.ps1 +178 -0
  181. package/install-to-copilot.ps1 +303 -0
  182. package/package.json +101 -0
  183. package/requirements.txt +9 -0
  184. package/workers/requirements-dynamic.txt +11 -0
  185. package/workers/requirements.txt +8 -0
  186. package/workers/speakeasy_compat.py +175 -0
  187. package/workers/static_worker.py +5183 -0
  188. package/workers/yara_rules/default.yar +33 -0
  189. package/workers/yara_rules/malware_families.yar +93 -0
  190. package/workers/yara_rules/packers.yar +80 -0
@@ -0,0 +1,666 @@
1
+ /**
2
+ * Reconstruction workflow implementation
3
+ * One-shot orchestration for source-like reconstruction across native/.NET paths.
4
+ */
5
+ import { z } from 'zod';
6
+ import { generateCacheKey } from '../cache-manager.js';
7
+ import { lookupCachedResult, formatCacheWarning } from '../tools/cache-observability.js';
8
+ import { createRuntimeDetectHandler } from '../tools/runtime-detect.js';
9
+ import { createCodeReconstructPlanHandler } from '../tools/code-reconstruct-plan.js';
10
+ import { createCodeReconstructExportHandler } from '../tools/code-reconstruct-export.js';
11
+ import { createDotNetReconstructExportHandler } from '../tools/dotnet-reconstruct-export.js';
12
+ import { findBestGhidraAnalysis } from '../ghidra-analysis-status.js';
13
+ import { loadDynamicTraceEvidence } from '../dynamic-trace.js';
14
+ import { loadSemanticFunctionExplanationIndex, loadSemanticNameSuggestionIndex, } from '../semantic-name-suggestion-artifacts.js';
15
+ import { AnalysisProvenanceSchema, buildRuntimeArtifactProvenance, buildSemanticArtifactProvenance, } from '../analysis-provenance.js';
16
+ import { AnalysisSelectionDiffSchema, buildArtifactSelectionDiff, } from '../selection-diff.js';
17
+ const TOOL_NAME = 'workflow.reconstruct';
18
+ const TOOL_VERSION = '0.1.3';
19
+ const CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
20
+ export const ReconstructWorkflowInputSchema = z.object({
21
+ sample_id: z.string().describe('Sample ID (format: sha256:<hex>)'),
22
+ path: z
23
+ .enum(['auto', 'native', 'dotnet'])
24
+ .default('auto')
25
+ .describe('Routing strategy for reconstruction path'),
26
+ topk: z
27
+ .number()
28
+ .int()
29
+ .min(1)
30
+ .max(40)
31
+ .default(16)
32
+ .describe('Top-K high-value functions used by export tools'),
33
+ export_name: z
34
+ .string()
35
+ .min(1)
36
+ .max(64)
37
+ .optional()
38
+ .describe('Optional export folder name'),
39
+ validate_build: z
40
+ .boolean()
41
+ .default(true)
42
+ .describe('For native path, compile the exported C skeleton when clang is available'),
43
+ run_harness: z
44
+ .boolean()
45
+ .default(true)
46
+ .describe('For native path, execute reconstruct_harness after a successful build'),
47
+ compiler_path: z
48
+ .string()
49
+ .min(1)
50
+ .max(260)
51
+ .optional()
52
+ .describe('Optional explicit clang compiler path for native validation'),
53
+ build_timeout_ms: z
54
+ .number()
55
+ .int()
56
+ .min(5000)
57
+ .max(300000)
58
+ .default(60000)
59
+ .describe('Timeout for native clang build validation in milliseconds'),
60
+ run_timeout_ms: z
61
+ .number()
62
+ .int()
63
+ .min(5000)
64
+ .max(300000)
65
+ .default(30000)
66
+ .describe('Timeout for reconstruct_harness execution in milliseconds'),
67
+ evidence_scope: z
68
+ .enum(['all', 'latest', 'session'])
69
+ .default('all')
70
+ .describe('Runtime evidence scope forwarded to downstream reconstruct/export tools'),
71
+ evidence_session_tag: z
72
+ .string()
73
+ .optional()
74
+ .describe('Optional runtime evidence session selector used when evidence_scope=session or to narrow all/latest results'),
75
+ semantic_scope: z
76
+ .enum(['all', 'latest', 'session'])
77
+ .default('all')
78
+ .describe('Semantic review artifact scope forwarded to native reconstruct/export tools'),
79
+ semantic_session_tag: z
80
+ .string()
81
+ .optional()
82
+ .describe('Optional semantic review session selector used when semantic_scope=session or to narrow all/latest results'),
83
+ compare_evidence_scope: z
84
+ .enum(['all', 'latest', 'session'])
85
+ .optional()
86
+ .describe('Optional baseline runtime evidence scope used to compare this workflow result against another runtime artifact selection'),
87
+ compare_evidence_session_tag: z
88
+ .string()
89
+ .optional()
90
+ .describe('Optional baseline runtime evidence session selector used when compare_evidence_scope=session'),
91
+ compare_semantic_scope: z
92
+ .enum(['all', 'latest', 'session'])
93
+ .optional()
94
+ .describe('Optional baseline semantic artifact scope used to compare this workflow result against another naming/explanation selection'),
95
+ compare_semantic_session_tag: z
96
+ .string()
97
+ .optional()
98
+ .describe('Optional baseline semantic artifact session selector used when compare_semantic_scope=session'),
99
+ include_plan: z
100
+ .boolean()
101
+ .default(true)
102
+ .describe('Include code.reconstruct.plan stage in the workflow'),
103
+ include_obfuscation_fallback: z
104
+ .boolean()
105
+ .default(true)
106
+ .describe('When routing to .NET path, generate IL fallback notes when needed'),
107
+ fallback_on_error: z
108
+ .boolean()
109
+ .default(true)
110
+ .describe('When primary export path fails, automatically try the alternative path'),
111
+ allow_partial: z
112
+ .boolean()
113
+ .default(true)
114
+ .describe('When all export paths fail, still return runtime/plan as partial output'),
115
+ reuse_cached: z
116
+ .boolean()
117
+ .default(true)
118
+ .describe('Reuse cached workflow result for identical inputs'),
119
+ })
120
+ .refine((value) => value.evidence_scope !== 'session' || Boolean(value.evidence_session_tag?.trim()), {
121
+ message: 'evidence_session_tag is required when evidence_scope=session',
122
+ path: ['evidence_session_tag'],
123
+ })
124
+ .refine((value) => value.semantic_scope !== 'session' || Boolean(value.semantic_session_tag?.trim()), {
125
+ message: 'semantic_session_tag is required when semantic_scope=session',
126
+ path: ['semantic_session_tag'],
127
+ })
128
+ .refine((value) => value.compare_evidence_scope !== 'session' || Boolean(value.compare_evidence_session_tag?.trim()), {
129
+ message: 'compare_evidence_session_tag is required when compare_evidence_scope=session',
130
+ path: ['compare_evidence_session_tag'],
131
+ })
132
+ .refine((value) => value.compare_semantic_scope !== 'session' || Boolean(value.compare_semantic_session_tag?.trim()), {
133
+ message: 'compare_semantic_session_tag is required when compare_semantic_scope=session',
134
+ path: ['compare_semantic_session_tag'],
135
+ });
136
+ const RuntimeSummarySchema = z.object({
137
+ is_dotnet: z.boolean().nullable(),
138
+ dotnet_version: z.string().nullable(),
139
+ target_framework: z.string().nullable(),
140
+ primary_runtime: z.string().nullable(),
141
+ });
142
+ const PlanSummarySchema = z.object({
143
+ feasibility: z.enum(['high', 'medium', 'low']),
144
+ confidence: z.number().min(0).max(1),
145
+ restoration_expectation: z.string(),
146
+ blockers: z.array(z.string()),
147
+ recommendations: z.array(z.string()),
148
+ });
149
+ const BinaryProfileSchema = z.object({
150
+ binary_role: z.string(),
151
+ original_filename: z.string().nullable(),
152
+ export_count: z.number().int().nonnegative(),
153
+ forwarder_count: z.number().int().nonnegative(),
154
+ notable_exports: z.array(z.string()),
155
+ packed: z.boolean(),
156
+ packing_confidence: z.number().min(0).max(1),
157
+ analysis_priorities: z.array(z.string()),
158
+ });
159
+ const ManagedProfileSchema = z.object({
160
+ assembly_name: z.string().nullable(),
161
+ assembly_version: z.string().nullable(),
162
+ module_name: z.string().nullable(),
163
+ metadata_version: z.string().nullable(),
164
+ is_library: z.boolean(),
165
+ entry_point_token: z.string().nullable(),
166
+ type_count: z.number().int().nonnegative(),
167
+ method_count: z.number().int().nonnegative(),
168
+ namespace_count: z.number().int().nonnegative(),
169
+ assembly_reference_count: z.number().int().nonnegative(),
170
+ resource_count: z.number().int().nonnegative(),
171
+ dominant_namespaces: z.array(z.string()),
172
+ notable_types: z.array(z.string()),
173
+ assembly_references: z.array(z.string()),
174
+ resources: z.array(z.string()),
175
+ analysis_priorities: z.array(z.string()),
176
+ });
177
+ const ExportSummarySchema = z.object({
178
+ tool: z.enum(['code.reconstruct.export', 'dotnet.reconstruct.export']),
179
+ export_root: z.string(),
180
+ manifest_path: z.string().nullable(),
181
+ gaps_path: z.string().nullable(),
182
+ notes_path: z.string().nullable(),
183
+ metadata_path: z.string().nullable(),
184
+ csproj_path: z.string().nullable(),
185
+ readme_path: z.string().nullable(),
186
+ fallback_notes_path: z.string().nullable(),
187
+ build_validation_status: z.enum(['passed', 'failed', 'skipped', 'unavailable']).nullable(),
188
+ harness_validation_status: z.enum(['passed', 'failed', 'skipped', 'unavailable']).nullable(),
189
+ build_log_path: z.string().nullable(),
190
+ harness_log_path: z.string().nullable(),
191
+ executable_path: z.string().nullable(),
192
+ degraded_mode: z.boolean().nullable(),
193
+ module_count: z.number().int().nonnegative().nullable(),
194
+ unresolved_count: z.number().int().nonnegative().nullable(),
195
+ class_count: z.number().int().nonnegative().nullable(),
196
+ binary_profile: BinaryProfileSchema.nullable(),
197
+ managed_profile: ManagedProfileSchema.nullable(),
198
+ });
199
+ export const ReconstructWorkflowOutputSchema = z.object({
200
+ ok: z.boolean(),
201
+ data: z
202
+ .object({
203
+ sample_id: z.string(),
204
+ selected_path: z.enum(['native', 'dotnet']),
205
+ degraded: z.boolean(),
206
+ stage_status: z.object({
207
+ runtime: z.enum(['ok', 'failed']),
208
+ plan: z.enum(['ok', 'failed', 'skipped']),
209
+ export_primary: z.enum(['ok', 'failed', 'skipped']),
210
+ export_fallback: z.enum(['ok', 'failed', 'skipped']),
211
+ }),
212
+ provenance: AnalysisProvenanceSchema,
213
+ selection_diffs: AnalysisSelectionDiffSchema.optional(),
214
+ runtime: RuntimeSummarySchema,
215
+ plan: PlanSummarySchema.nullable(),
216
+ export: ExportSummarySchema.nullable(),
217
+ notes: z.array(z.string()),
218
+ })
219
+ .optional(),
220
+ warnings: z.array(z.string()).optional(),
221
+ errors: z.array(z.string()).optional(),
222
+ artifacts: z.array(z.any()).optional(),
223
+ metrics: z
224
+ .object({
225
+ elapsed_ms: z.number(),
226
+ tool: z.string(),
227
+ cached: z.boolean().optional(),
228
+ cache_key: z.string().optional(),
229
+ cache_tier: z.string().optional(),
230
+ cache_created_at: z.string().optional(),
231
+ cache_expires_at: z.string().optional(),
232
+ cache_hit_at: z.string().optional(),
233
+ })
234
+ .optional(),
235
+ });
236
+ export const reconstructWorkflowToolDefinition = {
237
+ name: TOOL_NAME,
238
+ description: 'Run a complete source-reconstruction workflow with auto routing (native/.NET), planning, export, and cache observability.',
239
+ inputSchema: ReconstructWorkflowInputSchema,
240
+ outputSchema: ReconstructWorkflowOutputSchema,
241
+ };
242
+ function normalizeError(error) {
243
+ if (error instanceof Error) {
244
+ return error.message;
245
+ }
246
+ return String(error);
247
+ }
248
+ function pickPrimaryRuntime(runtimeData) {
249
+ const suspected = runtimeData?.suspected || [];
250
+ if (suspected.length === 0) {
251
+ return null;
252
+ }
253
+ const sorted = [...suspected].sort((a, b) => b.confidence - a.confidence);
254
+ return sorted[0].runtime || null;
255
+ }
256
+ function summarizeRuntime(runtimeData) {
257
+ return {
258
+ is_dotnet: runtimeData?.is_dotnet ?? null,
259
+ dotnet_version: runtimeData?.dotnet_version ?? null,
260
+ target_framework: runtimeData?.target_framework ?? null,
261
+ primary_runtime: pickPrimaryRuntime(runtimeData),
262
+ };
263
+ }
264
+ export function createReconstructWorkflowHandler(workspaceManager, database, cacheManager, dependencies) {
265
+ const runtimeDetectHandler = dependencies?.runtimeDetectHandler ||
266
+ createRuntimeDetectHandler(workspaceManager, database, cacheManager);
267
+ const planHandler = dependencies?.planHandler ||
268
+ createCodeReconstructPlanHandler(workspaceManager, database, cacheManager);
269
+ const nativeExportHandler = dependencies?.nativeExportHandler ||
270
+ createCodeReconstructExportHandler(workspaceManager, database, cacheManager);
271
+ const dotnetExportHandler = dependencies?.dotnetExportHandler ||
272
+ createDotNetReconstructExportHandler(workspaceManager, database, cacheManager);
273
+ return async (args) => {
274
+ const input = ReconstructWorkflowInputSchema.parse(args);
275
+ const startTime = Date.now();
276
+ try {
277
+ const sample = database.findSample(input.sample_id);
278
+ if (!sample) {
279
+ return {
280
+ ok: false,
281
+ errors: [`Sample not found: ${input.sample_id}`],
282
+ };
283
+ }
284
+ const warnings = [];
285
+ const notes = [];
286
+ const stageStatus = {
287
+ runtime: 'failed',
288
+ plan: 'skipped',
289
+ export_primary: 'skipped',
290
+ export_fallback: 'skipped',
291
+ };
292
+ const runtimeResult = await runtimeDetectHandler({ sample_id: input.sample_id });
293
+ const runtimeData = runtimeResult.ok && runtimeResult.data
294
+ ? runtimeResult.data
295
+ : undefined;
296
+ if (!runtimeResult.ok) {
297
+ warnings.push(`runtime.detect unavailable: ${(runtimeResult.errors || ['unknown error']).join('; ')}`);
298
+ }
299
+ else if (runtimeResult.warnings && runtimeResult.warnings.length > 0) {
300
+ warnings.push(...runtimeResult.warnings.map((item) => `runtime: ${item}`));
301
+ stageStatus.runtime = 'ok';
302
+ }
303
+ else {
304
+ stageStatus.runtime = 'ok';
305
+ }
306
+ let selectedPath;
307
+ if (input.path === 'auto') {
308
+ selectedPath = runtimeData?.is_dotnet ? 'dotnet' : 'native';
309
+ }
310
+ else {
311
+ selectedPath = input.path;
312
+ }
313
+ if (input.path === 'dotnet' && runtimeData?.is_dotnet === false) {
314
+ return {
315
+ ok: false,
316
+ errors: ['Requested dotnet path, but runtime.detect does not recognize the sample as .NET.'],
317
+ warnings: runtimeData?.suspected && runtimeData.suspected.length > 0
318
+ ? [
319
+ `runtime.detect suspected: ${runtimeData.suspected
320
+ .map((item) => `${item.runtime}(${item.confidence.toFixed(2)})`)
321
+ .join(', ')}`,
322
+ ]
323
+ : undefined,
324
+ metrics: {
325
+ elapsed_ms: Date.now() - startTime,
326
+ tool: TOOL_NAME,
327
+ },
328
+ };
329
+ }
330
+ if (selectedPath === 'native' && runtimeData?.is_dotnet) {
331
+ warnings.push('Selected native path while runtime indicates .NET; forcing native as requested.');
332
+ }
333
+ const completedGhidraAnalysis = findBestGhidraAnalysis(database.findAnalysesBySample(input.sample_id), 'function_index');
334
+ const dynamicEvidence = await loadDynamicTraceEvidence(workspaceManager, database, input.sample_id, {
335
+ evidenceScope: input.evidence_scope,
336
+ sessionTag: input.evidence_session_tag,
337
+ });
338
+ const semanticNameIndex = await loadSemanticNameSuggestionIndex(workspaceManager, database, input.sample_id, {
339
+ scope: input.semantic_scope,
340
+ sessionTag: input.semantic_session_tag,
341
+ });
342
+ const semanticExplanationIndex = await loadSemanticFunctionExplanationIndex(workspaceManager, database, input.sample_id, {
343
+ scope: input.semantic_scope,
344
+ sessionTag: input.semantic_session_tag,
345
+ });
346
+ const provenance = {
347
+ runtime: buildRuntimeArtifactProvenance(dynamicEvidence, input.evidence_scope, input.evidence_session_tag),
348
+ semantic_names: buildSemanticArtifactProvenance('semantic naming artifacts', semanticNameIndex, input.semantic_scope, input.semantic_session_tag),
349
+ semantic_explanations: buildSemanticArtifactProvenance('semantic explanation artifacts', semanticExplanationIndex, input.semantic_scope, input.semantic_session_tag),
350
+ };
351
+ const selectionDiffs = {};
352
+ if (input.compare_evidence_scope) {
353
+ const baselineDynamicEvidence = await loadDynamicTraceEvidence(workspaceManager, database, input.sample_id, {
354
+ evidenceScope: input.compare_evidence_scope,
355
+ sessionTag: input.compare_evidence_session_tag,
356
+ });
357
+ selectionDiffs.runtime = buildArtifactSelectionDiff('runtime', provenance.runtime, buildRuntimeArtifactProvenance(baselineDynamicEvidence, input.compare_evidence_scope, input.compare_evidence_session_tag));
358
+ }
359
+ if (input.compare_semantic_scope) {
360
+ const baselineSemanticNameIndex = await loadSemanticNameSuggestionIndex(workspaceManager, database, input.sample_id, {
361
+ scope: input.compare_semantic_scope,
362
+ sessionTag: input.compare_semantic_session_tag,
363
+ });
364
+ const baselineSemanticExplanationIndex = await loadSemanticFunctionExplanationIndex(workspaceManager, database, input.sample_id, {
365
+ scope: input.compare_semantic_scope,
366
+ sessionTag: input.compare_semantic_session_tag,
367
+ });
368
+ selectionDiffs.semantic_names = buildArtifactSelectionDiff('semantic_names', provenance.semantic_names, buildSemanticArtifactProvenance('semantic naming artifacts', baselineSemanticNameIndex, input.compare_semantic_scope, input.compare_semantic_session_tag));
369
+ selectionDiffs.semantic_explanations = buildArtifactSelectionDiff('semantic_explanations', provenance.semantic_explanations, buildSemanticArtifactProvenance('semantic explanation artifacts', baselineSemanticExplanationIndex, input.compare_semantic_scope, input.compare_semantic_session_tag));
370
+ }
371
+ const analysisMarker = completedGhidraAnalysis?.finished_at || completedGhidraAnalysis?.id || 'none';
372
+ const cacheKey = generateCacheKey({
373
+ sampleSha256: sample.sha256,
374
+ toolName: TOOL_NAME,
375
+ toolVersion: TOOL_VERSION,
376
+ args: {
377
+ path: input.path,
378
+ selected_path: selectedPath,
379
+ topk: input.topk,
380
+ export_name: input.export_name || null,
381
+ validate_build: input.validate_build,
382
+ run_harness: input.run_harness,
383
+ compiler_path: input.compiler_path || null,
384
+ build_timeout_ms: input.build_timeout_ms,
385
+ run_timeout_ms: input.run_timeout_ms,
386
+ evidence_scope: input.evidence_scope,
387
+ evidence_session_tag: input.evidence_session_tag || null,
388
+ semantic_scope: input.semantic_scope,
389
+ semantic_session_tag: input.semantic_session_tag || null,
390
+ compare_evidence_scope: input.compare_evidence_scope || null,
391
+ compare_evidence_session_tag: input.compare_evidence_session_tag || null,
392
+ compare_semantic_scope: input.compare_semantic_scope || null,
393
+ compare_semantic_session_tag: input.compare_semantic_session_tag || null,
394
+ include_plan: input.include_plan,
395
+ include_obfuscation_fallback: input.include_obfuscation_fallback,
396
+ fallback_on_error: input.fallback_on_error,
397
+ allow_partial: input.allow_partial,
398
+ runtime_is_dotnet: runtimeData?.is_dotnet ?? null,
399
+ runtime_primary: pickPrimaryRuntime(runtimeData),
400
+ runtime_dotnet_version: runtimeData?.dotnet_version ?? null,
401
+ runtime_target_framework: runtimeData?.target_framework ?? null,
402
+ analysis_marker: analysisMarker,
403
+ },
404
+ });
405
+ if (input.reuse_cached) {
406
+ const cachedLookup = await lookupCachedResult(cacheManager, cacheKey);
407
+ if (cachedLookup) {
408
+ return {
409
+ ok: true,
410
+ data: cachedLookup.data,
411
+ warnings: ['Result from cache', formatCacheWarning(cachedLookup.metadata)],
412
+ metrics: {
413
+ elapsed_ms: Date.now() - startTime,
414
+ tool: TOOL_NAME,
415
+ cached: true,
416
+ cache_key: cachedLookup.metadata.key,
417
+ cache_tier: cachedLookup.metadata.tier,
418
+ cache_created_at: cachedLookup.metadata.createdAt,
419
+ cache_expires_at: cachedLookup.metadata.expiresAt,
420
+ cache_hit_at: cachedLookup.metadata.fetchedAt,
421
+ },
422
+ };
423
+ }
424
+ }
425
+ let planSummary = null;
426
+ if (input.include_plan) {
427
+ const planResult = await planHandler({
428
+ sample_id: input.sample_id,
429
+ target_language: selectedPath === 'dotnet' ? 'csharp' : 'c',
430
+ depth: 'standard',
431
+ include_decompiler: true,
432
+ include_strings: true,
433
+ });
434
+ if (planResult.ok && planResult.data) {
435
+ const data = planResult.data;
436
+ planSummary = {
437
+ feasibility: data.feasibility,
438
+ confidence: data.confidence,
439
+ restoration_expectation: data.restoration_expectation,
440
+ blockers: data.blockers || [],
441
+ recommendations: data.recommendations || [],
442
+ };
443
+ stageStatus.plan = 'ok';
444
+ }
445
+ else {
446
+ warnings.push(`plan unavailable: ${(planResult.errors || ['unknown error']).join('; ')}`);
447
+ stageStatus.plan = 'failed';
448
+ }
449
+ if (planResult.warnings && planResult.warnings.length > 0) {
450
+ warnings.push(...planResult.warnings.map((item) => `plan: ${item}`));
451
+ }
452
+ }
453
+ let exportSummary = null;
454
+ let artifacts = [];
455
+ const primaryPath = selectedPath;
456
+ const fallbackPath = primaryPath === 'dotnet' ? 'native' : 'dotnet';
457
+ const runExport = async (pathToRun) => {
458
+ if (pathToRun === 'dotnet') {
459
+ const dotnetResult = await dotnetExportHandler({
460
+ sample_id: input.sample_id,
461
+ topk: input.topk,
462
+ export_name: input.export_name,
463
+ include_obfuscation_fallback: input.include_obfuscation_fallback,
464
+ evidence_scope: input.evidence_scope,
465
+ evidence_session_tag: input.evidence_session_tag,
466
+ reuse_cached: input.reuse_cached,
467
+ });
468
+ if (!dotnetResult.ok || !dotnetResult.data) {
469
+ return {
470
+ ok: false,
471
+ errors: dotnetResult.errors || ['dotnet.reconstruct.export failed'],
472
+ warnings: dotnetResult.warnings || [],
473
+ };
474
+ }
475
+ const data = dotnetResult.data;
476
+ return {
477
+ ok: true,
478
+ warnings: dotnetResult.warnings || [],
479
+ artifacts: dotnetResult.artifacts || [],
480
+ summary: {
481
+ tool: 'dotnet.reconstruct.export',
482
+ export_root: data.export_root,
483
+ manifest_path: null,
484
+ gaps_path: null,
485
+ notes_path: data.reverse_notes_path || null,
486
+ metadata_path: data.metadata_path || null,
487
+ csproj_path: data.csproj_path,
488
+ readme_path: data.readme_path,
489
+ fallback_notes_path: data.fallback_notes_path,
490
+ build_validation_status: data.build_validation?.status || null,
491
+ harness_validation_status: null,
492
+ build_log_path: null,
493
+ harness_log_path: null,
494
+ executable_path: null,
495
+ degraded_mode: data.degraded_mode ?? null,
496
+ module_count: null,
497
+ unresolved_count: null,
498
+ class_count: Array.isArray(data.classes) ? data.classes.length : 0,
499
+ binary_profile: null,
500
+ managed_profile: data.managed_profile || null,
501
+ },
502
+ };
503
+ }
504
+ const nativeResult = await nativeExportHandler({
505
+ sample_id: input.sample_id,
506
+ topk: input.topk,
507
+ module_limit: 8,
508
+ min_module_size: 1,
509
+ include_imports: true,
510
+ include_strings: true,
511
+ export_name: input.export_name,
512
+ validate_build: input.validate_build,
513
+ run_harness: input.run_harness,
514
+ compiler_path: input.compiler_path,
515
+ build_timeout_ms: input.build_timeout_ms,
516
+ run_timeout_ms: input.run_timeout_ms,
517
+ evidence_scope: input.evidence_scope,
518
+ evidence_session_tag: input.evidence_session_tag,
519
+ semantic_scope: input.semantic_scope,
520
+ semantic_session_tag: input.semantic_session_tag,
521
+ reuse_cached: input.reuse_cached,
522
+ });
523
+ if (!nativeResult.ok || !nativeResult.data) {
524
+ return {
525
+ ok: false,
526
+ errors: nativeResult.errors || ['code.reconstruct.export failed'],
527
+ warnings: nativeResult.warnings || [],
528
+ };
529
+ }
530
+ const data = nativeResult.data;
531
+ return {
532
+ ok: true,
533
+ warnings: nativeResult.warnings || [],
534
+ artifacts: nativeResult.artifacts || [],
535
+ summary: {
536
+ tool: 'code.reconstruct.export',
537
+ export_root: data.export_root,
538
+ manifest_path: data.manifest_path,
539
+ gaps_path: data.gaps_path,
540
+ notes_path: data.notes_path || null,
541
+ metadata_path: null,
542
+ csproj_path: null,
543
+ readme_path: null,
544
+ fallback_notes_path: null,
545
+ build_validation_status: data.build_validation?.status || null,
546
+ harness_validation_status: data.harness_validation?.status || null,
547
+ build_log_path: data.build_validation?.log_path || null,
548
+ harness_log_path: data.harness_validation?.log_path || null,
549
+ executable_path: data.build_validation?.executable_path || null,
550
+ degraded_mode: null,
551
+ module_count: data.module_count,
552
+ unresolved_count: data.unresolved_count,
553
+ class_count: null,
554
+ binary_profile: data.binary_profile || null,
555
+ managed_profile: null,
556
+ },
557
+ };
558
+ };
559
+ const primaryExportResult = await runExport(primaryPath);
560
+ if (primaryExportResult.ok) {
561
+ stageStatus.export_primary = 'ok';
562
+ exportSummary = primaryExportResult.summary;
563
+ artifacts = primaryExportResult.artifacts || [];
564
+ if (primaryExportResult.warnings.length > 0) {
565
+ warnings.push(...primaryExportResult.warnings.map((item) => `${primaryPath === 'dotnet' ? 'dotnet_export' : 'native_export'}: ${item}`));
566
+ }
567
+ }
568
+ else {
569
+ stageStatus.export_primary = 'failed';
570
+ warnings.push(`primary export(${primaryPath}) failed: ${(primaryExportResult.errors || ['unknown error']).join('; ')}`);
571
+ if (primaryExportResult.warnings.length > 0) {
572
+ warnings.push(...primaryExportResult.warnings.map((item) => `${primaryPath === 'dotnet' ? 'dotnet_export' : 'native_export'}: ${item}`));
573
+ }
574
+ }
575
+ if (!exportSummary && input.fallback_on_error) {
576
+ const fallbackExportResult = await runExport(fallbackPath);
577
+ if (fallbackExportResult.ok) {
578
+ stageStatus.export_fallback = 'ok';
579
+ exportSummary = fallbackExportResult.summary;
580
+ artifacts = fallbackExportResult.artifacts || [];
581
+ selectedPath = fallbackPath;
582
+ notes.push(`Primary export path failed; switched to fallback path: ${fallbackPath}.`);
583
+ if (fallbackExportResult.warnings.length > 0) {
584
+ warnings.push(...fallbackExportResult.warnings.map((item) => `${fallbackPath === 'dotnet' ? 'dotnet_export' : 'native_export'}: ${item}`));
585
+ }
586
+ }
587
+ else {
588
+ stageStatus.export_fallback = 'failed';
589
+ warnings.push(`fallback export(${fallbackPath}) failed: ${(fallbackExportResult.errors || ['unknown error']).join('; ')}`);
590
+ if (fallbackExportResult.warnings.length > 0) {
591
+ warnings.push(...fallbackExportResult.warnings.map((item) => `${fallbackPath === 'dotnet' ? 'dotnet_export' : 'native_export'}: ${item}`));
592
+ }
593
+ }
594
+ }
595
+ if (!exportSummary && !input.fallback_on_error) {
596
+ stageStatus.export_fallback = 'skipped';
597
+ }
598
+ if (!exportSummary && !input.allow_partial) {
599
+ return {
600
+ ok: false,
601
+ errors: ['All export paths failed and allow_partial=false.'],
602
+ warnings,
603
+ metrics: {
604
+ elapsed_ms: Date.now() - startTime,
605
+ tool: TOOL_NAME,
606
+ },
607
+ };
608
+ }
609
+ if (planSummary?.feasibility === 'low') {
610
+ notes.push('Feasibility is low; treat output as partial semantic reconstruction.');
611
+ }
612
+ if (runtimeData?.is_dotnet) {
613
+ notes.push('Runtime signal indicates .NET metadata is available for high-fidelity recovery.');
614
+ }
615
+ else {
616
+ notes.push('Runtime signal indicates native path; exact original source text is not recoverable.');
617
+ }
618
+ if (exportSummary?.binary_profile?.analysis_priorities?.length) {
619
+ notes.push(`Binary profile priorities: ${exportSummary.binary_profile.analysis_priorities.join(', ')}.`);
620
+ }
621
+ if (exportSummary?.managed_profile?.analysis_priorities?.length) {
622
+ notes.push(`Managed profile priorities: ${exportSummary.managed_profile.analysis_priorities.join(', ')}.`);
623
+ }
624
+ if (selectedPath === 'native' && exportSummary?.build_validation_status) {
625
+ notes.push(`Native build validation: ${exportSummary.build_validation_status}.`);
626
+ }
627
+ if (selectedPath === 'native' && exportSummary?.harness_validation_status) {
628
+ notes.push(`Harness validation: ${exportSummary.harness_validation_status}.`);
629
+ }
630
+ const outputData = {
631
+ sample_id: input.sample_id,
632
+ selected_path: selectedPath,
633
+ degraded: stageStatus.export_primary !== 'ok' || stageStatus.plan === 'failed' || !exportSummary,
634
+ stage_status: stageStatus,
635
+ provenance,
636
+ selection_diffs: Object.keys(selectionDiffs).length > 0 ? selectionDiffs : undefined,
637
+ runtime: summarizeRuntime(runtimeData),
638
+ plan: planSummary,
639
+ export: exportSummary,
640
+ notes,
641
+ };
642
+ await cacheManager.setCachedResult(cacheKey, outputData, CACHE_TTL_MS, sample.sha256);
643
+ return {
644
+ ok: true,
645
+ data: outputData,
646
+ warnings: warnings.length > 0 ? warnings : undefined,
647
+ artifacts: artifacts,
648
+ metrics: {
649
+ elapsed_ms: Date.now() - startTime,
650
+ tool: TOOL_NAME,
651
+ },
652
+ };
653
+ }
654
+ catch (error) {
655
+ return {
656
+ ok: false,
657
+ errors: [normalizeError(error)],
658
+ metrics: {
659
+ elapsed_ms: Date.now() - startTime,
660
+ tool: TOOL_NAME,
661
+ },
662
+ };
663
+ }
664
+ };
665
+ }
666
+ //# sourceMappingURL=reconstruct.js.map