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
@@ -109,6 +109,22 @@ export function deriveRuntimeStageCandidates(input) {
109
109
  semanticCorpus.includes('overlay')) {
110
110
  stages.add('scan_pe_layout');
111
111
  }
112
+ if (behaviorTags.has('dll_lifecycle') ||
113
+ /\b(dllmain|dllregisterserver|dllunregisterserver|dllinstall|dllcanunloadnow|dll_process_attach|dll_thread_attach|disablethreadlibrarycalls)\b/i.test(semanticCorpus)) {
114
+ stages.add('dll_lifecycle');
115
+ }
116
+ if (behaviorTags.has('com_activation') ||
117
+ /\b(cocreateinstance|queryinterface|registerclassobject|dllgetclassobject|iclassfactory|clsid_|progid|inprocserver32|localserver32)\b/i.test(semanticCorpus)) {
118
+ stages.add('com_activation');
119
+ }
120
+ if (behaviorTags.has('export_dispatch') ||
121
+ /\b(export dispatch|forwarded export|ordinal|dispatch exported|dllcanunloadnow|dllgetclassobject)\b/i.test(semanticCorpus)) {
122
+ stages.add('export_dispatch');
123
+ }
124
+ if (behaviorTags.has('plugin_callback') ||
125
+ /\b(callback|plugin|hook|event sink|notify|host extension|host interaction)\b/i.test(semanticCorpus)) {
126
+ stages.add('callback_surface');
127
+ }
112
128
  return Array.from(stages);
113
129
  }
114
130
  function regionMatchersForStage(stage) {
@@ -127,10 +143,110 @@ function regionMatchersForStage(stage) {
127
143
  return [/registry/i, /key/i];
128
144
  case 'scan_pe_layout':
129
145
  return [/packer/i, /entropy/i, /section/i, /layout/i];
146
+ case 'dll_lifecycle':
147
+ return [/dll/i, /lifecycle/i, /attach/i, /detach/i, /module/i];
148
+ case 'com_activation':
149
+ return [/com/i, /class/i, /factory/i, /activation/i, /registration/i];
150
+ case 'export_dispatch':
151
+ return [/export/i, /dispatch/i, /ordinal/i, /forward/i];
152
+ case 'callback_surface':
153
+ return [/callback/i, /plugin/i, /hook/i, /notify/i, /host/i];
130
154
  default:
131
155
  return [];
132
156
  }
133
157
  }
