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.
- package/CODEX_INSTALLATION.md +69 -0
- package/COPILOT_INSTALLATION.md +77 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/bin/windows-exe-decompiler-mcp-server.js +3 -0
- package/dist/analysis-provenance.d.ts +184 -0
- package/dist/analysis-provenance.js +74 -0
- package/dist/analysis-task-runner.d.ts +31 -0
- package/dist/analysis-task-runner.js +160 -0
- package/dist/artifact-inventory.d.ts +23 -0
- package/dist/artifact-inventory.js +175 -0
- package/dist/cache-manager.d.ts +128 -0
- package/dist/cache-manager.js +454 -0
- package/dist/confidence-semantics.d.ts +66 -0
- package/dist/confidence-semantics.js +122 -0
- package/dist/config.d.ts +335 -0
- package/dist/config.js +193 -0
- package/dist/database.d.ts +227 -0
- package/dist/database.js +601 -0
- package/dist/decompiler-worker.d.ts +441 -0
- package/dist/decompiler-worker.js +1962 -0
- package/dist/dynamic-trace.d.ts +95 -0
- package/dist/dynamic-trace.js +629 -0
- package/dist/env-validator.d.ts +15 -0
- package/dist/env-validator.js +249 -0
- package/dist/error-handler.d.ts +28 -0
- package/dist/error-handler.example.d.ts +22 -0
- package/dist/error-handler.example.js +141 -0
- package/dist/error-handler.js +139 -0
- package/dist/ghidra-analysis-status.d.ts +49 -0
- package/dist/ghidra-analysis-status.js +178 -0
- package/dist/ghidra-config.d.ts +134 -0
- package/dist/ghidra-config.js +464 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +200 -0
- package/dist/job-queue.d.ts +169 -0
- package/dist/job-queue.js +407 -0
- package/dist/logger.d.ts +106 -0
- package/dist/logger.js +176 -0
- package/dist/policy-guard.d.ts +115 -0
- package/dist/policy-guard.js +243 -0
- package/dist/process-output.d.ts +15 -0
- package/dist/process-output.js +90 -0
- package/dist/prompts/function-explanation-review.d.ts +5 -0
- package/dist/prompts/function-explanation-review.js +64 -0
- package/dist/prompts/semantic-name-review.d.ts +5 -0
- package/dist/prompts/semantic-name-review.js +63 -0
- package/dist/runtime-correlation.d.ts +34 -0
- package/dist/runtime-correlation.js +279 -0
- package/dist/runtime-paths.d.ts +3 -0
- package/dist/runtime-paths.js +11 -0
- package/dist/selection-diff.d.ts +667 -0
- package/dist/selection-diff.js +53 -0
- package/dist/semantic-name-suggestion-artifacts.d.ts +116 -0
- package/dist/semantic-name-suggestion-artifacts.js +314 -0
- package/dist/server.d.ts +129 -0
- package/dist/server.js +578 -0
- package/dist/tools/artifact-read.d.ts +235 -0
- package/dist/tools/artifact-read.js +317 -0
- package/dist/tools/artifacts-diff.d.ts +728 -0
- package/dist/tools/artifacts-diff.js +304 -0
- package/dist/tools/artifacts-list.d.ts +515 -0
- package/dist/tools/artifacts-list.js +389 -0
- package/dist/tools/attack-map.d.ts +290 -0
- package/dist/tools/attack-map.js +519 -0
- package/dist/tools/cache-observability.d.ts +4 -0
- package/dist/tools/cache-observability.js +36 -0
- package/dist/tools/code-function-cfg.d.ts +50 -0
- package/dist/tools/code-function-cfg.js +102 -0
- package/dist/tools/code-function-decompile.d.ts +55 -0
- package/dist/tools/code-function-decompile.js +103 -0
- package/dist/tools/code-function-disassemble.d.ts +43 -0
- package/dist/tools/code-function-disassemble.js +185 -0
- package/dist/tools/code-function-explain-apply.d.ts +255 -0
- package/dist/tools/code-function-explain-apply.js +225 -0
- package/dist/tools/code-function-explain-prepare.d.ts +535 -0
- package/dist/tools/code-function-explain-prepare.js +276 -0
- package/dist/tools/code-function-explain-review.d.ts +397 -0
- package/dist/tools/code-function-explain-review.js +589 -0
- package/dist/tools/code-function-rename-apply.d.ts +248 -0
- package/dist/tools/code-function-rename-apply.js +220 -0
- package/dist/tools/code-function-rename-prepare.d.ts +506 -0
- package/dist/tools/code-function-rename-prepare.js +279 -0
- package/dist/tools/code-function-rename-review.d.ts +574 -0
- package/dist/tools/code-function-rename-review.js +761 -0
- package/dist/tools/code-functions-list.d.ts +37 -0
- package/dist/tools/code-functions-list.js +91 -0
- package/dist/tools/code-functions-rank.d.ts +34 -0
- package/dist/tools/code-functions-rank.js +90 -0
- package/dist/tools/code-functions-reconstruct.d.ts +2725 -0
- package/dist/tools/code-functions-reconstruct.js +2807 -0
- package/dist/tools/code-functions-search.d.ts +39 -0
- package/dist/tools/code-functions-search.js +90 -0
- package/dist/tools/code-reconstruct-export.d.ts +1212 -0
- package/dist/tools/code-reconstruct-export.js +4002 -0
- package/dist/tools/code-reconstruct-plan.d.ts +274 -0
- package/dist/tools/code-reconstruct-plan.js +342 -0
- package/dist/tools/dotnet-metadata-extract.d.ts +541 -0
- package/dist/tools/dotnet-metadata-extract.js +355 -0
- package/dist/tools/dotnet-reconstruct-export.d.ts +567 -0
- package/dist/tools/dotnet-reconstruct-export.js +1151 -0
- package/dist/tools/dotnet-types-list.d.ts +325 -0
- package/dist/tools/dotnet-types-list.js +201 -0
- package/dist/tools/dynamic-dependencies.d.ts +115 -0
- package/dist/tools/dynamic-dependencies.js +213 -0
- package/dist/tools/dynamic-memory-import.d.ts +10 -0
- package/dist/tools/dynamic-memory-import.js +567 -0
- package/dist/tools/dynamic-trace-import.d.ts +10 -0
- package/dist/tools/dynamic-trace-import.js +235 -0
- package/dist/tools/entrypoint-fallback-disasm.d.ts +30 -0
- package/dist/tools/entrypoint-fallback-disasm.js +89 -0
- package/dist/tools/ghidra-analyze.d.ts +88 -0
- package/dist/tools/ghidra-analyze.js +208 -0
- package/dist/tools/ghidra-health.d.ts +37 -0
- package/dist/tools/ghidra-health.js +212 -0
- package/dist/tools/ioc-export.d.ts +209 -0
- package/dist/tools/ioc-export.js +542 -0
- package/dist/tools/packer-detect.d.ts +165 -0
- package/dist/tools/packer-detect.js +284 -0
- package/dist/tools/pe-exports-extract.d.ts +175 -0
- package/dist/tools/pe-exports-extract.js +253 -0
- package/dist/tools/pe-fingerprint.d.ts +234 -0
- package/dist/tools/pe-fingerprint.js +269 -0
- package/dist/tools/pe-imports-extract.d.ts +105 -0
- package/dist/tools/pe-imports-extract.js +245 -0
- package/dist/tools/report-generate.d.ts +157 -0
- package/dist/tools/report-generate.js +457 -0
- package/dist/tools/report-summarize.d.ts +2131 -0
- package/dist/tools/report-summarize.js +596 -0
- package/dist/tools/runtime-detect.d.ts +135 -0
- package/dist/tools/runtime-detect.js +247 -0
- package/dist/tools/sample-ingest.d.ts +94 -0
- package/dist/tools/sample-ingest.js +327 -0
- package/dist/tools/sample-profile-get.d.ts +183 -0
- package/dist/tools/sample-profile-get.js +121 -0
- package/dist/tools/sandbox-execute.d.ts +441 -0
- package/dist/tools/sandbox-execute.js +392 -0
- package/dist/tools/strings-extract.d.ts +375 -0
- package/dist/tools/strings-extract.js +314 -0
- package/dist/tools/strings-floss-decode.d.ts +143 -0
- package/dist/tools/strings-floss-decode.js +259 -0
- package/dist/tools/system-health.d.ts +434 -0
- package/dist/tools/system-health.js +446 -0
- package/dist/tools/task-cancel.d.ts +21 -0
- package/dist/tools/task-cancel.js +70 -0
- package/dist/tools/task-status.d.ts +27 -0
- package/dist/tools/task-status.js +106 -0
- package/dist/tools/task-sweep.d.ts +22 -0
- package/dist/tools/task-sweep.js +77 -0
- package/dist/tools/tool-help.d.ts +340 -0
- package/dist/tools/tool-help.js +261 -0
- package/dist/tools/yara-scan.d.ts +554 -0
- package/dist/tools/yara-scan.js +313 -0
- package/dist/types.d.ts +266 -0
- package/dist/types.js +41 -0
- package/dist/worker-pool.d.ts +204 -0
- package/dist/worker-pool.js +650 -0
- package/dist/workflows/deep-static.d.ts +104 -0
- package/dist/workflows/deep-static.js +276 -0
- package/dist/workflows/function-explanation-review.d.ts +655 -0
- package/dist/workflows/function-explanation-review.js +440 -0
- package/dist/workflows/reconstruct.d.ts +2053 -0
- package/dist/workflows/reconstruct.js +666 -0
- package/dist/workflows/semantic-name-review.d.ts +2418 -0
- package/dist/workflows/semantic-name-review.js +521 -0
- package/dist/workflows/triage.d.ts +659 -0
- package/dist/workflows/triage.js +1374 -0
- package/dist/workspace-manager.d.ts +150 -0
- package/dist/workspace-manager.js +411 -0
- package/ghidra_scripts/DecompileFunction.java +487 -0
- package/ghidra_scripts/DecompileFunction.py +150 -0
- package/ghidra_scripts/ExtractCFG.java +256 -0
- package/ghidra_scripts/ExtractCFG.py +233 -0
- package/ghidra_scripts/ExtractFunctions.java +442 -0
- package/ghidra_scripts/ExtractFunctions.py +101 -0
- package/ghidra_scripts/README.md +125 -0
- package/ghidra_scripts/SearchFunctionReferences.java +380 -0
- package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -0
- package/helpers/DotNetMetadataProbe/Program.cs +566 -0
- package/install-to-codex.ps1 +178 -0
- package/install-to-copilot.ps1 +303 -0
- package/package.json +101 -0
- package/requirements.txt +9 -0
- package/workers/requirements-dynamic.txt +11 -0
- package/workers/requirements.txt +8 -0
- package/workers/speakeasy_compat.py +175 -0
- package/workers/static_worker.py +5183 -0
- package/workers/yara_rules/default.yar +33 -0
- package/workers/yara_rules/malware_families.yar +93 -0
- package/workers/yara_rules/packers.yar +80 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment validation for MCP Server
|
|
3
|
+
* Validates that required dependencies and paths exist
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
/**
|
|
9
|
+
* Check if a file or directory exists
|
|
10
|
+
*/
|
|
11
|
+
function pathExists(path) {
|
|
12
|
+
try {
|
|
13
|
+
fs.accessSync(path, fs.constants.F_OK);
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Check if a command is available in PATH
|
|
22
|
+
*/
|
|
23
|
+
function commandExists(command) {
|
|
24
|
+
try {
|
|
25
|
+
execSync(`which ${command}`, { stdio: 'ignore' });
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validate Node.js version
|
|
34
|
+
*/
|
|
35
|
+
function validateNodeVersion() {
|
|
36
|
+
const version = process.version;
|
|
37
|
+
const major = parseInt(version.slice(1).split('.')[0], 10);
|
|
38
|
+
if (major < 18) {
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
error: `Node.js version ${version} is not supported. Minimum required version is 18.0.0`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { valid: true };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Validate workspace directory
|
|
48
|
+
*/
|
|
49
|
+
function validateWorkspace(workspaceRoot) {
|
|
50
|
+
if (!pathExists(workspaceRoot)) {
|
|
51
|
+
try {
|
|
52
|
+
fs.mkdirSync(workspaceRoot, { recursive: true });
|
|
53
|
+
return { valid: true, warning: `Created workspace directory: ${workspaceRoot}` };
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
return {
|
|
57
|
+
valid: false,
|
|
58
|
+
error: `Failed to create workspace directory ${workspaceRoot}: ${error.message}`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Check if directory is writable
|
|
63
|
+
try {
|
|
64
|
+
fs.accessSync(workspaceRoot, fs.constants.W_OK);
|
|
65
|
+
return { valid: true };
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return {
|
|
69
|
+
valid: false,
|
|
70
|
+
error: `Workspace directory ${workspaceRoot} is not writable`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Validate database configuration
|
|
76
|
+
*/
|
|
77
|
+
function validateDatabase(dbConfig) {
|
|
78
|
+
if (dbConfig.type === 'sqlite') {
|
|
79
|
+
if (dbConfig.path) {
|
|
80
|
+
const dir = path.dirname(dbConfig.path);
|
|
81
|
+
if (dir && dir !== '.' && !pathExists(dir)) {
|
|
82
|
+
try {
|
|
83
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
84
|
+
return { valid: true, warning: `Created database directory: ${dir}` };
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
return {
|
|
88
|
+
valid: false,
|
|
89
|
+
error: `Failed to create database directory ${dir}: ${error.message}`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { valid: true };
|
|
95
|
+
}
|
|
96
|
+
if (dbConfig.type === 'postgresql') {
|
|
97
|
+
if (!dbConfig.host || !dbConfig.database) {
|
|
98
|
+
return {
|
|
99
|
+
valid: false,
|
|
100
|
+
error: 'PostgreSQL configuration requires host and database name',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return { valid: true };
|
|
104
|
+
}
|
|
105
|
+
return { valid: true };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Validate Ghidra worker configuration
|
|
109
|
+
*/
|
|
110
|
+
function validateGhidraWorker(ghidraConfig) {
|
|
111
|
+
if (!ghidraConfig.enabled) {
|
|
112
|
+
return { valid: true };
|
|
113
|
+
}
|
|
114
|
+
if (!ghidraConfig.path) {
|
|
115
|
+
return {
|
|
116
|
+
valid: false,
|
|
117
|
+
error: 'Ghidra worker is enabled but GHIDRA_PATH is not configured',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (!pathExists(ghidraConfig.path)) {
|
|
121
|
+
return {
|
|
122
|
+
valid: false,
|
|
123
|
+
error: `Ghidra path does not exist: ${ghidraConfig.path}`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// Check for analyzeHeadless script
|
|
127
|
+
const analyzeHeadless = `${ghidraConfig.path}/support/analyzeHeadless`;
|
|
128
|
+
if (!pathExists(analyzeHeadless) && !pathExists(`${analyzeHeadless}.bat`)) {
|
|
129
|
+
return {
|
|
130
|
+
valid: false,
|
|
131
|
+
error: `Ghidra analyzeHeadless script not found at ${analyzeHeadless}`,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return { valid: true };
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Validate Python worker configuration
|
|
138
|
+
*/
|
|
139
|
+
function validatePythonWorker(staticConfig) {
|
|
140
|
+
if (!staticConfig.enabled) {
|
|
141
|
+
return { valid: true };
|
|
142
|
+
}
|
|
143
|
+
const pythonCmd = staticConfig.pythonPath || 'python3';
|
|
144
|
+
if (!commandExists(pythonCmd)) {
|
|
145
|
+
return {
|
|
146
|
+
valid: false,
|
|
147
|
+
error: `Python command not found: ${pythonCmd}`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// Check Python version
|
|
151
|
+
try {
|
|
152
|
+
const version = execSync(`${pythonCmd} --version`, { encoding: 'utf-8' });
|
|
153
|
+
const match = version.match(/Python (\d+)\.(\d+)/);
|
|
154
|
+
if (match) {
|
|
155
|
+
const major = parseInt(match[1], 10);
|
|
156
|
+
const minor = parseInt(match[2], 10);
|
|
157
|
+
if (major < 3 || (major === 3 && minor < 9)) {
|
|
158
|
+
return {
|
|
159
|
+
valid: false,
|
|
160
|
+
error: `Python version ${version.trim()} is not supported. Minimum required version is 3.9`,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
return {
|
|
167
|
+
valid: false,
|
|
168
|
+
error: `Failed to check Python version: ${error.message}`,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return { valid: true };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Validate .NET worker configuration
|
|
175
|
+
*/
|
|
176
|
+
function validateDotNetWorker(dotnetConfig) {
|
|
177
|
+
if (!dotnetConfig.enabled) {
|
|
178
|
+
return { valid: true };
|
|
179
|
+
}
|
|
180
|
+
if (!dotnetConfig.ilspyPath) {
|
|
181
|
+
return {
|
|
182
|
+
valid: false,
|
|
183
|
+
error: '.NET worker is enabled but ilspyPath is not configured',
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
if (!pathExists(dotnetConfig.ilspyPath)) {
|
|
187
|
+
return {
|
|
188
|
+
valid: false,
|
|
189
|
+
error: `ILSpy path does not exist: ${dotnetConfig.ilspyPath}`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return { valid: true };
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Validate the entire environment based on configuration
|
|
196
|
+
*/
|
|
197
|
+
export function validateEnvironment(config) {
|
|
198
|
+
const errors = [];
|
|
199
|
+
const warnings = [];
|
|
200
|
+
// Validate Node.js version
|
|
201
|
+
const nodeValidation = validateNodeVersion();
|
|
202
|
+
if (!nodeValidation.valid && nodeValidation.error) {
|
|
203
|
+
errors.push(nodeValidation.error);
|
|
204
|
+
}
|
|
205
|
+
// Validate workspace
|
|
206
|
+
const workspaceValidation = validateWorkspace(config.workspace.root);
|
|
207
|
+
if (!workspaceValidation.valid && workspaceValidation.error) {
|
|
208
|
+
errors.push(workspaceValidation.error);
|
|
209
|
+
}
|
|
210
|
+
if (workspaceValidation.warning) {
|
|
211
|
+
warnings.push(workspaceValidation.warning);
|
|
212
|
+
}
|
|
213
|
+
// Validate database
|
|
214
|
+
const dbValidation = validateDatabase(config.database);
|
|
215
|
+
if (!dbValidation.valid && dbValidation.error) {
|
|
216
|
+
errors.push(dbValidation.error);
|
|
217
|
+
}
|
|
218
|
+
if (dbValidation.warning) {
|
|
219
|
+
warnings.push(dbValidation.warning);
|
|
220
|
+
}
|
|
221
|
+
// Validate workers
|
|
222
|
+
const ghidraValidation = validateGhidraWorker(config.workers.ghidra);
|
|
223
|
+
if (!ghidraValidation.valid && ghidraValidation.error) {
|
|
224
|
+
errors.push(ghidraValidation.error);
|
|
225
|
+
}
|
|
226
|
+
if (ghidraValidation.warning) {
|
|
227
|
+
warnings.push(ghidraValidation.warning);
|
|
228
|
+
}
|
|
229
|
+
const pythonValidation = validatePythonWorker(config.workers.static);
|
|
230
|
+
if (!pythonValidation.valid && pythonValidation.error) {
|
|
231
|
+
errors.push(pythonValidation.error);
|
|
232
|
+
}
|
|
233
|
+
if (pythonValidation.warning) {
|
|
234
|
+
warnings.push(pythonValidation.warning);
|
|
235
|
+
}
|
|
236
|
+
const dotnetValidation = validateDotNetWorker(config.workers.dotnet);
|
|
237
|
+
if (!dotnetValidation.valid && dotnetValidation.error) {
|
|
238
|
+
errors.push(dotnetValidation.error);
|
|
239
|
+
}
|
|
240
|
+
if (dotnetValidation.warning) {
|
|
241
|
+
warnings.push(dotnetValidation.warning);
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
valid: errors.length === 0,
|
|
245
|
+
errors,
|
|
246
|
+
warnings,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=env-validator.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling and classification module
|
|
3
|
+
* Requirements: 22.1, 22.2, 22.3, 22.4, 22.5, 22.6
|
|
4
|
+
*/
|
|
5
|
+
import { ErrorCategory, ErrorContext, ErrorResult } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Classify an error into a category
|
|
8
|
+
* Requirements: 22.1, 22.2, 22.3
|
|
9
|
+
*/
|
|
10
|
+
export declare function classifyError(error: Error): ErrorCategory;
|
|
11
|
+
/**
|
|
12
|
+
* Calculate exponential backoff delay
|
|
13
|
+
* Requirements: 22.4
|
|
14
|
+
*
|
|
15
|
+
* Formula: min(baseMs * 2^attempt, maxMs) + jitter
|
|
16
|
+
*/
|
|
17
|
+
export declare function exponentialBackoff(attempt: number, baseMs?: number, maxMs?: number): number;
|
|
18
|
+
/**
|
|
19
|
+
* Handle an error and determine retry strategy
|
|
20
|
+
* Requirements: 22.1, 22.2, 22.3, 22.4, 22.5, 22.6
|
|
21
|
+
*/
|
|
22
|
+
export declare function handleError(error: Error, context: ErrorContext): ErrorResult;
|
|
23
|
+
/**
|
|
24
|
+
* Create a standardized error message
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatErrorMessage(category: ErrorCategory, originalError: Error, context: ErrorContext): string;
|
|
27
|
+
export { ErrorCategory, ErrorContext, ErrorResult } from './types.js';
|
|
28
|
+
//# sourceMappingURL=error-handler.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example usage of the error handler module
|
|
3
|
+
* This file demonstrates how to integrate error handling into tools and workers
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Example: Retry logic for a tool with exponential backoff
|
|
7
|
+
*/
|
|
8
|
+
declare function executeToolWithRetry<T>(toolName: string, sampleId: string, operation: () => Promise<T>, maxRetries?: number): Promise<T>;
|
|
9
|
+
/**
|
|
10
|
+
* Example: PE fingerprint tool with fallback parser
|
|
11
|
+
*/
|
|
12
|
+
declare function peFingerprint(sampleId: string, useLief?: boolean): Promise<any>;
|
|
13
|
+
/**
|
|
14
|
+
* Example: Ghidra analysis with timeout handling
|
|
15
|
+
*/
|
|
16
|
+
declare function ghidraAnalyze(sampleId: string): Promise<any>;
|
|
17
|
+
/**
|
|
18
|
+
* Example: Policy-denied error (non-retryable)
|
|
19
|
+
*/
|
|
20
|
+
declare function sandboxExecute(sampleId: string, requireApproval: boolean): Promise<any>;
|
|
21
|
+
export { executeToolWithRetry, peFingerprint, ghidraAnalyze, sandboxExecute };
|
|
22
|
+
//# sourceMappingURL=error-handler.example.d.ts.map
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example usage of the error handler module
|
|
3
|
+
* This file demonstrates how to integrate error handling into tools and workers
|
|
4
|
+
*/
|
|
5
|
+
import { handleError, classifyError, formatErrorMessage, ErrorCategory } from './error-handler.js';
|
|
6
|
+
/**
|
|
7
|
+
* Example: Retry logic for a tool with exponential backoff
|
|
8
|
+
*/
|
|
9
|
+
async function executeToolWithRetry(toolName, sampleId, operation, maxRetries = 3) {
|
|
10
|
+
let lastError = null;
|
|
11
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
12
|
+
try {
|
|
13
|
+
return await operation();
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
lastError = error;
|
|
17
|
+
const context = {
|
|
18
|
+
tool: toolName,
|
|
19
|
+
sampleId,
|
|
20
|
+
attempt,
|
|
21
|
+
maxRetries
|
|
22
|
+
};
|
|
23
|
+
const result = handleError(lastError, context);
|
|
24
|
+
// Log the error with formatted message
|
|
25
|
+
const category = classifyError(lastError);
|
|
26
|
+
const message = formatErrorMessage(category, lastError, context);
|
|
27
|
+
console.error(message);
|
|
28
|
+
if (!result.shouldRetry) {
|
|
29
|
+
// Handle fallback actions
|
|
30
|
+
if (result.fallbackAction === 'use_lief_instead_of_pefile') {
|
|
31
|
+
console.log('Attempting fallback parser...');
|
|
32
|
+
// Implement fallback logic here
|
|
33
|
+
}
|
|
34
|
+
else if (result.fallbackAction === 'log_warning') {
|
|
35
|
+
console.warn('Partial success, continuing...');
|
|
36
|
+
}
|
|
37
|
+
else if (result.fallbackAction === 'notify_admin') {
|
|
38
|
+
console.error('Max retries exceeded, admin notification required');
|
|
39
|
+
}
|
|
40
|
+
throw lastError;
|
|
41
|
+
}
|
|
42
|
+
// Wait before retrying
|
|
43
|
+
if (result.backoffMs) {
|
|
44
|
+
console.log(`Retrying in ${result.backoffMs}ms...`);
|
|
45
|
+
await sleep(result.backoffMs);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
throw lastError;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Example: PE fingerprint tool with fallback parser
|
|
53
|
+
*/
|
|
54
|
+
async function peFingerprint(sampleId, useLief = false) {
|
|
55
|
+
const context = {
|
|
56
|
+
tool: 'pe.fingerprint',
|
|
57
|
+
sampleId,
|
|
58
|
+
attempt: 0,
|
|
59
|
+
maxRetries: 1
|
|
60
|
+
};
|
|
61
|
+
try {
|
|
62
|
+
if (useLief) {
|
|
63
|
+
// Use LIEF parser
|
|
64
|
+
return await parsePEWithLief(sampleId);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Use pefile parser
|
|
68
|
+
return await parsePEWithPefile(sampleId);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const result = handleError(error, context);
|
|
73
|
+
if (result.fallbackAction === 'use_lief_instead_of_pefile') {
|
|
74
|
+
console.log('Primary parser failed, trying LIEF...');
|
|
75
|
+
return await peFingerprint(sampleId, true);
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Example: Ghidra analysis with timeout handling
|
|
82
|
+
*/
|
|
83
|
+
async function ghidraAnalyze(sampleId) {
|
|
84
|
+
return executeToolWithRetry('ghidra.analyze', sampleId, async () => {
|
|
85
|
+
// Simulate Ghidra analysis
|
|
86
|
+
const result = await runGhidraHeadless(sampleId);
|
|
87
|
+
return result;
|
|
88
|
+
}, 3 // Max 3 retries for timeout errors
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Example: Policy-denied error (non-retryable)
|
|
93
|
+
*/
|
|
94
|
+
async function sandboxExecute(sampleId, requireApproval) {
|
|
95
|
+
const context = {
|
|
96
|
+
tool: 'sandbox.execute',
|
|
97
|
+
sampleId,
|
|
98
|
+
attempt: 0,
|
|
99
|
+
maxRetries: 0 // No retries for policy errors
|
|
100
|
+
};
|
|
101
|
+
try {
|
|
102
|
+
if (!requireApproval) {
|
|
103
|
+
throw new Error('Policy denied: approval required for dynamic execution');
|
|
104
|
+
}
|
|
105
|
+
return await executeSandbox(sampleId);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
const result = handleError(error, context);
|
|
109
|
+
if (!result.shouldRetry) {
|
|
110
|
+
const category = classifyError(error);
|
|
111
|
+
if (category === ErrorCategory.POLICY_DENIED) {
|
|
112
|
+
console.error('Operation denied by policy guard');
|
|
113
|
+
// Log to audit log
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Helper functions (stubs for demonstration)
|
|
120
|
+
async function sleep(ms) {
|
|
121
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
122
|
+
}
|
|
123
|
+
async function parsePEWithPefile(_sampleId) {
|
|
124
|
+
// Stub implementation
|
|
125
|
+
throw new Error('Parse error: malformed PE header');
|
|
126
|
+
}
|
|
127
|
+
async function parsePEWithLief(_sampleId) {
|
|
128
|
+
// Stub implementation
|
|
129
|
+
return { success: true, parser: 'lief' };
|
|
130
|
+
}
|
|
131
|
+
async function runGhidraHeadless(_sampleId) {
|
|
132
|
+
// Stub implementation
|
|
133
|
+
return { functions: [] };
|
|
134
|
+
}
|
|
135
|
+
async function executeSandbox(_sampleId) {
|
|
136
|
+
// Stub implementation
|
|
137
|
+
return { traces: [] };
|
|
138
|
+
}
|
|
139
|
+
// Export examples for documentation
|
|
140
|
+
export { executeToolWithRetry, peFingerprint, ghidraAnalyze, sandboxExecute };
|
|
141
|
+
//# sourceMappingURL=error-handler.example.js.map
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling and classification module
|
|
3
|
+
* Requirements: 22.1, 22.2, 22.3, 22.4, 22.5, 22.6
|
|
4
|
+
*/
|
|
5
|
+
import { ErrorCategory } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Classify an error into a category
|
|
8
|
+
* Requirements: 22.1, 22.2, 22.3
|
|
9
|
+
*/
|
|
10
|
+
export function classifyError(error) {
|
|
11
|
+
const message = error.message.toLowerCase();
|
|
12
|
+
// Timeout errors
|
|
13
|
+
if (message.includes('timeout') || message.includes('timed out')) {
|
|
14
|
+
return ErrorCategory.TIMEOUT;
|
|
15
|
+
}
|
|
16
|
+
// Resource exhaustion
|
|
17
|
+
if (message.includes('out of memory') ||
|
|
18
|
+
message.includes('resource exhausted') ||
|
|
19
|
+
message.includes('enomem') ||
|
|
20
|
+
message.includes('enospc')) {
|
|
21
|
+
return ErrorCategory.RESOURCE_EXHAUSTED;
|
|
22
|
+
}
|
|
23
|
+
// Worker unavailable
|
|
24
|
+
if (message.includes('worker unavailable') ||
|
|
25
|
+
message.includes('connection refused') ||
|
|
26
|
+
message.includes('econnrefused')) {
|
|
27
|
+
return ErrorCategory.WORKER_UNAVAILABLE;
|
|
28
|
+
}
|
|
29
|
+
// Invalid input
|
|
30
|
+
if (message.includes('invalid input') ||
|
|
31
|
+
message.includes('validation failed') ||
|
|
32
|
+
message.includes('invalid argument')) {
|
|
33
|
+
return ErrorCategory.INVALID_INPUT;
|
|
34
|
+
}
|
|
35
|
+
// Parse errors
|
|
36
|
+
if (message.includes('parse error') ||
|
|
37
|
+
message.includes('malformed') ||
|
|
38
|
+
message.includes('invalid pe')) {
|
|
39
|
+
return ErrorCategory.PARSE_ERROR;
|
|
40
|
+
}
|
|
41
|
+
// Policy denied
|
|
42
|
+
if (message.includes('policy denied') ||
|
|
43
|
+
message.includes('permission denied') ||
|
|
44
|
+
message.includes('unauthorized')) {
|
|
45
|
+
return ErrorCategory.POLICY_DENIED;
|
|
46
|
+
}
|
|
47
|
+
// Not found
|
|
48
|
+
if (message.includes('not found') || message.includes('enoent')) {
|
|
49
|
+
return ErrorCategory.NOT_FOUND;
|
|
50
|
+
}
|
|
51
|
+
// Partial success
|
|
52
|
+
if (message.includes('partial')) {
|
|
53
|
+
return ErrorCategory.PARTIAL_SUCCESS;
|
|
54
|
+
}
|
|
55
|
+
return ErrorCategory.UNKNOWN;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Calculate exponential backoff delay
|
|
59
|
+
* Requirements: 22.4
|
|
60
|
+
*
|
|
61
|
+
* Formula: min(baseMs * 2^attempt, maxMs) + jitter
|
|
62
|
+
*/
|
|
63
|
+
export function exponentialBackoff(attempt, baseMs = 1000, maxMs = 30000) {
|
|
64
|
+
const exponentialDelay = Math.min(baseMs * Math.pow(2, attempt), maxMs);
|
|
65
|
+
// Add jitter (0-20% of delay) to prevent thundering herd
|
|
66
|
+
const jitter = Math.random() * 0.2 * exponentialDelay;
|
|
67
|
+
return Math.floor(exponentialDelay + jitter);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Handle an error and determine retry strategy
|
|
71
|
+
* Requirements: 22.1, 22.2, 22.3, 22.4, 22.5, 22.6
|
|
72
|
+
*/
|
|
73
|
+
export function handleError(error, context) {
|
|
74
|
+
const category = classifyError(error);
|
|
75
|
+
switch (category) {
|
|
76
|
+
case ErrorCategory.TIMEOUT:
|
|
77
|
+
case ErrorCategory.RESOURCE_EXHAUSTED:
|
|
78
|
+
case ErrorCategory.WORKER_UNAVAILABLE:
|
|
79
|
+
// Retryable errors with exponential backoff
|
|
80
|
+
if (context.attempt < context.maxRetries) {
|
|
81
|
+
return {
|
|
82
|
+
shouldRetry: true,
|
|
83
|
+
backoffMs: exponentialBackoff(context.attempt)
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
return {
|
|
88
|
+
shouldRetry: false,
|
|
89
|
+
fallbackAction: 'notify_admin'
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
case ErrorCategory.INVALID_INPUT:
|
|
93
|
+
case ErrorCategory.POLICY_DENIED:
|
|
94
|
+
// Non-retryable errors - log and fail immediately
|
|
95
|
+
return {
|
|
96
|
+
shouldRetry: false
|
|
97
|
+
};
|
|
98
|
+
case ErrorCategory.PARSE_ERROR:
|
|
99
|
+
// Try fallback parser for PE parsing errors
|
|
100
|
+
if (context.tool === 'pe.fingerprint') {
|
|
101
|
+
return {
|
|
102
|
+
shouldRetry: true,
|
|
103
|
+
fallbackAction: 'use_lief_instead_of_pefile'
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return {
|
|
108
|
+
shouldRetry: false
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
case ErrorCategory.PARTIAL_SUCCESS:
|
|
112
|
+
// Log warning but don't retry
|
|
113
|
+
return {
|
|
114
|
+
shouldRetry: false,
|
|
115
|
+
fallbackAction: 'log_warning'
|
|
116
|
+
};
|
|
117
|
+
case ErrorCategory.NOT_FOUND:
|
|
118
|
+
// Don't retry for not found errors
|
|
119
|
+
return {
|
|
120
|
+
shouldRetry: false
|
|
121
|
+
};
|
|
122
|
+
case ErrorCategory.UNKNOWN:
|
|
123
|
+
default:
|
|
124
|
+
// Unknown errors - don't retry by default
|
|
125
|
+
return {
|
|
126
|
+
shouldRetry: false,
|
|
127
|
+
fallbackAction: 'log_error'
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Create a standardized error message
|
|
133
|
+
*/
|
|
134
|
+
export function formatErrorMessage(category, originalError, context) {
|
|
135
|
+
return `[${category}] ${context.tool} failed for sample ${context.sampleId} (attempt ${context.attempt}/${context.maxRetries}): ${originalError.message}`;
|
|
136
|
+
}
|
|
137
|
+
// Re-export types for convenience
|
|
138
|
+
export { ErrorCategory } from './types.js';
|
|
139
|
+
//# sourceMappingURL=error-handler.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for interpreting persisted Ghidra analysis states.
|
|
3
|
+
*/
|
|
4
|
+
import type { Analysis } from './database.js';
|
|
5
|
+
export declare const GHIDRA_READY_STATUSES: Set<string>;
|
|
6
|
+
export declare const GHIDRA_STRICT_READY_STATUSES: Set<string>;
|
|
7
|
+
export type GhidraCapability = 'function_index' | 'decompile' | 'cfg';
|
|
8
|
+
export type GhidraCapabilityState = 'ready' | 'degraded' | 'missing';
|
|
9
|
+
export interface GhidraCapabilityStatus {
|
|
10
|
+
available: boolean;
|
|
11
|
+
status: GhidraCapabilityState;
|
|
12
|
+
reason?: string;
|
|
13
|
+
warnings?: string[];
|
|
14
|
+
checked_at?: string;
|
|
15
|
+
target?: string;
|
|
16
|
+
details?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
export interface GhidraAnalysisMetadata {
|
|
19
|
+
function_count?: number;
|
|
20
|
+
project_path?: string;
|
|
21
|
+
project_key?: string;
|
|
22
|
+
function_extraction?: {
|
|
23
|
+
status?: string;
|
|
24
|
+
script_used?: string;
|
|
25
|
+
warnings?: string[];
|
|
26
|
+
attempts?: unknown[];
|
|
27
|
+
};
|
|
28
|
+
readiness?: Partial<Record<GhidraCapability, GhidraCapabilityStatus>>;
|
|
29
|
+
end_to_end_probe?: {
|
|
30
|
+
target?: string;
|
|
31
|
+
decompile?: GhidraCapabilityStatus;
|
|
32
|
+
cfg?: GhidraCapabilityStatus;
|
|
33
|
+
checked_at?: string;
|
|
34
|
+
};
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
export interface GhidraReadinessMatrix {
|
|
38
|
+
function_index: GhidraCapabilityStatus;
|
|
39
|
+
decompile: GhidraCapabilityStatus;
|
|
40
|
+
cfg: GhidraCapabilityStatus;
|
|
41
|
+
}
|
|
42
|
+
export declare function parseGhidraAnalysisMetadata(outputJson: string | null | undefined): GhidraAnalysisMetadata;
|
|
43
|
+
export declare function isGhidraReadyStatus(status: string | null | undefined): boolean;
|
|
44
|
+
export declare function isStrictGhidraReadyStatus(status: string | null | undefined): boolean;
|
|
45
|
+
export declare function getGhidraReadiness(analysis: Pick<Analysis, 'status' | 'output_json'>): GhidraReadinessMatrix;
|
|
46
|
+
export declare function getGhidraCapabilityStatus(analysis: Pick<Analysis, 'status' | 'output_json'>, capability: GhidraCapability): GhidraCapabilityStatus;
|
|
47
|
+
export declare function isGhidraCapabilityReady(analysis: Pick<Analysis, 'status' | 'output_json'>, capability: GhidraCapability): boolean;
|
|
48
|
+
export declare function findBestGhidraAnalysis(analyses: Analysis[], capability?: GhidraCapability): Analysis | undefined;
|
|
49
|
+
//# sourceMappingURL=ghidra-analysis-status.d.ts.map
|