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,542 @@
1
+ /**
2
+ * ioc.export tool
3
+ * Export layered IOC data and optional ATT&CK mapping in JSON / CSV / STIX 2.1.
4
+ */
5
+ import fs from 'fs/promises';
6
+ import path from 'path';
7
+ import { createHash, randomUUID } from 'crypto';
8
+ import { z } from 'zod';
9
+ import { createTriageWorkflowHandler } from '../workflows/triage.js';
10
+ import { createPackerDetectHandler } from './packer-detect.js';
11
+ import { mapIndicatorsToAttack } from './attack-map.js';
12
+ const TOOL_NAME = 'ioc.export';
13
+ const TOOL_VERSION = '0.1.0';
14
+ export const IOCExportInputSchema = z.object({
15
+ sample_id: z.string().describe('Sample ID (format: sha256:<hex>)'),
16
+ format: z.enum(['json', 'csv', 'stix2']).optional().default('json'),
17
+ include_attack_map: z
18
+ .boolean()
19
+ .optional()
20
+ .default(true)
21
+ .describe('Include ATT&CK mapping block in export payload'),
22
+ include_low_confidence: z
23
+ .boolean()
24
+ .optional()
25
+ .default(false)
26
+ .describe('Include low-confidence IOC and ATT&CK records'),
27
+ max_iocs: z.number().int().min(1).max(5000).optional().default(300),
28
+ persist_artifact: z.boolean().optional().default(true),
29
+ force_refresh: z
30
+ .boolean()
31
+ .optional()
32
+ .default(false)
33
+ .describe('Bypass cache in upstream analysis tools'),
34
+ });
35
+ const IOCRecordSchema = z.object({
36
+ type: z.string(),
37
+ value: z.string(),
38
+ confidence: z.enum(['high', 'medium', 'low']),
39
+ source: z.string(),
40
+ tags: z.array(z.string()),
41
+ });
42
+ export const IOCExportOutputSchema = z.object({
43
+ ok: z.boolean(),
44
+ data: z
45
+ .object({
46
+ sample_id: z.string(),
47
+ format: z.enum(['json', 'csv', 'stix2']),
48
+ tool_version: z.string(),
49
+ ioc_count: z.number(),
50
+ iocs: z.array(IOCRecordSchema),
51
+ content: z.string(),
52
+ mime_type: z.string(),
53
+ attack_technique_count: z.number(),
54
+ artifact: z
55
+ .object({
56
+ id: z.string(),
57
+ path: z.string(),
58
+ type: z.string(),
59
+ sha256: z.string(),
60
+ mime: z.string(),
61
+ })
62
+ .optional(),
63
+ })
64
+ .optional(),
65
+ warnings: z.array(z.string()).optional(),
66
+ errors: z.array(z.string()).optional(),
67
+ artifacts: z.array(z.any()).optional(),
68
+ metrics: z
69
+ .object({
70
+ elapsed_ms: z.number(),
71
+ tool: z.string(),
72
+ })
73
+ .optional(),
74
+ });
75
+ export const iocExportToolDefinition = {
76
+ name: TOOL_NAME,
77
+ description: 'Export normalized IOC data and optional ATT&CK mapping as JSON, CSV, or STIX 2.1 bundle.',
78
+ inputSchema: IOCExportInputSchema,
79
+ outputSchema: IOCExportOutputSchema,
80
+ };
81
+ function toStringArray(value) {
82
+ if (!Array.isArray(value)) {
83
+ return [];
84
+ }
85
+ return value.filter((item) => typeof item === 'string');
86
+ }
87
+ function dedupeIOC(records) {
88
+ const map = new Map();
89
+ for (const record of records) {
90
+ const key = `${record.type}::${record.value.toLowerCase()}`;
91
+ const existing = map.get(key);
92
+ if (!existing) {
93
+ map.set(key, record);
94
+ continue;
95
+ }
96
+ const confidenceRank = {
97
+ low: 1,
98
+ medium: 2,
99
+ high: 3,
100
+ };
101
+ const betterConfidence = confidenceRank[record.confidence] > confidenceRank[existing.confidence]
102
+ ? record.confidence
103
+ : existing.confidence;
104
+ map.set(key, {
105
+ ...existing,
106
+ confidence: betterConfidence,
107
+ tags: Array.from(new Set([...existing.tags, ...record.tags])),
108
+ source: Array.from(new Set([existing.source, record.source])).join('+'),
109
+ });
110
+ }
111
+ return Array.from(map.values());
112
+ }
113
+ function collectIOCRecords(iocs, includeLow) {
114
+ const records = [];
115
+ const highValue = (iocs.high_value_iocs || {});
116
+ for (const value of toStringArray(iocs.urls)) {
117
+ records.push({ type: 'url', value, confidence: 'high', source: 'triage.urls', tags: ['network'] });
118
+ }
119
+ for (const value of toStringArray(iocs.ip_addresses)) {
120
+ records.push({ type: 'ipv4', value, confidence: 'high', source: 'triage.ip_addresses', tags: ['network'] });
121
+ }
122
+ for (const value of toStringArray(iocs.registry_keys)) {
123
+ records.push({
124
+ type: 'registry_key',
125
+ value,
126
+ confidence: 'medium',
127
+ source: 'triage.registry_keys',
128
+ tags: ['persistence'],
129
+ });
130
+ }
131
+ for (const value of toStringArray(iocs.file_paths)) {
132
+ records.push({
133
+ type: 'file_path',
134
+ value,
135
+ confidence: 'medium',
136
+ source: 'triage.file_paths',
137
+ tags: ['filesystem'],
138
+ });
139
+ }
140
+ for (const value of toStringArray(iocs.suspicious_imports)) {
141
+ records.push({
142
+ type: 'api',
143
+ value,
144
+ confidence: 'medium',
145
+ source: 'triage.suspicious_imports',
146
+ tags: ['api'],
147
+ });
148
+ }
149
+ for (const value of toStringArray(highValue.commands)) {
150
+ records.push({
151
+ type: 'command',
152
+ value,
153
+ confidence: 'medium',
154
+ source: 'triage.high_value_iocs.commands',
155
+ tags: ['execution'],
156
+ });
157
+ }
158
+ for (const value of toStringArray(highValue.pipes)) {
159
+ records.push({
160
+ type: 'pipe',
161
+ value,
162
+ confidence: 'medium',
163
+ source: 'triage.high_value_iocs.pipes',
164
+ tags: ['ipc'],
165
+ });
166
+ }
167
+ for (const value of toStringArray(iocs.yara_matches)) {
168
+ records.push({
169
+ type: 'yara_rule',
170
+ value,
171
+ confidence: 'high',
172
+ source: 'triage.yara_matches',
173
+ tags: ['detection'],
174
+ });
175
+ }
176
+ for (const value of toStringArray(iocs.yara_low_confidence)) {
177
+ records.push({
178
+ type: 'yara_rule',
179
+ value,
180
+ confidence: 'low',
181
+ source: 'triage.yara_low_confidence',
182
+ tags: ['detection', 'low_confidence'],
183
+ });
184
+ }
185
+ let deduped = dedupeIOC(records);
186
+ if (!includeLow) {
187
+ deduped = deduped.filter((item) => item.confidence !== 'low');
188
+ }
189
+ return deduped;
190
+ }
191
+ function csvEscape(value) {
192
+ if (value.includes('"') || value.includes(',') || value.includes('\n')) {
193
+ return `"${value.replace(/"/g, '""')}"`;
194
+ }
195
+ return value;
196
+ }
197
+ function toCSV(records) {
198
+ const lines = ['type,value,confidence,source,tags'];
199
+ for (const record of records) {
200
+ lines.push([
201
+ csvEscape(record.type),
202
+ csvEscape(record.value),
203
+ csvEscape(record.confidence),
204
+ csvEscape(record.source),
205
+ csvEscape(record.tags.join('|')),
206
+ ].join(','));
207
+ }
208
+ return lines.join('\n');
209
+ }
210
+ function normalizeStixTimestamp(date) {
211
+ return date.toISOString().replace(/\.\d{3}Z$/, 'Z');
212
+ }
213
+ function buildIndicatorPattern(record) {
214
+ const escaped = record.value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
215
+ if (record.type === 'url') {
216
+ return `[url:value = '${escaped}']`;
217
+ }
218
+ if (record.type === 'ipv4' && /^(?:\d{1,3}\.){3}\d{1,3}$/.test(record.value)) {
219
+ return `[ipv4-addr:value = '${escaped}']`;
220
+ }
221
+ if (record.type === 'registry_key') {
222
+ return `[windows-registry-key:key = '${escaped}']`;
223
+ }
224
+ if (record.type === 'file_path') {
225
+ const fileName = path.basename(record.value).replace(/\\/g, '\\\\').replace(/'/g, "\\'");
226
+ return `[file:name = '${fileName}']`;
227
+ }
228
+ if (record.type === 'command') {
229
+ return `[process:command_line = '${escaped}']`;
230
+ }
231
+ return null;
232
+ }
233
+ function toSTIX(sampleId, records, attackTechniques) {
234
+ const now = new Date();
235
+ const created = normalizeStixTimestamp(now);
236
+ const objects = [];
237
+ const objectRefs = [];
238
+ const indicatorIds = [];
239
+ for (const record of records) {
240
+ const observedDataId = `observed-data--${randomUUID()}`;
241
+ const observedObjects = {
242
+ '0': {
243
+ type: record.type === 'url'
244
+ ? 'url'
245
+ : record.type === 'ipv4'
246
+ ? 'ipv4-addr'
247
+ : record.type === 'registry_key'
248
+ ? 'windows-registry-key'
249
+ : record.type === 'file_path'
250
+ ? 'file'
251
+ : record.type === 'command'
252
+ ? 'process'
253
+ : record.type === 'api'
254
+ ? 'x-mcp-api-call'
255
+ : record.type === 'pipe'
256
+ ? 'x-mcp-pipe'
257
+ : 'x-mcp-ioc',
258
+ value: record.value,
259
+ command_line: record.type === 'command' ? record.value : undefined,
260
+ key: record.type === 'registry_key' ? record.value : undefined,
261
+ name: record.type === 'file_path' ? path.basename(record.value) : record.value,
262
+ },
263
+ };
264
+ objects.push({
265
+ type: 'observed-data',
266
+ spec_version: '2.1',
267
+ id: observedDataId,
268
+ created,
269
+ modified: created,
270
+ first_observed: created,
271
+ last_observed: created,
272
+ number_observed: 1,
273
+ objects: observedObjects,
274
+ labels: record.tags,
275
+ x_mcp_source: record.source,
276
+ x_mcp_confidence_level: record.confidence,
277
+ });
278
+ objectRefs.push(observedDataId);
279
+ const pattern = buildIndicatorPattern(record);
280
+ if (pattern) {
281
+ const id = `indicator--${randomUUID()}`;
282
+ objects.push({
283
+ type: 'indicator',
284
+ spec_version: '2.1',
285
+ id,
286
+ created,
287
+ modified: created,
288
+ name: `${record.type}:${record.value.slice(0, 80)}`,
289
+ pattern_type: 'stix',
290
+ pattern,
291
+ valid_from: created,
292
+ confidence: record.confidence === 'high' ? 80 : record.confidence === 'medium' ? 60 : 35,
293
+ labels: record.tags,
294
+ });
295
+ objectRefs.push(id);
296
+ indicatorIds.push(id);
297
+ objects.push({
298
+ type: 'relationship',
299
+ spec_version: '2.1',
300
+ id: `relationship--${randomUUID()}`,
301
+ created,
302
+ modified: created,
303
+ relationship_type: 'based-on',
304
+ source_ref: id,
305
+ target_ref: observedDataId,
306
+ });
307
+ continue;
308
+ }
309
+ const noteId = `note--${randomUUID()}`;
310
+ objects.push({
311
+ type: 'note',
312
+ spec_version: '2.1',
313
+ id: noteId,
314
+ created,
315
+ modified: created,
316
+ abstract: `IOC (${record.type})`,
317
+ content: `${record.value}\nsource=${record.source}\nconfidence=${record.confidence}`,
318
+ labels: record.tags,
319
+ object_refs: [observedDataId],
320
+ });
321
+ objectRefs.push(noteId);
322
+ }
323
+ for (const technique of attackTechniques) {
324
+ const attackPatternId = `attack-pattern--${randomUUID()}`;
325
+ objects.push({
326
+ type: 'attack-pattern',
327
+ spec_version: '2.1',
328
+ id: attackPatternId,
329
+ created,
330
+ modified: created,
331
+ name: `${technique.technique_id} ${technique.name}`,
332
+ external_references: [
333
+ {
334
+ source_name: 'mitre-attack',
335
+ external_id: technique.technique_id,
336
+ url: `https://attack.mitre.org/techniques/${technique.technique_id.replace('.', '/')}/`,
337
+ },
338
+ ],
339
+ x_mcp_confidence: Number(technique.confidence.toFixed(2)),
340
+ kill_chain_phases: technique.tactics.map((tactic) => ({
341
+ kill_chain_name: 'mitre-attack',
342
+ phase_name: tactic.toLowerCase().replace(/\s+/g, '-'),
343
+ })),
344
+ });
345
+ objectRefs.push(attackPatternId);
346
+ for (const indicatorId of indicatorIds.slice(0, 30)) {
347
+ objects.push({
348
+ type: 'relationship',
349
+ spec_version: '2.1',
350
+ id: `relationship--${randomUUID()}`,
351
+ created,
352
+ modified: created,
353
+ relationship_type: 'related-to',
354
+ source_ref: attackPatternId,
355
+ target_ref: indicatorId,
356
+ });
357
+ }
358
+ }
359
+ const reportId = `report--${randomUUID()}`;
360
+ objects.push({
361
+ type: 'report',
362
+ spec_version: '2.1',
363
+ id: reportId,
364
+ created,
365
+ modified: created,
366
+ name: `IOC export for ${sampleId}`,
367
+ description: 'Generated by ioc.export tool',
368
+ report_types: ['threat-report'],
369
+ object_refs: objectRefs,
370
+ published: created,
371
+ });
372
+ return JSON.stringify({
373
+ type: 'bundle',
374
+ id: `bundle--${randomUUID()}`,
375
+ spec_version: '2.1',
376
+ objects,
377
+ }, null, 2);
378
+ }
379
+ function formatContent(sampleId, format, records, attackTechniques) {
380
+ if (format === 'csv') {
381
+ return {
382
+ content: toCSV(records),
383
+ mimeType: 'text/csv',
384
+ extension: 'csv',
385
+ };
386
+ }
387
+ if (format === 'stix2') {
388
+ return {
389
+ content: toSTIX(sampleId, records, attackTechniques),
390
+ mimeType: 'application/stix+json',
391
+ extension: 'json',
392
+ };
393
+ }
394
+ return {
395
+ content: JSON.stringify({
396
+ sample_id: sampleId,
397
+ generated_at: new Date().toISOString(),
398
+ iocs: records,
399
+ attack_map: attackTechniques,
400
+ }, null, 2),
401
+ mimeType: 'application/json',
402
+ extension: 'json',
403
+ };
404
+ }
405
+ export function createIOCExportHandler(workspaceManager, database, cacheManager) {
406
+ const triageHandler = createTriageWorkflowHandler(workspaceManager, database, cacheManager);
407
+ const packerHandler = createPackerDetectHandler(workspaceManager, database, cacheManager);
408
+ return async (args) => {
409
+ const startTime = Date.now();
410
+ try {
411
+ const input = IOCExportInputSchema.parse(args);
412
+ const sample = database.findSample(input.sample_id);
413
+ if (!sample) {
414
+ return {
415
+ ok: false,
416
+ errors: [`Sample not found: ${input.sample_id}`],
417
+ metrics: {
418
+ elapsed_ms: Date.now() - startTime,
419
+ tool: TOOL_NAME,
420
+ },
421
+ };
422
+ }
423
+ const triageResult = await triageHandler({
424
+ sample_id: input.sample_id,
425
+ force_refresh: input.force_refresh,
426
+ });
427
+ if (!triageResult.ok || !triageResult.data) {
428
+ return {
429
+ ok: false,
430
+ errors: triageResult.errors || ['workflow.triage failed'],
431
+ warnings: triageResult.warnings,
432
+ metrics: {
433
+ elapsed_ms: Date.now() - startTime,
434
+ tool: TOOL_NAME,
435
+ },
436
+ };
437
+ }
438
+ const triageData = triageResult.data;
439
+ const iocs = (triageData.iocs || {});
440
+ let records = collectIOCRecords(iocs, input.include_low_confidence);
441
+ records = records.slice(0, input.max_iocs);
442
+ let attackTechniques = [];
443
+ const warnings = [...(triageResult.warnings || [])];
444
+ if (input.include_attack_map) {
445
+ const highValue = (iocs.high_value_iocs || {});
446
+ const packerResult = await packerHandler({
447
+ sample_id: input.sample_id,
448
+ force_refresh: input.force_refresh,
449
+ });
450
+ const packerData = (packerResult.data || {});
451
+ const indicators = {
452
+ suspiciousImports: toStringArray(iocs.suspicious_imports),
453
+ suspiciousStrings: toStringArray(iocs.suspicious_strings),
454
+ commands: toStringArray(highValue.commands),
455
+ urls: toStringArray(iocs.urls),
456
+ ips: toStringArray(iocs.ip_addresses),
457
+ registryKeys: toStringArray(iocs.registry_keys),
458
+ yaraMatches: toStringArray(iocs.yara_matches),
459
+ yaraLowConfidence: toStringArray(iocs.yara_low_confidence),
460
+ packed: Boolean(packerData.packed),
461
+ packerConfidence: typeof packerData.confidence === 'number' ? Number(packerData.confidence) : 0,
462
+ runtimeHints: [],
463
+ };
464
+ const mapped = mapIndicatorsToAttack(indicators, {
465
+ includeLowConfidence: input.include_low_confidence,
466
+ maxTechniques: 80,
467
+ });
468
+ attackTechniques = mapped.techniques.map((item) => ({
469
+ technique_id: item.technique_id,
470
+ name: item.name,
471
+ tactics: item.tactics,
472
+ confidence: item.confidence,
473
+ }));
474
+ if (packerResult.warnings) {
475
+ warnings.push(...packerResult.warnings);
476
+ }
477
+ }
478
+ const formatted = formatContent(input.sample_id, input.format, records, attackTechniques);
479
+ const artifacts = [];
480
+ let artifactRef;
481
+ if (input.persist_artifact) {
482
+ const workspace = await workspaceManager.getWorkspace(input.sample_id);
483
+ const exportDir = path.join(workspace.reports, 'ioc_exports');
484
+ await fs.mkdir(exportDir, { recursive: true });
485
+ const fileName = `ioc_export_${Date.now()}.${formatted.extension}`;
486
+ const absPath = path.join(exportDir, fileName);
487
+ await fs.writeFile(absPath, formatted.content, 'utf-8');
488
+ const artifactId = randomUUID();
489
+ const sha256 = createHash('sha256').update(formatted.content).digest('hex');
490
+ const relativePath = `reports/ioc_exports/${fileName}`;
491
+ database.insertArtifact({
492
+ id: artifactId,
493
+ sample_id: input.sample_id,
494
+ type: `ioc_export_${input.format}`,
495
+ path: relativePath,
496
+ sha256,
497
+ mime: formatted.mimeType,
498
+ created_at: new Date().toISOString(),
499
+ });
500
+ artifactRef = {
501
+ id: artifactId,
502
+ type: `ioc_export_${input.format}`,
503
+ path: relativePath,
504
+ sha256,
505
+ mime: formatted.mimeType,
506
+ };
507
+ artifacts.push(artifactRef);
508
+ }
509
+ return {
510
+ ok: true,
511
+ data: {
512
+ sample_id: input.sample_id,
513
+ format: input.format,
514
+ tool_version: TOOL_VERSION,
515
+ ioc_count: records.length,
516
+ iocs: records,
517
+ content: formatted.content,
518
+ mime_type: formatted.mimeType,
519
+ attack_technique_count: attackTechniques.length,
520
+ artifact: artifactRef,
521
+ },
522
+ warnings: warnings.length > 0 ? Array.from(new Set(warnings)) : undefined,
523
+ artifacts,
524
+ metrics: {
525
+ elapsed_ms: Date.now() - startTime,
526
+ tool: TOOL_NAME,
527
+ },
528
+ };
529
+ }
530
+ catch (error) {
531
+ return {
532
+ ok: false,
533
+ errors: [error.message],
534
+ metrics: {
535
+ elapsed_ms: Date.now() - startTime,
536
+ tool: TOOL_NAME,
537
+ },
538
+ };
539
+ }
540
+ };
541
+ }
542
+ //# sourceMappingURL=ioc-export.js.map
@@ -0,0 +1,165 @@
1
+ /**
2
+ * packer.detect tool implementation
3
+ * Detects if a PE file is packed and identifies the packer used
4
+ * Requirements: 7.1, 7.2, 7.3
5
+ */
6
+ import { z } from 'zod';
7
+ import type { ToolDefinition, ToolArgs, WorkerResult } from '../types.js';
8
+ import type { WorkspaceManager } from '../workspace-manager.js';
9
+ import type { DatabaseManager } from '../database.js';
10
+ import type { CacheManager } from '../cache-manager.js';
11
+ /**
12
+ * Input schema for packer.detect tool
13
+ * Requirements: 7.1
14
+ */
15
+ export declare const PackerDetectInputSchema: z.ZodObject<{
16
+ sample_id: z.ZodString;
17
+ engines: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodEnum<["yara", "entropy", "entrypoint"]>, "many">>>;
18
+ force_refresh: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
19
+ }, "strip", z.ZodTypeAny, {
20
+ sample_id: string;
21
+ force_refresh: boolean;
22
+ engines: ("entropy" | "yara" | "entrypoint")[];
23
+ }, {
24
+ sample_id: string;
25
+ force_refresh?: boolean | undefined;
26
+ engines?: ("entropy" | "yara" | "entrypoint")[] | undefined;
27
+ }>;
28
+ export type PackerDetectInput = z.infer<typeof PackerDetectInputSchema>;
29
+ /**
30
+ * Output schema for packer.detect tool
31
+ * Requirements: 7.1, 7.2, 7.3, 7.4, 7.5
32
+ */
33
+ export declare const PackerDetectOutputSchema: z.ZodObject<{
34
+ ok: z.ZodBoolean;
35
+ data: z.ZodOptional<z.ZodObject<{
36
+ packed: z.ZodBoolean;
37
+ confidence: z.ZodNumber;
38
+ detections: z.ZodArray<z.ZodObject<{
39
+ method: z.ZodString;
40
+ name: z.ZodString;
41
+ confidence: z.ZodNumber;
42
+ details: z.ZodRecord<z.ZodString, z.ZodAny>;
43
+ }, "strip", z.ZodTypeAny, {
44
+ name: string;
45
+ confidence: number;
46
+ method: string;
47
+ details: Record<string, any>;
48
+ }, {
49
+ name: string;
50
+ confidence: number;
51
+ method: string;
52
+ details: Record<string, any>;
53
+ }>, "many">;
54
+ methods: z.ZodArray<z.ZodString, "many">;
55
+ confidence_breakdown: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
56
+ feature_fusion: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
57
+ evidence: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
58
+ inference: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
59
+ }, "strip", z.ZodTypeAny, {
60
+ confidence: number;
61
+ methods: string[];
62
+ packed: boolean;
63
+ detections: {
64
+ name: string;
65
+ confidence: number;
66
+ method: string;
67
+ details: Record<string, any>;
68
+ }[];
69
+ evidence?: Record<string, any> | undefined;
70
+ inference?: Record<string, any> | undefined;
71
+ confidence_breakdown?: Record<string, number> | undefined;
72
+ feature_fusion?: Record<string, any> | undefined;
73
+ }, {
74
+ confidence: number;
75
+ methods: string[];
76
+ packed: boolean;
77
+ detections: {
78
+ name: string;
79
+ confidence: number;
80
+ method: string;
81
+ details: Record<string, any>;
82
+ }[];
83
+ evidence?: Record<string, any> | undefined;
84
+ inference?: Record<string, any> | undefined;
85
+ confidence_breakdown?: Record<string, number> | undefined;
86
+ feature_fusion?: Record<string, any> | undefined;
87
+ }>>;
88
+ warnings: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
89
+ errors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
90
+ artifacts: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
91
+ metrics: z.ZodOptional<z.ZodObject<{
92
+ elapsed_ms: z.ZodNumber;
93
+ tool: z.ZodString;
94
+ engines_used: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
95
+ }, "strip", z.ZodTypeAny, {
96
+ elapsed_ms: number;
97
+ tool: string;
98
+ engines_used?: string[] | undefined;
99
+ }, {
100
+ elapsed_ms: number;
101
+ tool: string;
102
+ engines_used?: string[] | undefined;
103
+ }>>;
104
+ }, "strip", z.ZodTypeAny, {
105
+ ok: boolean;
106
+ metrics?: {
107
+ elapsed_ms: number;
108
+ tool: string;
109
+ engines_used?: string[] | undefined;
110
+ } | undefined;
111
+ data?: {
112
+ confidence: number;
113
+ methods: string[];
114
+ packed: boolean;
115
+ detections: {
116
+ name: string;
117
+ confidence: number;
118
+ method: string;
119
+ details: Record<string, any>;
120
+ }[];
121
+ evidence?: Record<string, any> | undefined;
122
+ inference?: Record<string, any> | undefined;
123
+ confidence_breakdown?: Record<string, number> | undefined;
124
+ feature_fusion?: Record<string, any> | undefined;
125
+ } | undefined;
126
+ warnings?: string[] | undefined;
127
+ errors?: string[] | undefined;
128
+ artifacts?: any[] | undefined;
129
+ }, {
130
+ ok: boolean;
131
+ metrics?: {
132
+ elapsed_ms: number;
133
+ tool: string;
134
+ engines_used?: string[] | undefined;
135
+ } | undefined;
136
+ data?: {
137
+ confidence: number;
138
+ methods: string[];
139
+ packed: boolean;
140
+ detections: {
141
+ name: string;
142
+ confidence: number;
143
+ method: string;
144
+ details: Record<string, any>;
145
+ }[];
146
+ evidence?: Record<string, any> | undefined;
147
+ inference?: Record<string, any> | undefined;
148
+ confidence_breakdown?: Record<string, number> | undefined;
149
+ feature_fusion?: Record<string, any> | undefined;
150
+ } | undefined;
151
+ warnings?: string[] | undefined;
152
+ errors?: string[] | undefined;
153
+ artifacts?: any[] | undefined;
154
+ }>;
155
+ export type PackerDetectOutput = z.infer<typeof PackerDetectOutputSchema>;
156
+ /**
157
+ * Tool definition for packer.detect
158
+ */
159
+ export declare const packerDetectToolDefinition: ToolDefinition;
160
+ /**
161
+ * Create packer.detect tool handler
162
+ * Requirements: 7.1, 7.2, 7.3, 7.4, 7.5
163
+ */
164
+ export declare function createPackerDetectHandler(workspaceManager: WorkspaceManager, database: DatabaseManager, cacheManager: CacheManager): (args: ToolArgs) => Promise<WorkerResult>;
165
+ //# sourceMappingURL=packer-detect.d.ts.map