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,375 @@
1
+ /**
2
+ * strings.extract tool implementation
3
+ * Extracts readable strings (ASCII and Unicode) from PE files
4
+ * Requirements: 4.1, 4.2, 4.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 strings.extract tool
13
+ * Requirements: 4.1, 4.2, 4.3
14
+ */
15
+ export declare const StringsExtractInputSchema: z.ZodObject<{
16
+ sample_id: z.ZodString;
17
+ min_len: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
18
+ encoding: z.ZodDefault<z.ZodOptional<z.ZodEnum<["ascii", "unicode", "all"]>>>;
19
+ max_strings: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
20
+ max_string_length: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
21
+ context_window_bytes: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
22
+ max_context_windows: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
23
+ category_filter: z.ZodDefault<z.ZodOptional<z.ZodEnum<["all", "ioc", "url", "network", "ipc", "command", "registry", "file_path", "suspicious_api"]>>>;
24
+ force_refresh: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
25
+ }, "strip", z.ZodTypeAny, {
26
+ sample_id: string;
27
+ encoding: "ascii" | "all" | "unicode";
28
+ force_refresh: boolean;
29
+ min_len: number;
30
+ max_strings: number;
31
+ max_string_length: number;
32
+ context_window_bytes: number;
33
+ max_context_windows: number;
34
+ category_filter: "all" | "registry" | "network" | "command" | "ioc" | "url" | "ipc" | "file_path" | "suspicious_api";
35
+ }, {
36
+ sample_id: string;
37
+ encoding?: "ascii" | "all" | "unicode" | undefined;
38
+ force_refresh?: boolean | undefined;
39
+ min_len?: number | undefined;
40
+ max_strings?: number | undefined;
41
+ max_string_length?: number | undefined;
42
+ context_window_bytes?: number | undefined;
43
+ max_context_windows?: number | undefined;
44
+ category_filter?: "all" | "registry" | "network" | "command" | "ioc" | "url" | "ipc" | "file_path" | "suspicious_api" | undefined;
45
+ }>;
46
+ export type StringsExtractInput = z.infer<typeof StringsExtractInputSchema>;
47
+ /**
48
+ * Output schema for strings.extract tool
49
+ * Requirements: 4.1, 4.2, 4.3, 4.6
50
+ */
51
+ export declare const StringsExtractOutputSchema: z.ZodObject<{
52
+ ok: z.ZodBoolean;
53
+ data: z.ZodOptional<z.ZodObject<{
54
+ strings: z.ZodArray<z.ZodObject<{
55
+ offset: z.ZodNumber;
56
+ string: z.ZodString;
57
+ encoding: z.ZodString;
58
+ }, "strip", z.ZodTypeAny, {
59
+ string: string;
60
+ encoding: string;
61
+ offset: number;
62
+ }, {
63
+ string: string;
64
+ encoding: string;
65
+ offset: number;
66
+ }>, "many">;
67
+ count: z.ZodNumber;
68
+ total_count: z.ZodOptional<z.ZodNumber>;
69
+ pre_filter_count: z.ZodOptional<z.ZodNumber>;
70
+ truncated: z.ZodOptional<z.ZodBoolean>;
71
+ max_strings: z.ZodOptional<z.ZodNumber>;
72
+ max_string_length: z.ZodOptional<z.ZodNumber>;
73
+ min_len: z.ZodNumber;
74
+ encoding_filter: z.ZodString;
75
+ category_filter: z.ZodOptional<z.ZodString>;
76
+ summary: z.ZodOptional<z.ZodObject<{
77
+ cluster_counts: z.ZodRecord<z.ZodString, z.ZodNumber>;
78
+ clusters: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>;
79
+ top_high_value: z.ZodArray<z.ZodObject<{
80
+ offset: z.ZodNumber;
81
+ string: z.ZodString;
82
+ encoding: z.ZodString;
83
+ categories: z.ZodArray<z.ZodString, "many">;
84
+ }, "strip", z.ZodTypeAny, {
85
+ string: string;
86
+ encoding: string;
87
+ offset: number;
88
+ categories: string[];
89
+ }, {
90
+ string: string;
91
+ encoding: string;
92
+ offset: number;
93
+ categories: string[];
94
+ }>, "many">;
95
+ context_windows: z.ZodOptional<z.ZodArray<z.ZodObject<{
96
+ start_offset: z.ZodNumber;
97
+ end_offset: z.ZodNumber;
98
+ score: z.ZodNumber;
99
+ categories: z.ZodArray<z.ZodString, "many">;
100
+ strings: z.ZodArray<z.ZodObject<{
101
+ offset: z.ZodNumber;
102
+ string: z.ZodString;
103
+ encoding: z.ZodString;
104
+ categories: z.ZodArray<z.ZodString, "many">;
105
+ }, "strip", z.ZodTypeAny, {
106
+ string: string;
107
+ encoding: string;
108
+ offset: number;
109
+ categories: string[];
110
+ }, {
111
+ string: string;
112
+ encoding: string;
113
+ offset: number;
114
+ categories: string[];
115
+ }>, "many">;
116
+ }, "strip", z.ZodTypeAny, {
117
+ score: number;
118
+ strings: {
119
+ string: string;
120
+ encoding: string;
121
+ offset: number;
122
+ categories: string[];
123
+ }[];
124
+ categories: string[];
125
+ start_offset: number;
126
+ end_offset: number;
127
+ }, {
128
+ score: number;
129
+ strings: {
130
+ string: string;
131
+ encoding: string;
132
+ offset: number;
133
+ categories: string[];
134
+ }[];
135
+ categories: string[];
136
+ start_offset: number;
137
+ end_offset: number;
138
+ }>, "many">>;
139
+ }, "strip", z.ZodTypeAny, {
140
+ cluster_counts: Record<string, number>;
141
+ clusters: Record<string, string[]>;
142
+ top_high_value: {
143
+ string: string;
144
+ encoding: string;
145
+ offset: number;
146
+ categories: string[];
147
+ }[];
148
+ context_windows?: {
149
+ score: number;
150
+ strings: {
151
+ string: string;
152
+ encoding: string;
153
+ offset: number;
154
+ categories: string[];
155
+ }[];
156
+ categories: string[];
157
+ start_offset: number;
158
+ end_offset: number;
159
+ }[] | undefined;
160
+ }, {
161
+ cluster_counts: Record<string, number>;
162
+ clusters: Record<string, string[]>;
163
+ top_high_value: {
164
+ string: string;
165
+ encoding: string;
166
+ offset: number;
167
+ categories: string[];
168
+ }[];
169
+ context_windows?: {
170
+ score: number;
171
+ strings: {
172
+ string: string;
173
+ encoding: string;
174
+ offset: number;
175
+ categories: string[];
176
+ }[];
177
+ categories: string[];
178
+ start_offset: number;
179
+ end_offset: number;
180
+ }[] | undefined;
181
+ }>>;
182
+ }, "strip", z.ZodTypeAny, {
183
+ strings: {
184
+ string: string;
185
+ encoding: string;
186
+ offset: number;
187
+ }[];
188
+ count: number;
189
+ min_len: number;
190
+ encoding_filter: string;
191
+ summary?: {
192
+ cluster_counts: Record<string, number>;
193
+ clusters: Record<string, string[]>;
194
+ top_high_value: {
195
+ string: string;
196
+ encoding: string;
197
+ offset: number;
198
+ categories: string[];
199
+ }[];
200
+ context_windows?: {
201
+ score: number;
202
+ strings: {
203
+ string: string;
204
+ encoding: string;
205
+ offset: number;
206
+ categories: string[];
207
+ }[];
208
+ categories: string[];
209
+ start_offset: number;
210
+ end_offset: number;
211
+ }[] | undefined;
212
+ } | undefined;
213
+ max_strings?: number | undefined;
214
+ max_string_length?: number | undefined;
215
+ category_filter?: string | undefined;
216
+ total_count?: number | undefined;
217
+ pre_filter_count?: number | undefined;
218
+ truncated?: boolean | undefined;
219
+ }, {
220
+ strings: {
221
+ string: string;
222
+ encoding: string;
223
+ offset: number;
224
+ }[];
225
+ count: number;
226
+ min_len: number;
227
+ encoding_filter: string;
228
+ summary?: {
229
+ cluster_counts: Record<string, number>;
230
+ clusters: Record<string, string[]>;
231
+ top_high_value: {
232
+ string: string;
233
+ encoding: string;
234
+ offset: number;
235
+ categories: string[];
236
+ }[];
237
+ context_windows?: {
238
+ score: number;
239
+ strings: {
240
+ string: string;
241
+ encoding: string;
242
+ offset: number;
243
+ categories: string[];
244
+ }[];
245
+ categories: string[];
246
+ start_offset: number;
247
+ end_offset: number;
248
+ }[] | undefined;
249
+ } | undefined;
250
+ max_strings?: number | undefined;
251
+ max_string_length?: number | undefined;
252
+ category_filter?: string | undefined;
253
+ total_count?: number | undefined;
254
+ pre_filter_count?: number | undefined;
255
+ truncated?: boolean | undefined;
256
+ }>>;
257
+ warnings: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
258
+ errors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
259
+ artifacts: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
260
+ metrics: z.ZodOptional<z.ZodObject<{
261
+ elapsed_ms: z.ZodNumber;
262
+ tool: z.ZodString;
263
+ }, "strip", z.ZodTypeAny, {
264
+ elapsed_ms: number;
265
+ tool: string;
266
+ }, {
267
+ elapsed_ms: number;
268
+ tool: string;
269
+ }>>;
270
+ }, "strip", z.ZodTypeAny, {
271
+ ok: boolean;
272
+ metrics?: {
273
+ elapsed_ms: number;
274
+ tool: string;
275
+ } | undefined;
276
+ data?: {
277
+ strings: {
278
+ string: string;
279
+ encoding: string;
280
+ offset: number;
281
+ }[];
282
+ count: number;
283
+ min_len: number;
284
+ encoding_filter: string;
285
+ summary?: {
286
+ cluster_counts: Record<string, number>;
287
+ clusters: Record<string, string[]>;
288
+ top_high_value: {
289
+ string: string;
290
+ encoding: string;
291
+ offset: number;
292
+ categories: string[];
293
+ }[];
294
+ context_windows?: {
295
+ score: number;
296
+ strings: {
297
+ string: string;
298
+ encoding: string;
299
+ offset: number;
300
+ categories: string[];
301
+ }[];
302
+ categories: string[];
303
+ start_offset: number;
304
+ end_offset: number;
305
+ }[] | undefined;
306
+ } | undefined;
307
+ max_strings?: number | undefined;
308
+ max_string_length?: number | undefined;
309
+ category_filter?: string | undefined;
310
+ total_count?: number | undefined;
311
+ pre_filter_count?: number | undefined;
312
+ truncated?: boolean | undefined;
313
+ } | undefined;
314
+ warnings?: string[] | undefined;
315
+ errors?: string[] | undefined;
316
+ artifacts?: any[] | undefined;
317
+ }, {
318
+ ok: boolean;
319
+ metrics?: {
320
+ elapsed_ms: number;
321
+ tool: string;
322
+ } | undefined;
323
+ data?: {
324
+ strings: {
325
+ string: string;
326
+ encoding: string;
327
+ offset: number;
328
+ }[];
329
+ count: number;
330
+ min_len: number;
331
+ encoding_filter: string;
332
+ summary?: {
333
+ cluster_counts: Record<string, number>;
334
+ clusters: Record<string, string[]>;
335
+ top_high_value: {
336
+ string: string;
337
+ encoding: string;
338
+ offset: number;
339
+ categories: string[];
340
+ }[];
341
+ context_windows?: {
342
+ score: number;
343
+ strings: {
344
+ string: string;
345
+ encoding: string;
346
+ offset: number;
347
+ categories: string[];
348
+ }[];
349
+ categories: string[];
350
+ start_offset: number;
351
+ end_offset: number;
352
+ }[] | undefined;
353
+ } | undefined;
354
+ max_strings?: number | undefined;
355
+ max_string_length?: number | undefined;
356
+ category_filter?: string | undefined;
357
+ total_count?: number | undefined;
358
+ pre_filter_count?: number | undefined;
359
+ truncated?: boolean | undefined;
360
+ } | undefined;
361
+ warnings?: string[] | undefined;
362
+ errors?: string[] | undefined;
363
+ artifacts?: any[] | undefined;
364
+ }>;
365
+ export type StringsExtractOutput = z.infer<typeof StringsExtractOutputSchema>;
366
+ /**
367
+ * Tool definition for strings.extract
368
+ */
369
+ export declare const stringsExtractToolDefinition: ToolDefinition;
370
+ /**
371
+ * Create strings.extract tool handler
372
+ * Requirements: 4.1, 4.2, 4.3
373
+ */
374
+ export declare function createStringsExtractHandler(workspaceManager: WorkspaceManager, database: DatabaseManager, cacheManager: CacheManager): (args: ToolArgs) => Promise<WorkerResult>;
375
+ //# sourceMappingURL=strings-extract.d.ts.map
@@ -0,0 +1,314 @@
1
+ /**
2
+ * strings.extract tool implementation
3
+ * Extracts readable strings (ASCII and Unicode) from PE files
4
+ * Requirements: 4.1, 4.2, 4.3
5
+ */
6
+ import { z } from 'zod';
7
+ import { spawn } from 'child_process';
8
+ import path from 'path';
9
+ import { v4 as uuidv4 } from 'uuid';
10
+ import { generateCacheKey } from '../cache-manager.js';
11
+ import { resolvePackagePath } from '../runtime-paths.js';
12
+ import { lookupCachedResult, formatCacheWarning } from './cache-observability.js';
13
+ // ============================================================================
14
+ // Constants
15
+ // ============================================================================
16
+ const TOOL_NAME = 'strings.extract';
17
+ const TOOL_VERSION = '1.0.0';
18
+ const CACHE_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
19
+ // ============================================================================
20
+ // Input/Output Schemas
21
+ // ============================================================================
22
+ /**
23
+ * Input schema for strings.extract tool
24
+ * Requirements: 4.1, 4.2, 4.3
25
+ */
26
+ export const StringsExtractInputSchema = z.object({
27
+ sample_id: z.string().describe('Sample ID (format: sha256:<hex>)'),
28
+ min_len: z.number().int().min(1).optional().default(4).describe('Minimum string length'),
29
+ encoding: z.enum(['ascii', 'unicode', 'all']).optional().default('all').describe('Encoding type to extract'),
30
+ max_strings: z.number().int().min(1).optional().default(500).describe('Maximum number of strings to return (default: 500)'),
31
+ max_string_length: z.number().int().min(16).optional().default(512).describe('Maximum length for each returned string'),
32
+ context_window_bytes: z
33
+ .number()
34
+ .int()
35
+ .min(32)
36
+ .max(65536)
37
+ .optional()
38
+ .default(1024)
39
+ .describe('Maximum byte gap used to regroup nearby strings into context windows'),
40
+ max_context_windows: z
41
+ .number()
42
+ .int()
43
+ .min(1)
44
+ .max(100)
45
+ .optional()
46
+ .default(12)
47
+ .describe('Maximum number of context windows returned in the summary'),
48
+ category_filter: z.enum(['all', 'ioc', 'url', 'network', 'ipc', 'command', 'registry', 'file_path', 'suspicious_api'])
49
+ .optional()
50
+ .default('all')
51
+ .describe('Optional category filter; use `ioc` to prioritize IOC-related strings'),
52
+ force_refresh: z
53
+ .boolean()
54
+ .optional()
55
+ .default(false)
56
+ .describe('Bypass cache lookup and recompute from source sample'),
57
+ });
58
+ /**
59
+ * Output schema for strings.extract tool
60
+ * Requirements: 4.1, 4.2, 4.3, 4.6
61
+ */
62
+ export const StringsExtractOutputSchema = z.object({
63
+ ok: z.boolean(),
64
+ data: z.object({
65
+ strings: z.array(z.object({
66
+ offset: z.number(),
67
+ string: z.string(),
68
+ encoding: z.string(),
69
+ })),
70
+ count: z.number(),
71
+ total_count: z.number().optional(),
72
+ pre_filter_count: z.number().optional(),
73
+ truncated: z.boolean().optional(),
74
+ max_strings: z.number().optional(),
75
+ max_string_length: z.number().optional(),
76
+ min_len: z.number(),
77
+ encoding_filter: z.string(),
78
+ category_filter: z.string().optional(),
79
+ summary: z.object({
80
+ cluster_counts: z.record(z.string(), z.number()),
81
+ clusters: z.record(z.string(), z.array(z.string())),
82
+ top_high_value: z.array(z.object({
83
+ offset: z.number(),
84
+ string: z.string(),
85
+ encoding: z.string(),
86
+ categories: z.array(z.string()),
87
+ })),
88
+ context_windows: z.array(z.object({
89
+ start_offset: z.number(),
90
+ end_offset: z.number(),
91
+ score: z.number(),
92
+ categories: z.array(z.string()),
93
+ strings: z.array(z.object({
94
+ offset: z.number(),
95
+ string: z.string(),
96
+ encoding: z.string(),
97
+ categories: z.array(z.string()),
98
+ })),
99
+ })).optional(),
100
+ }).optional(),
101
+ }).optional(),
102
+ warnings: z.array(z.string()).optional(),
103
+ errors: z.array(z.string()).optional(),
104
+ artifacts: z.array(z.any()).optional(),
105
+ metrics: z.object({
106
+ elapsed_ms: z.number(),
107
+ tool: z.string(),
108
+ }).optional(),
109
+ });
110
+ // ============================================================================
111
+ // Tool Definition
112
+ // ============================================================================
113
+ /**
114
+ * Tool definition for strings.extract
115
+ */
116
+ export const stringsExtractToolDefinition = {
117
+ name: TOOL_NAME,
118
+ description: '提取 PE 文件中的可读字符串(ASCII 和 Unicode),支持多种编码',
119
+ inputSchema: StringsExtractInputSchema,
120
+ outputSchema: StringsExtractOutputSchema,
121
+ };
122
+ /**
123
+ * Spawn Python Static Worker and communicate via stdin/stdout JSON protocol
124
+ *
125
+ * Requirements: Worker communication
126
+ *
127
+ * @param request - Worker request object
128
+ * @returns Worker response object
129
+ */
130
+ async function callStaticWorker(request) {
131
+ return new Promise((resolve, reject) => {
132
+ // Get Python worker path
133
+ const workerPath = resolvePackagePath('workers', 'static_worker.py');
134
+ // Spawn Python process
135
+ const pythonCommand = process.platform === 'win32' ? 'python' : 'python3';
136
+ const pythonProcess = spawn(pythonCommand, [workerPath], {
137
+ stdio: ['pipe', 'pipe', 'pipe'],
138
+ });
139
+ let stdout = '';
140
+ let stderr = '';
141
+ // Collect stdout
142
+ pythonProcess.stdout.on('data', (data) => {
143
+ stdout += data.toString();
144
+ });
145
+ // Collect stderr
146
+ pythonProcess.stderr.on('data', (data) => {
147
+ stderr += data.toString();
148
+ });
149
+ // Handle process exit
150
+ pythonProcess.on('close', (code) => {
151
+ if (code !== 0) {
152
+ reject(new Error(`Python worker exited with code ${code}. stderr: ${stderr}`));
153
+ return;
154
+ }
155
+ // Parse response from stdout
156
+ try {
157
+ const lines = stdout.trim().split('\n');
158
+ const lastLine = lines[lines.length - 1];
159
+ const response = JSON.parse(lastLine);
160
+ resolve(response);
161
+ }
162
+ catch (error) {
163
+ reject(new Error(`Failed to parse worker response: ${error.message}. stdout: ${stdout}`));
164
+ }
165
+ });
166
+ // Handle process error
167
+ pythonProcess.on('error', (error) => {
168
+ reject(new Error(`Failed to spawn Python worker: ${error.message}`));
169
+ });
170
+ // Send request to worker via stdin
171
+ try {
172
+ pythonProcess.stdin.write(JSON.stringify(request) + '\n');
173
+ pythonProcess.stdin.end();
174
+ }
175
+ catch (error) {
176
+ reject(new Error(`Failed to write to worker stdin: ${error.message}`));
177
+ }
178
+ });
179
+ }
180
+ // ============================================================================
181
+ // Tool Handler
182
+ // ============================================================================
183
+ /**
184
+ * Create strings.extract tool handler
185
+ * Requirements: 4.1, 4.2, 4.3
186
+ */
187
+ export function createStringsExtractHandler(workspaceManager, database, cacheManager) {
188
+ return async (args) => {
189
+ const input = args;
190
+ const startTime = Date.now();
191
+ try {
192
+ // 1. Generate cache key
193
+ const sample = database.findSample(input.sample_id);
194
+ if (!sample) {
195
+ return {
196
+ ok: false,
197
+ errors: [`Sample not found: ${input.sample_id}`],
198
+ };
199
+ }
200
+ const cacheKey = generateCacheKey({
201
+ sampleSha256: sample.sha256,
202
+ toolName: TOOL_NAME,
203
+ toolVersion: TOOL_VERSION,
204
+ args: {
205
+ min_len: input.min_len,
206
+ encoding: input.encoding,
207
+ max_strings: input.max_strings,
208
+ max_string_length: input.max_string_length,
209
+ context_window_bytes: input.context_window_bytes,
210
+ max_context_windows: input.max_context_windows,
211
+ category_filter: input.category_filter,
212
+ },
213
+ });
214
+ // 2. Check cache
215
+ if (!input.force_refresh) {
216
+ const cachedLookup = await lookupCachedResult(cacheManager, cacheKey);
217
+ if (cachedLookup) {
218
+ return {
219
+ ok: true,
220
+ data: cachedLookup.data,
221
+ warnings: ['Result from cache', formatCacheWarning(cachedLookup.metadata)],
222
+ metrics: {
223
+ elapsed_ms: Date.now() - startTime,
224
+ tool: TOOL_NAME,
225
+ cached: true,
226
+ cache_key: cachedLookup.metadata.key,
227
+ cache_tier: cachedLookup.metadata.tier,
228
+ cache_created_at: cachedLookup.metadata.createdAt,
229
+ cache_expires_at: cachedLookup.metadata.expiresAt,
230
+ cache_hit_at: cachedLookup.metadata.fetchedAt,
231
+ },
232
+ };
233
+ }
234
+ }
235
+ // 3. Get sample path from workspace
236
+ const workspace = await workspaceManager.getWorkspace(input.sample_id);
237
+ // Find the sample file in the original directory
238
+ const fs = await import('fs/promises');
239
+ const files = await fs.readdir(workspace.original);
240
+ if (files.length === 0) {
241
+ return {
242
+ ok: false,
243
+ errors: ['Sample file not found in workspace'],
244
+ };
245
+ }
246
+ const samplePath = path.join(workspace.original, files[0]);
247
+ // 4. Prepare worker request
248
+ const workerRequest = {
249
+ job_id: uuidv4(),
250
+ tool: TOOL_NAME,
251
+ sample: {
252
+ sample_id: input.sample_id,
253
+ path: samplePath,
254
+ },
255
+ args: {
256
+ min_len: input.min_len,
257
+ encoding: input.encoding,
258
+ max_strings: input.max_strings,
259
+ max_string_length: input.max_string_length,
260
+ context_window_bytes: input.context_window_bytes,
261
+ max_context_windows: input.max_context_windows,
262
+ category_filter: input.category_filter,
263
+ },
264
+ context: {
265
+ request_time_utc: new Date().toISOString(),
266
+ policy: {
267
+ allow_dynamic: false,
268
+ allow_network: false,
269
+ },
270
+ versions: {
271
+ tool_version: TOOL_VERSION,
272
+ },
273
+ },
274
+ };
275
+ // 5. Call Static Worker
276
+ // Requirements: 4.1, 4.2, 4.3
277
+ const workerResponse = await callStaticWorker(workerRequest);
278
+ if (!workerResponse.ok) {
279
+ return {
280
+ ok: false,
281
+ errors: workerResponse.errors,
282
+ warnings: workerResponse.warnings,
283
+ };
284
+ }
285
+ // 6. Cache result
286
+ await cacheManager.setCachedResult(cacheKey, workerResponse.data, CACHE_TTL_MS);
287
+ // 7. Return result
288
+ return {
289
+ ok: true,
290
+ data: workerResponse.data,
291
+ warnings: input.force_refresh
292
+ ? ['force_refresh=true; bypassed cache lookup', ...(workerResponse.warnings || [])]
293
+ : workerResponse.warnings,
294
+ errors: workerResponse.errors,
295
+ artifacts: workerResponse.artifacts,
296
+ metrics: {
297
+ ...workerResponse.metrics,
298
+ elapsed_ms: Date.now() - startTime,
299
+ },
300
+ };
301
+ }
302
+ catch (error) {
303
+ return {
304
+ ok: false,
305
+ errors: [error.message],
306
+ metrics: {
307
+ elapsed_ms: Date.now() - startTime,
308
+ tool: TOOL_NAME,
309
+ },
310
+ };
311
+ }
312
+ };
313
+ }
314
+ //# sourceMappingURL=strings-extract.js.map