158
+ function protectionMatchersForStage(stage) {
159
+ switch (stage) {
160
+ case 'resolve_dynamic_apis':
161
+ return [/image/i, /r-x/i, /execute/i];
162
+ case 'prepare_remote_process_access':
163
+ return [/read_write/i, /write/i, /execute/i, /rwx/i];
164
+ case 'anti_analysis_checks':
165
+ case 'check_execution_environment':
166
+ return [/read/i, /image/i];
167
+ case 'file_operations':
168
+ return [/file/i, /read_write/i, /container/i];
169
+ case 'registry_operations':
170
+ case 'stage_registry_state':
171
+ return [/read_write/i, /data/i];
172
+ case 'scan_pe_layout':
173
+ return [/image/i, /container/i, /read/i];
174
+ case 'dll_lifecycle':
175
+ case 'com_activation':
176
+ case 'export_dispatch':
177
+ case 'callback_surface':
178
+ return [/image/i, /read/i, /execute/i];
179
+ default:
180
+ return [];
181
+ }
182
+ }
183
+ function ownerMatchersForStage(stage) {
184
+ switch (stage) {
185
+ case 'resolve_dynamic_apis':
186
+ case 'prepare_remote_process_access':
187
+ return [/kernel32/i, /kernelbase/i, /ntdll/i, /\.exe$/i, /\.dll$/i];
188
+ case 'anti_analysis_checks':
189
+ case 'check_execution_environment':
190
+ return [/kernel32/i, /kernelbase/i, /ntdll/i];
191
+ case 'file_operations':
192
+ return [/kernel32/i, /kernelbase/i, /shlwapi/i];
193
+ case 'registry_operations':
194
+ case 'stage_registry_state':
195
+ return [/advapi32/i];
196
+ case 'scan_pe_layout':
197
+ return [/\.exe$/i, /\.dll$/i];
198
+ case 'dll_lifecycle':
199
+ return [/\.dll$/i, /\.ocx$/i, /\.cpl$/i];
200
+ case 'com_activation':
201
+ return [/ole32/i, /oleaut32/i, /combase/i, /rpcrt4/i, /\.dll$/i];
202
+ case 'export_dispatch':
203
+ return [/\.dll$/i, /\.ocx$/i, /\.cpl$/i];
204
+ case 'callback_surface':
205
+ return [/plugin/i, /host/i, /extension/i, /addin/i, /\.dll$/i];
206
+ default:
207
+ return [];
208
+ }
209
+ }
210
+ function segmentMatchersForStage(stage) {
211
+ switch (stage) {
212
+ case 'scan_pe_layout':
213
+ return [/\.pdata/i, /\.xdata/i, /\.text/i, /\.rsrc/i];
214
+ case 'dll_lifecycle':
215
+ return [/\.tls/i, /\.crt/i, /\.rdata/i, /init/i];
216
+ case 'com_activation':
217
+ return [/\.idata/i, /\.rdata/i, /class/i, /factory/i];
218
+ case 'export_dispatch':
219
+ return [/\.edata/i, /export/i, /dispatch/i];
220
+ case 'callback_surface':
221
+ return [/callback/i, /hook/i, /event/i, /notify/i];
222
+ case 'prepare_remote_process_access':
223
+ return [/\.text/i, /\.data/i];
224
+ default:
225
+ return [];
226
+ }
227
+ }
228
+ function collectMatchedMetadataValues(values, candidateStages, input, matchersForStage, fallbackMatcher) {
229
+ const semanticCorpus = buildSemanticCorpus(input);
230
+ const matched = new Set();
231
+ for (const value of values) {
232
+ const lowered = value.toLowerCase();
233
+ for (const stage of candidateStages) {
234
+ if (matchersForStage(stage).some((matcher) => matcher.test(lowered))) {
235
+ matched.add(value);
236
+ }
237
+ }
238
+ if (fallbackMatcher?.(lowered, semanticCorpus)) {
239
+ matched.add(value);
240
+ }
241
+ }
242
+ return Array.from(matched).slice(0, 6);
243
+ }
244
+ function collectMatchedAddressRanges(matchedMemoryRegions, dynamicEvidence) {
245
+ if (matchedMemoryRegions.length === 0) {
246
+ return [];
247
+ }
248
+ return dedupe(dynamicEvidence.address_ranges || []).slice(0, Math.max(1, matchedMemoryRegions.length));
249
+ }
134
250
  function collectMatchedMemoryRegions(candidateStages, input, dynamicEvidence) {
135
251
  const semanticCorpus = buildSemanticCorpus(input);
136
252
  const matched = new Set();
@@ -168,6 +284,11 @@ export function correlateFunctionWithRuntimeEvidence(input, dynamicEvidence) {
168
284
  const observedStages = new Set((dynamicEvidence.stages || []).map((item) => item.toLowerCase()));
169
285
  const matchedStages = candidateStages.filter((item) => observedStages.has(item.toLowerCase()));
170
286
  const matchedMemoryRegions = collectMatchedMemoryRegions(candidateStages, input, dynamicEvidence);
287
+ const matchedProtections = collectMatchedMetadataValues(dynamicEvidence.protections || [], candidateStages, input, protectionMatchersForStage, (lowered, semanticCorpus) => semanticCorpus.includes('remote') && /(read_write|write|execute|rwx)/i.test(lowered));
288
+ const matchedRegionOwners = collectMatchedMetadataValues(dynamicEvidence.region_owners || [], candidateStages, input, ownerMatchersForStage, (lowered, semanticCorpus) => semanticCorpus.includes('com') && /(ole32|oleaut32|combase|rpcrt4)/i.test(lowered));
289
+ const matchedObservedModules = collectMatchedMetadataValues(dynamicEvidence.observed_modules || [], candidateStages, input, ownerMatchersForStage, (lowered, semanticCorpus) => semanticCorpus.includes('com') && /(ole32|oleaut32|combase|rpcrt4)/i.test(lowered));
290
+ const matchedSegmentNames = collectMatchedMetadataValues(dynamicEvidence.segment_names || [], candidateStages, input, segmentMatchersForStage);
291
+ const matchedAddressRanges = collectMatchedAddressRanges(matchedMemoryRegions, dynamicEvidence);
171
292
  const evidenceSources = summarizeEvidenceSources(dynamicEvidence);
172
293
  const sourceNames = summarizeSourceNames(dynamicEvidence);
173
294
  const provenanceLayers = summarizeProvenanceLayers(dynamicEvidence);
@@ -178,7 +299,13 @@ export function correlateFunctionWithRuntimeEvidence(input, dynamicEvidence) {
178
299
  (input.stringHints || []).length > 0 ? 'string_hint' : '',
179
300
  input.semanticSummary ? 'semantic_summary' : '',
180
301
  ]);
181
- if (matchedApis.length === 0 && matchedStages.length === 0 && matchedMemoryRegions.length === 0) {
302
+ if (matchedApis.length === 0 &&
303
+ matchedStages.length === 0 &&
304
+ matchedMemoryRegions.length === 0 &&
305
+ matchedProtections.length === 0 &&
306
+ matchedRegionOwners.length === 0 &&
307
+ matchedObservedModules.length === 0 &&
308
+ matchedSegmentNames.length === 0) {
182
309
  return undefined;
183
310
  }
184
311
  const highSignalMatches = matchedApis.filter((item) => highSignalSet.has(normalizeRuntimeApiName(item).toLowerCase()));
@@ -192,6 +319,21 @@ export function correlateFunctionWithRuntimeEvidence(input, dynamicEvidence) {
192
319
  if (matchedMemoryRegions.length > 0) {
193
320
  notes.push(`Runtime memory regions align with this function: ${matchedMemoryRegions.slice(0, 4).join(', ')}`);
194
321
  }
322
+ if (matchedProtections.length > 0) {
323
+ notes.push(`Runtime protections align with this function: ${matchedProtections.slice(0, 4).join(', ')}`);
324
+ }
325
+ if (matchedRegionOwners.length > 0) {
326
+ notes.push(`Runtime region owners align with this function: ${matchedRegionOwners.slice(0, 4).join(', ')}`);
327
+ }
328
+ if (matchedObservedModules.length > 0) {
329
+ notes.push(`Runtime modules align with this function: ${matchedObservedModules.slice(0, 4).join(', ')}`);
330
+ }
331
+ if (matchedSegmentNames.length > 0) {
332
+ notes.push(`Runtime segments align with this function: ${matchedSegmentNames.slice(0, 4).join(', ')}`);
333
+ }
334
+ if (matchedAddressRanges.length > 0) {
335
+ notes.push(`Runtime address ranges associated with this function: ${matchedAddressRanges.slice(0, 3).join(', ')}`);
336
+ }
195
337
  if (dynamicEvidence.executed) {
196
338
  notes.push('Correlation includes executed runtime evidence, not just static or memory-only hints.');
197
339
  }
@@ -225,6 +367,22 @@ export function correlateFunctionWithRuntimeEvidence(input, dynamicEvidence) {
225
367
  ...(matchedMemoryRegions.some((item) => /network|socket|http|pipe|ipc/i.test(item))
226
368
  ? ['network_ops']
227
369
  : []),
370
+ ...(matchedRegionOwners.some((item) => /ole32|oleaut32|combase|rpcrt4/i.test(item)) ||
371
+ matchedObservedModules.some((item) => /ole32|oleaut32|combase|rpcrt4/i.test(item))
372
+ ? ['com_activation']
373
+ : []),
374
+ ...(matchedSegmentNames.some((item) => /\.edata|export|dispatch/i.test(item)) ||
375
+ matchedStages.some((item) => item.toLowerCase() === 'export_dispatch')
376
+ ? ['export_dispatch']
377
+ : []),
378
+ ...(matchedSegmentNames.some((item) => /\.tls|\.crt|init/i.test(item)) ||
379
+ matchedStages.some((item) => item.toLowerCase() === 'dll_lifecycle')
380
+ ? ['dll_lifecycle']
381
+ : []),
382
+ ...(matchedStages.some((item) => item.toLowerCase() === 'callback_surface') ||
383
+ matchedObservedModules.some((item) => /plugin|host|extension|addin/i.test(item))
384
+ ? ['callback_surface']
385
+ : []),
228
386
  ]);
229
387
  if (suggestedModules.length > 0) {
230
388
  notes.push(`Suggested semantic modules: ${suggestedModules.slice(0, 4).join(', ')}`);
@@ -233,7 +391,9 @@ export function correlateFunctionWithRuntimeEvidence(input, dynamicEvidence) {
233
391
  Math.min(0.24, dedupe(matchedApis).length * 0.08) +
234
392
  Math.min(0.16, dedupe(matchedStages).length * 0.06) +
235
393
  Math.min(0.12, highSignalMatches.length * 0.05) +
236
- Math.min(0.1, matchedMemoryRegions.length * 0.04), 0.45, 0.97);
394
+ Math.min(0.1, matchedMemoryRegions.length * 0.04) +
395
+ Math.min(0.07, matchedProtections.length * 0.02) +
396
+ Math.min(0.07, (matchedRegionOwners.length + matchedObservedModules.length + matchedSegmentNames.length) * 0.015), 0.45, 0.97);
237
397
  return {
238
398
  corroborated_apis: dedupe(matchedApis).slice(0, 8),
239
399
  corroborated_stages: dedupe(matchedStages).slice(0, 6),
@@ -245,6 +405,11 @@ export function correlateFunctionWithRuntimeEvidence(input, dynamicEvidence) {
245
405
  artifact_count: dynamicEvidence.artifact_count,
246
406
  executed_artifact_count: dynamicEvidence.executed_artifact_count || 0,
247
407
  matched_memory_regions: matchedMemoryRegions,
408
+ matched_protections: matchedProtections,
409
+ matched_address_ranges: matchedAddressRanges,
410
+ matched_region_owners: matchedRegionOwners,
411
+ matched_observed_modules: matchedObservedModules,
412
+ matched_segment_names: matchedSegmentNames,
248
413
  suggested_modules: suggestedModules,
249
414
  matched_by: matchedBy,
250
415
  provenance_layers: provenanceLayers,
@@ -273,6 +438,18 @@ export function modulesSuggestedByRuntimeStages(stages = []) {
273
438
  else if (stage === 'resolve_dynamic_apis') {
274
439
  modules.add('process_ops');
275
440
  }
441
+ else if (stage === 'dll_lifecycle') {
442
+ modules.add('dll_lifecycle');
443
+ }
444
+ else if (stage === 'com_activation') {
445
+ modules.add('com_activation');
446
+ }
447
+ else if (stage === 'export_dispatch') {
448
+ modules.add('export_dispatch');
449
+ }
450
+ else if (stage === 'callback_surface') {
451
+ modules.add('callback_surface');
452
+ }
276
453
  }
277
454
  return Array.from(modules);
278
455
  }
@@ -0,0 +1,28 @@
1
+ import type { WorkspaceManager } from './workspace-manager.js';
2
+ export type SampleWorkspaceStatus = 'ready' | 'workspace_missing' | 'original_dir_missing' | 'original_file_missing';
3
+ export interface SampleWorkspaceIntegrity {
4
+ status: SampleWorkspaceStatus;
5
+ workspace_root: string | null;
6
+ original_dir: string | null;
7
+ reports_dir: string | null;
8
+ ghidra_dir: string | null;
9
+ workspace_exists: boolean;
10
+ original_dir_exists: boolean;
11
+ reports_dir_exists: boolean;
12
+ ghidra_dir_exists: boolean;
13
+ original_present: boolean;
14
+ original_file_count: number;
15
+ original_files: string[];
16
+ alternate_workspace_root: string | null;
17
+ alternate_original_dir: string | null;
18
+ alternate_original_present: boolean;
19
+ alternate_original_files: string[];
20
+ remediation: string[];
21
+ }
22
+ export declare function inspectSampleWorkspace(workspaceManager: WorkspaceManager, sampleId: string): Promise<SampleWorkspaceIntegrity>;
23
+ export declare function formatMissingOriginalError(sampleId: string, integrity: SampleWorkspaceIntegrity): string;
24
+ export declare function resolvePrimarySamplePath(workspaceManager: WorkspaceManager, sampleId: string): Promise<{
25
+ samplePath: string;
26
+ integrity: SampleWorkspaceIntegrity;
27
+ }>;
28
+ //# sourceMappingURL=sample-workspace.d.ts.map
@@ -0,0 +1,186 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ function buildRemediation(status, sampleId) {
4
+ const shared = [
5
+ `Re-ingest the sample with sample.ingest(path=<absolute path>) for ${sampleId}.`,
6
+ 'Or restore the original binary into workspace/original before running PE/Ghidra recovery tools.',
7
+ ];
8
+ if (status === 'workspace_missing') {
9
+ return [
10
+ `Workspace directory is missing for ${sampleId}.`,
11
+ ...shared,
12
+ ];
13
+ }
14
+ if (status === 'original_dir_missing') {
15
+ return [
16
+ 'workspace/original is missing even though the sample record still exists.',
17
+ ...shared,
18
+ ];
19
+ }
20
+ if (status === 'original_file_missing') {
21
+ return [
22
+ 'workspace/original exists but contains no sample file.',
23
+ ...shared,
24
+ ];
25
+ }
26
+ return [];
27
+ }
28
+ function inferStatus(options) {
29
+ if (!options.workspaceExists) {
30
+ return 'workspace_missing';
31
+ }
32
+ if (!options.originalDirExists) {
33
+ return 'original_dir_missing';
34
+ }
35
+ if (options.originalFileCount <= 0) {
36
+ return 'original_file_missing';
37
+ }
38
+ return 'ready';
39
+ }
40
+ export async function inspectSampleWorkspace(workspaceManager, sampleId) {
41
+ const workspaceRootBase = workspaceManager.getWorkspaceRoot();
42
+ let workspaceRoot = null;
43
+ let originalDir = null;
44
+ let reportsDir = null;
45
+ let ghidraDir = null;
46
+ let workspaceExists = false;
47
+ let originalDirExists = false;
48
+ let reportsDirExists = false;
49
+ let ghidraDirExists = false;
50
+ let originalFiles = [];
51
+ let alternateWorkspaceRoot = null;
52
+ let alternateOriginalDir = null;
53
+ let alternateOriginalFiles = [];
54
+ try {
55
+ const workspace = await workspaceManager.getWorkspace(sampleId);
56
+ workspaceRoot = workspace.root;
57
+ originalDir = workspace.original;
58
+ reportsDir = workspace.reports;
59
+ ghidraDir = workspace.ghidra;
60
+ try {
61
+ await fs.access(workspace.root);
62
+ workspaceExists = true;
63
+ }
64
+ catch {
65
+ workspaceExists = false;
66
+ }
67
+ try {
68
+ await fs.access(workspace.original);
69
+ originalDirExists = true;
70
+ const entries = await fs.readdir(workspace.original, { withFileTypes: true });
71
+ originalFiles = entries
72
+ .filter((entry) => entry.isFile())
73
+ .map((entry) => entry.name)
74
+ .sort();
75
+ }
76
+ catch {
77
+ originalDirExists = false;
78
+ }
79
+ try {
80
+ await fs.access(workspace.reports);
81
+ reportsDirExists = true;
82
+ }
83
+ catch {
84
+ reportsDirExists = false;
85
+ }
86
+ try {
87
+ await fs.access(workspace.ghidra);
88
+ ghidraDirExists = true;
89
+ }
90
+ catch {
91
+ ghidraDirExists = false;
92
+ }
93
+ }
94
+ catch {
95
+ workspaceExists = false;
96
+ }
97
+ const status = inferStatus({
98
+ workspaceExists,
99
+ originalDirExists,
100
+ originalFileCount: originalFiles.length,
101
+ });
102
+ if (status !== 'ready') {
103
+ const workspaceParent = path.dirname(path.dirname(workspaceRootBase));
104
+ const candidateRoots = Array.from(new Set([
105
+ path.join(workspaceParent, 'workspaces'),
106
+ ])).filter((candidate) => path.resolve(candidate) !== path.resolve(workspaceRootBase));
107
+ const sampleHash = sampleId.startsWith('sha256:') ? sampleId.slice(7).toLowerCase() : null;
108
+ if (sampleHash && /^[a-f0-9]{64}$/.test(sampleHash)) {
109
+ for (const candidateRoot of candidateRoots) {
110
+ const candidateOriginalDir = path.join(candidateRoot, sampleHash.slice(0, 2), sampleHash.slice(2, 4), sampleHash, 'original');
111
+ try {
112
+ const entries = await fs.readdir(candidateOriginalDir, { withFileTypes: true });
113
+ const files = entries.filter((entry) => entry.isFile()).map((entry) => entry.name).sort();
114
+ if (files.length > 0) {
115
+ alternateWorkspaceRoot = candidateRoot;
116
+ alternateOriginalDir = candidateOriginalDir;
117
+ alternateOriginalFiles = files;
118
+ break;
119
+ }
120
+ }
121
+ catch {
122
+ // ignore missing legacy workspace candidates
123
+ }
124
+ }
125
+ }
126
+ }
127
+ return {
128
+ status,
129
+ workspace_root: workspaceRoot,
130
+ original_dir: originalDir,
131
+ reports_dir: reportsDir,
132
+ ghidra_dir: ghidraDir,
133
+ workspace_exists: workspaceExists,
134
+ original_dir_exists: originalDirExists,
135
+ reports_dir_exists: reportsDirExists,
136
+ ghidra_dir_exists: ghidraDirExists,
137
+ original_present: originalFiles.length > 0,
138
+ original_file_count: originalFiles.length,
139
+ original_files: originalFiles,
140
+ alternate_workspace_root: alternateWorkspaceRoot,
141
+ alternate_original_dir: alternateOriginalDir,
142
+ alternate_original_present: alternateOriginalFiles.length > 0,
143
+ alternate_original_files: alternateOriginalFiles,
144
+ remediation: buildRemediation(status, sampleId),
145
+ };
146
+ }
147
+ export function formatMissingOriginalError(sampleId, integrity) {
148
+ const parts = [
149
+ `Sample original file is unavailable for ${sampleId}.`,
150
+ `status=${integrity.status}`,
151
+ ];
152
+ if (integrity.workspace_root) {
153
+ parts.push(`workspace_root=${integrity.workspace_root}`);
154
+ }
155
+ if (integrity.original_dir) {
156
+ parts.push(`original_dir=${integrity.original_dir}`);
157
+ }
158
+ if (integrity.alternate_original_present && integrity.alternate_original_dir) {
159
+ parts.push(`alternate_original_dir=${integrity.alternate_original_dir}`);
160
+ parts.push('alternate_workspace_fallback=available');
161
+ }
162
+ if (integrity.remediation.length > 0) {
163
+ parts.push(`remediation=${integrity.remediation.join(' ')}`);
164
+ }
165
+ return parts.join(' ');
166
+ }
167
+ export async function resolvePrimarySamplePath(workspaceManager, sampleId) {
168
+ const integrity = await inspectSampleWorkspace(workspaceManager, sampleId);
169
+ if (integrity.original_present && integrity.original_dir) {
170
+ return {
171
+ samplePath: path.join(integrity.original_dir, integrity.original_files[0]),
172
+ integrity,
173
+ };
174
+ }
175
+ if (integrity.alternate_original_present && integrity.alternate_original_dir) {
176
+ return {
177
+ samplePath: path.join(integrity.alternate_original_dir, integrity.alternate_original_files[0]),
178
+ integrity,
179
+ };
180
+ }
181
+ if (!integrity.original_present || !integrity.original_dir) {
182
+ throw new Error(formatMissingOriginalError(sampleId, integrity));
183
+ }
184
+ throw new Error(formatMissingOriginalError(sampleId, integrity));
185
+ }
186
+ //# sourceMappingURL=sample-workspace.js.map
@@ -5,6 +5,8 @@ export declare const SEMANTIC_NAME_SUGGESTIONS_ARTIFACT_TYPE = "semantic_name_su
5
5
  export declare const SEMANTIC_NAME_PREPARE_BUNDLE_ARTIFACT_TYPE = "semantic_name_prepare_bundle";
6
6
  export declare const SEMANTIC_EXPLANATION_PREPARE_BUNDLE_ARTIFACT_TYPE = "semantic_explanation_prepare_bundle";
7
7
  export declare const SEMANTIC_FUNCTION_EXPLANATIONS_ARTIFACT_TYPE = "semantic_function_explanations";
8
+ export declare const SEMANTIC_MODULE_REVIEW_PREPARE_BUNDLE_ARTIFACT_TYPE = "semantic_module_review_prepare_bundle";
9
+ export declare const SEMANTIC_MODULE_REVIEWS_ARTIFACT_TYPE = "semantic_module_reviews";
8
10
  export interface SemanticNameSuggestionEntry {
9
11
  address?: string | null;
10
12
  function?: string | null;
@@ -102,15 +104,69 @@ export interface SemanticFunctionExplanationIndex {
102
104
  latest_created_at: string | null;
103
105
  scope_note: string;
104
106
  }
107
+ export interface SemanticModuleReviewEntry {
108
+ module_name: string;
109
+ refined_name?: string | null;
110
+ summary: string;
111
+ role_hint?: string | null;
112
+ confidence: number;
113
+ assumptions?: string[];
114
+ evidence_used?: string[];
115
+ rewrite_guidance?: string[];
116
+ focus_areas?: string[];
117
+ priority_functions?: string[];
118
+ }
119
+ export interface SemanticModuleReviewArtifactPayload {
120
+ schema_version: 1;
121
+ sample_id: string;
122
+ created_at: string;
123
+ session_tag?: string | null;
124
+ client_name?: string | null;
125
+ model_name?: string | null;
126
+ prepare_artifact_id?: string | null;
127
+ reviews: SemanticModuleReviewEntry[];
128
+ }
129
+ export interface LoadedSemanticModuleReview {
130
+ module_name: string;
131
+ refined_name: string | null;
132
+ summary: string;
133
+ role_hint: string | null;
134
+ confidence: number;
135
+ assumptions: string[];
136
+ evidence_used: string[];
137
+ rewrite_guidance: string[];
138
+ focus_areas: string[];
139
+ priority_functions: string[];
140
+ artifact_id: string;
141
+ created_at: string;
142
+ client_name: string | null;
143
+ model_name: string | null;
144
+ session_tag: string | null;
145
+ prepare_artifact_id: string | null;
146
+ }
147
+ export interface SemanticModuleReviewIndex {
148
+ byModule: Map<string, LoadedSemanticModuleReview>;
149
+ marker: string;
150
+ artifact_ids: string[];
151
+ session_tags: string[];
152
+ earliest_created_at: string | null;
153
+ latest_created_at: string | null;
154
+ scope_note: string;
155
+ }
105
156
  export declare function normalizeFunctionKey(value: string | null | undefined): string | null;
106
157
  export declare function normalizeAddressKey(value: string | null | undefined): string | null;
107
158
  export declare function sanitizeSemanticName(value: string | null | undefined): string | null;
159
+ export declare function normalizeModuleKey(value: string | null | undefined): string | null;
108
160
  export declare function persistSemanticNamePrepareBundleArtifact(workspaceManager: WorkspaceManager, database: DatabaseManager, sampleId: string, payload: unknown, sessionTag?: string | null): Promise<ArtifactRef>;
109
161
  export declare function persistSemanticExplanationPrepareBundleArtifact(workspaceManager: WorkspaceManager, database: DatabaseManager, sampleId: string, payload: unknown, sessionTag?: string | null): Promise<ArtifactRef>;
110
162
  export declare function persistSemanticFunctionExplanationsArtifact(workspaceManager: WorkspaceManager, database: DatabaseManager, payload: SemanticFunctionExplanationArtifactPayload): Promise<ArtifactRef>;
163
+ export declare function persistSemanticModuleReviewPrepareBundleArtifact(workspaceManager: WorkspaceManager, database: DatabaseManager, sampleId: string, payload: unknown, sessionTag?: string | null): Promise<ArtifactRef>;
164
+ export declare function persistSemanticModuleReviewsArtifact(workspaceManager: WorkspaceManager, database: DatabaseManager, payload: SemanticModuleReviewArtifactPayload): Promise<ArtifactRef>;
111
165
  export declare function persistSemanticNameSuggestionsArtifact(workspaceManager: WorkspaceManager, database: DatabaseManager, payload: SemanticNameSuggestionArtifactPayload): Promise<ArtifactRef>;
112
166
  export declare function loadSemanticNameSuggestionIndex(workspaceManager: WorkspaceManager, database: DatabaseManager, sampleId: string, options?: LoadSemanticArtifactOptions): Promise<SemanticNameSuggestionIndex>;
113
167
  export declare function loadSemanticFunctionExplanationIndex(workspaceManager: WorkspaceManager, database: DatabaseManager, sampleId: string, options?: LoadSemanticArtifactOptions): Promise<SemanticFunctionExplanationIndex>;
168
+ export declare function loadSemanticModuleReviewIndex(workspaceManager: WorkspaceManager, database: DatabaseManager, sampleId: string, options?: LoadSemanticArtifactOptions): Promise<SemanticModuleReviewIndex>;
114
169
  export declare function findSemanticNameSuggestion(index: SemanticNameSuggestionIndex | null | undefined, address: string | null | undefined, funcName: string | null | undefined): LoadedSemanticNameSuggestion | null;
115
170
  export declare function findSemanticFunctionExplanation(index: SemanticFunctionExplanationIndex | null | undefined, address: string | null | undefined, funcName: string | null | undefined): LoadedSemanticFunctionExplanation | null;
171
+ export declare function findSemanticModuleReview(index: SemanticModuleReviewIndex | null | undefined, moduleName: string | null | undefined): LoadedSemanticModuleReview | null;
116
172
  //# sourceMappingURL=semantic-name-suggestion-artifacts.d.ts.map
@@ -6,6 +6,8 @@ export const SEMANTIC_NAME_SUGGESTIONS_ARTIFACT_TYPE = 'semantic_name_suggestion
6
6
  export const SEMANTIC_NAME_PREPARE_BUNDLE_ARTIFACT_TYPE = 'semantic_name_prepare_bundle';
7
7
  export const SEMANTIC_EXPLANATION_PREPARE_BUNDLE_ARTIFACT_TYPE = 'semantic_explanation_prepare_bundle';
8
8
  export const SEMANTIC_FUNCTION_EXPLANATIONS_ARTIFACT_TYPE = 'semantic_function_explanations';
9
+ export const SEMANTIC_MODULE_REVIEW_PREPARE_BUNDLE_ARTIFACT_TYPE = 'semantic_module_review_prepare_bundle';
10
+ export const SEMANTIC_MODULE_REVIEWS_ARTIFACT_TYPE = 'semantic_module_reviews';
9
11
  const LATEST_SEMANTIC_ARTIFACT_WINDOW_MS = 10 * 1000;
10
12
  function sanitizePathSegment(value, fallback) {
11
13
  const normalized = (value || fallback)
@@ -46,6 +48,10 @@ export function sanitizeSemanticName(value) {
46
48
  }
47
49
  return collapsed;
48
50
  }
51
+ export function normalizeModuleKey(value) {
52
+ const normalized = sanitizeSemanticName(value);
53
+ return normalized && normalized.length > 0 ? normalized : null;
54
+ }
49
55
  async function persistSemanticNamingJsonArtifact(workspaceManager, database, sampleId, artifactType, filePrefix, payload, sessionTag) {
50
56
  const workspace = await workspaceManager.createWorkspace(sampleId);
51
57
  const sessionSegment = sanitizePathSegment(sessionTag || undefined, 'default');
@@ -85,6 +91,12 @@ export async function persistSemanticExplanationPrepareBundleArtifact(workspaceM
85
91
  export async function persistSemanticFunctionExplanationsArtifact(workspaceManager, database, payload) {
86
92
  return persistSemanticNamingJsonArtifact(workspaceManager, database, payload.sample_id, SEMANTIC_FUNCTION_EXPLANATIONS_ARTIFACT_TYPE, 'semantic_function_explanations', payload, payload.session_tag);
87
93
  }
94
+ export async function persistSemanticModuleReviewPrepareBundleArtifact(workspaceManager, database, sampleId, payload, sessionTag) {
95
+ return persistSemanticNamingJsonArtifact(workspaceManager, database, sampleId, SEMANTIC_MODULE_REVIEW_PREPARE_BUNDLE_ARTIFACT_TYPE, 'module_review_bundle', payload, sessionTag);
96
+ }
97
+ export async function persistSemanticModuleReviewsArtifact(workspaceManager, database, payload) {
98
+ return persistSemanticNamingJsonArtifact(workspaceManager, database, payload.sample_id, SEMANTIC_MODULE_REVIEWS_ARTIFACT_TYPE, 'semantic_module_reviews', payload, payload.session_tag);
99
+ }
88
100
  export async function persistSemanticNameSuggestionsArtifact(workspaceManager, database, payload) {
89
101
  return persistSemanticNamingJsonArtifact(workspaceManager, database, payload.sample_id, SEMANTIC_NAME_SUGGESTIONS_ARTIFACT_TYPE, 'semantic_name_suggestions', payload, payload.session_tag);
90
102
  }
@@ -283,6 +295,71 @@ export async function loadSemanticFunctionExplanationIndex(workspaceManager, dat
283
295
  : 'Semantic explanation artifacts reflect the selected artifact scope.',
284
296
  };
285
297
  }
298
+ export async function loadSemanticModuleReviewIndex(workspaceManager, database, sampleId, options = {}) {
299
+ const byModule = new Map();
300
+ const artifacts = database.findArtifactsByType(sampleId, SEMANTIC_MODULE_REVIEWS_ARTIFACT_TYPE);
301
+ const loadedArtifacts = [];
302
+ for (const artifact of artifacts) {
303
+ const payload = (await readArtifactJson(workspaceManager, database, sampleId, artifact.id));
304
+ loadedArtifacts.push({ artifact, payload });
305
+ }
306
+ const filteredArtifacts = filterSemanticArtifactsByScope(loadedArtifacts, options);
307
+ for (const { artifact, payload } of filteredArtifacts) {
308
+ if (!payload || !Array.isArray(payload.reviews)) {
309
+ continue;
310
+ }
311
+ for (const review of payload.reviews) {
312
+ const moduleKey = normalizeModuleKey(review.module_name);
313
+ if (!moduleKey || byModule.has(moduleKey)) {
314
+ continue;
315
+ }
316
+ byModule.set(moduleKey, {
317
+ module_name: review.module_name,
318
+ refined_name: review.refined_name || null,
319
+ summary: review.summary,
320
+ role_hint: review.role_hint || null,
321
+ confidence: review.confidence,
322
+ assumptions: review.assumptions || [],
323
+ evidence_used: review.evidence_used || [],
324
+ rewrite_guidance: review.rewrite_guidance || [],
325
+ focus_areas: review.focus_areas || [],
326
+ priority_functions: review.priority_functions || [],
327
+ artifact_id: artifact.id,
328
+ created_at: artifact.created_at,
329
+ client_name: payload.client_name || null,
330
+ model_name: payload.model_name || null,
331
+ session_tag: payload.session_tag || null,
332
+ prepare_artifact_id: payload.prepare_artifact_id || null,
333
+ });
334
+ }
335
+ }
336
+ return {
337
+ byModule,
338
+ marker: filteredArtifacts.length > 0
339
+ ? filteredArtifacts.map(({ artifact }) => `${artifact.id}:${artifact.sha256}`).join('|')
340
+ : 'none',
341
+ artifact_ids: filteredArtifacts.map(({ artifact }) => artifact.id),
342
+ session_tags: Array.from(new Set(filteredArtifacts.flatMap(({ artifact, payload }) => collectSemanticArtifactSessionTags(artifact.path, payload?.session_tag)))),
343
+ earliest_created_at: filteredArtifacts.length > 0
344
+ ? filteredArtifacts
345
+ .map(({ artifact }) => artifact.created_at)
346
+ .filter((item) => typeof item === 'string' && item.length > 0)
347
+ .sort()[0] || null
348
+ : null,
349
+ latest_created_at: filteredArtifacts.length > 0
350
+ ? filteredArtifacts
351
+ .map(({ artifact }) => artifact.created_at)
352
+ .filter((item) => typeof item === 'string' && item.length > 0)
353
+ .sort()
354
+ .slice(-1)[0] || null
355
+ : null,
356
+ scope_note: options.scope === 'session' && options.sessionTag?.trim()
357
+ ? `Semantic module review artifacts are limited to session selector "${options.sessionTag.trim()}".`
358
+ : options.scope === 'latest'
359
+ ? 'Semantic module review artifacts are limited to the latest artifact window.'
360
+ : 'Semantic module review artifacts reflect the selected artifact scope.',
361
+ };
362
+ }
286
363
  export function findSemanticNameSuggestion(index, address, funcName) {
287
364
  if (!index) {
288
365
  return null;
@@ -311,4 +388,14 @@ export function findSemanticFunctionExplanation(index, address, funcName) {
311
388
  }
312
389
  return null;
313
390
  }
391
+ export function findSemanticModuleReview(index, moduleName) {
392
+ if (!index) {
393
+ return null;
394
+ }
395
+ const moduleKey = normalizeModuleKey(moduleName);
396
+ if (moduleKey && index.byModule.has(moduleKey)) {
397
+ return index.byModule.get(moduleKey) || null;
398
+ }
399
+ return null;
400
+ }
314
401
  //# sourceMappingURL=semantic-name-suggestion-artifacts.js.map
package/dist/server.js CHANGED
@@ -30,7 +30,7 @@ export class MCPServer {
30
30
  // Initialize MCP SDK server
31
31
  this.server = new Server({
32
32
  name: 'windows-exe-decompiler-mcp-server',
33
- version: '0.1.0',
33
+ version: '0.1.3',
34
34
  }, {
35
35
  capabilities: {
36
36
  tools: {},