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,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* report.generate MCP Tool
|
|
3
|
+
*
|
|
4
|
+
* Requirements: 24.1, 24.3, 24.5, 24.6
|
|
5
|
+
*
|
|
6
|
+
* Generates comprehensive Markdown analysis report
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import type { ToolDefinition, ToolHandler } from '../types.js';
|
|
10
|
+
import type { DatabaseManager } from '../database.js';
|
|
11
|
+
import type { WorkspaceManager } from '../workspace-manager.js';
|
|
12
|
+
/**
|
|
13
|
+
* Input schema for report.generate tool
|
|
14
|
+
*/
|
|
15
|
+
export declare const reportGenerateInputSchema: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodObject<{
|
|
16
|
+
sample_id: z.ZodString;
|
|
17
|
+
format: z.ZodOptional<z.ZodEnum<["markdown", "json", "html"]>>;
|
|
18
|
+
include_sections: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
19
|
+
evidence_scope: z.ZodDefault<z.ZodOptional<z.ZodEnum<["all", "latest", "session"]>>>;
|
|
20
|
+
evidence_session_tag: z.ZodOptional<z.ZodString>;
|
|
21
|
+
semantic_scope: z.ZodDefault<z.ZodOptional<z.ZodEnum<["all", "latest", "session"]>>>;
|
|
22
|
+
semantic_session_tag: z.ZodOptional<z.ZodString>;
|
|
23
|
+
compare_evidence_scope: z.ZodOptional<z.ZodEnum<["all", "latest", "session"]>>;
|
|
24
|
+
compare_evidence_session_tag: z.ZodOptional<z.ZodString>;
|
|
25
|
+
compare_semantic_scope: z.ZodOptional<z.ZodEnum<["all", "latest", "session"]>>;
|
|
26
|
+
compare_semantic_session_tag: z.ZodOptional<z.ZodString>;
|
|
27
|
+
}, "strip", z.ZodTypeAny, {
|
|
28
|
+
sample_id: string;
|
|
29
|
+
evidence_scope: "all" | "latest" | "session";
|
|
30
|
+
semantic_scope: "all" | "latest" | "session";
|
|
31
|
+
evidence_session_tag?: string | undefined;
|
|
32
|
+
semantic_session_tag?: string | undefined;
|
|
33
|
+
compare_evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
34
|
+
compare_evidence_session_tag?: string | undefined;
|
|
35
|
+
compare_semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
36
|
+
compare_semantic_session_tag?: string | undefined;
|
|
37
|
+
format?: "markdown" | "json" | "html" | undefined;
|
|
38
|
+
include_sections?: string[] | undefined;
|
|
39
|
+
}, {
|
|
40
|
+
sample_id: string;
|
|
41
|
+
evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
42
|
+
evidence_session_tag?: string | undefined;
|
|
43
|
+
semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
44
|
+
semantic_session_tag?: string | undefined;
|
|
45
|
+
compare_evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
46
|
+
compare_evidence_session_tag?: string | undefined;
|
|
47
|
+
compare_semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
48
|
+
compare_semantic_session_tag?: string | undefined;
|
|
49
|
+
format?: "markdown" | "json" | "html" | undefined;
|
|
50
|
+
include_sections?: string[] | undefined;
|
|
51
|
+
}>, {
|
|
52
|
+
sample_id: string;
|
|
53
|
+
evidence_scope: "all" | "latest" | "session";
|
|
54
|
+
semantic_scope: "all" | "latest" | "session";
|
|
55
|
+
evidence_session_tag?: string | undefined;
|
|
56
|
+
semantic_session_tag?: string | undefined;
|
|
57
|
+
compare_evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
58
|
+
compare_evidence_session_tag?: string | undefined;
|
|
59
|
+
compare_semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
60
|
+
compare_semantic_session_tag?: string | undefined;
|
|
61
|
+
format?: "markdown" | "json" | "html" | undefined;
|
|
62
|
+
include_sections?: string[] | undefined;
|
|
63
|
+
}, {
|
|
64
|
+
sample_id: string;
|
|
65
|
+
evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
66
|
+
evidence_session_tag?: string | undefined;
|
|
67
|
+
semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
68
|
+
semantic_session_tag?: string | undefined;
|
|
69
|
+
compare_evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
70
|
+
compare_evidence_session_tag?: string | undefined;
|
|
71
|
+
compare_semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
72
|
+
compare_semantic_session_tag?: string | undefined;
|
|
73
|
+
format?: "markdown" | "json" | "html" | undefined;
|
|
74
|
+
include_sections?: string[] | undefined;
|
|
75
|
+
}>, {
|
|
76
|
+
sample_id: string;
|
|
77
|
+
evidence_scope: "all" | "latest" | "session";
|
|
78
|
+
semantic_scope: "all" | "latest" | "session";
|
|
79
|
+
evidence_session_tag?: string | undefined;
|
|
80
|
+
semantic_session_tag?: string | undefined;
|
|
81
|
+
compare_evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
82
|
+
compare_evidence_session_tag?: string | undefined;
|
|
83
|
+
compare_semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
84
|
+
compare_semantic_session_tag?: string | undefined;
|
|
85
|
+
format?: "markdown" | "json" | "html" | undefined;
|
|
86
|
+
include_sections?: string[] | undefined;
|
|
87
|
+
}, {
|
|
88
|
+
sample_id: string;
|
|
89
|
+
evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
90
|
+
evidence_session_tag?: string | undefined;
|
|
91
|
+
semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
92
|
+
semantic_session_tag?: string | undefined;
|
|
93
|
+
compare_evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
94
|
+
compare_evidence_session_tag?: string | undefined;
|
|
95
|
+
compare_semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
96
|
+
compare_semantic_session_tag?: string | undefined;
|
|
97
|
+
format?: "markdown" | "json" | "html" | undefined;
|
|
98
|
+
include_sections?: string[] | undefined;
|
|
99
|
+
}>, {
|
|
100
|
+
sample_id: string;
|
|
101
|
+
evidence_scope: "all" | "latest" | "session";
|
|
102
|
+
semantic_scope: "all" | "latest" | "session";
|
|
103
|
+
evidence_session_tag?: string | undefined;
|
|
104
|
+
semantic_session_tag?: string | undefined;
|
|
105
|
+
compare_evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
106
|
+
compare_evidence_session_tag?: string | undefined;
|
|
107
|
+
compare_semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
108
|
+
compare_semantic_session_tag?: string | undefined;
|
|
109
|
+
format?: "markdown" | "json" | "html" | undefined;
|
|
110
|
+
include_sections?: string[] | undefined;
|
|
111
|
+
}, {
|
|
112
|
+
sample_id: string;
|
|
113
|
+
evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
114
|
+
evidence_session_tag?: string | undefined;
|
|
115
|
+
semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
116
|
+
semantic_session_tag?: string | undefined;
|
|
117
|
+
compare_evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
118
|
+
compare_evidence_session_tag?: string | undefined;
|
|
119
|
+
compare_semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
120
|
+
compare_semantic_session_tag?: string | undefined;
|
|
121
|
+
format?: "markdown" | "json" | "html" | undefined;
|
|
122
|
+
include_sections?: string[] | undefined;
|
|
123
|
+
}>, {
|
|
124
|
+
sample_id: string;
|
|
125
|
+
evidence_scope: "all" | "latest" | "session";
|
|
126
|
+
semantic_scope: "all" | "latest" | "session";
|
|
127
|
+
evidence_session_tag?: string | undefined;
|
|
128
|
+
semantic_session_tag?: string | undefined;
|
|
129
|
+
compare_evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
130
|
+
compare_evidence_session_tag?: string | undefined;
|
|
131
|
+
compare_semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
132
|
+
compare_semantic_session_tag?: string | undefined;
|
|
133
|
+
format?: "markdown" | "json" | "html" | undefined;
|
|
134
|
+
include_sections?: string[] | undefined;
|
|
135
|
+
}, {
|
|
136
|
+
sample_id: string;
|
|
137
|
+
evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
138
|
+
evidence_session_tag?: string | undefined;
|
|
139
|
+
semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
140
|
+
semantic_session_tag?: string | undefined;
|
|
141
|
+
compare_evidence_scope?: "all" | "latest" | "session" | undefined;
|
|
142
|
+
compare_evidence_session_tag?: string | undefined;
|
|
143
|
+
compare_semantic_scope?: "all" | "latest" | "session" | undefined;
|
|
144
|
+
compare_semantic_session_tag?: string | undefined;
|
|
145
|
+
format?: "markdown" | "json" | "html" | undefined;
|
|
146
|
+
include_sections?: string[] | undefined;
|
|
147
|
+
}>;
|
|
148
|
+
export type ReportGenerateInput = z.infer<typeof reportGenerateInputSchema>;
|
|
149
|
+
/**
|
|
150
|
+
* Tool definition for report.generate
|
|
151
|
+
*/
|
|
152
|
+
export declare const reportGenerateToolDefinition: ToolDefinition;
|
|
153
|
+
/**
|
|
154
|
+
* Create handler for report.generate tool
|
|
155
|
+
*/
|
|
156
|
+
export declare function createReportGenerateHandler(workspaceManager: WorkspaceManager, database: DatabaseManager): ToolHandler;
|
|
157
|
+
//# sourceMappingURL=report-generate.d.ts.map
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* report.generate MCP Tool
|
|
3
|
+
*
|
|
4
|
+
* Requirements: 24.1, 24.3, 24.5, 24.6
|
|
5
|
+
*
|
|
6
|
+
* Generates comprehensive Markdown analysis report
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { createHash, randomUUID } from 'crypto';
|
|
12
|
+
import { logger } from '../logger.js';
|
|
13
|
+
import { isGhidraReadyStatus } from '../ghidra-analysis-status.js';
|
|
14
|
+
import { loadDynamicTraceEvidence } from '../dynamic-trace.js';
|
|
15
|
+
import { loadSemanticFunctionExplanationIndex } from '../semantic-name-suggestion-artifacts.js';
|
|
16
|
+
import { buildReportConfidenceSemantics } from '../confidence-semantics.js';
|
|
17
|
+
import { buildRuntimeArtifactProvenance, buildSemanticArtifactProvenance, } from '../analysis-provenance.js';
|
|
18
|
+
import { buildArtifactSelectionDiff, } from '../selection-diff.js';
|
|
19
|
+
/**
|
|
20
|
+
* Input schema for report.generate tool
|
|
21
|
+
*/
|
|
22
|
+
export const reportGenerateInputSchema = z.object({
|
|
23
|
+
sample_id: z.string().describe('Sample identifier (sha256:<hex>)'),
|
|
24
|
+
format: z.enum(['markdown', 'json', 'html']).optional().describe('Report format (default: markdown)'),
|
|
25
|
+
include_sections: z.array(z.string()).optional().describe('Sections to include (default: all)'),
|
|
26
|
+
evidence_scope: z
|
|
27
|
+
.enum(['all', 'latest', 'session'])
|
|
28
|
+
.optional()
|
|
29
|
+
.default('all')
|
|
30
|
+
.describe('Runtime evidence scope: all artifacts, latest artifact window, or a specific session selector'),
|
|
31
|
+
evidence_session_tag: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe('Optional runtime evidence session selector used when evidence_scope=session or to narrow all/latest results'),
|
|
35
|
+
semantic_scope: z
|
|
36
|
+
.enum(['all', 'latest', 'session'])
|
|
37
|
+
.optional()
|
|
38
|
+
.default('all')
|
|
39
|
+
.describe('Semantic explanation artifact scope: all artifacts, latest explanation window, or a specific semantic review session'),
|
|
40
|
+
semantic_session_tag: z
|
|
41
|
+
.string()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe('Optional semantic review session selector used when semantic_scope=session or to narrow all/latest results'),
|
|
44
|
+
compare_evidence_scope: z
|
|
45
|
+
.enum(['all', 'latest', 'session'])
|
|
46
|
+
.optional()
|
|
47
|
+
.describe('Optional baseline runtime evidence scope used to compare this report against another runtime artifact selection'),
|
|
48
|
+
compare_evidence_session_tag: z
|
|
49
|
+
.string()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe('Optional baseline runtime evidence session selector used when compare_evidence_scope=session'),
|
|
52
|
+
compare_semantic_scope: z
|
|
53
|
+
.enum(['all', 'latest', 'session'])
|
|
54
|
+
.optional()
|
|
55
|
+
.describe('Optional baseline semantic explanation scope used to compare this report against another semantic artifact selection'),
|
|
56
|
+
compare_semantic_session_tag: z
|
|
57
|
+
.string()
|
|
58
|
+
.optional()
|
|
59
|
+
.describe('Optional baseline semantic explanation session selector used when compare_semantic_scope=session'),
|
|
60
|
+
}).refine((value) => value.evidence_scope !== 'session' || Boolean(value.evidence_session_tag?.trim()), {
|
|
61
|
+
message: 'evidence_session_tag is required when evidence_scope=session',
|
|
62
|
+
path: ['evidence_session_tag'],
|
|
63
|
+
}).refine((value) => value.semantic_scope !== 'session' || Boolean(value.semantic_session_tag?.trim()), {
|
|
64
|
+
message: 'semantic_session_tag is required when semantic_scope=session',
|
|
65
|
+
path: ['semantic_session_tag'],
|
|
66
|
+
}).refine((value) => value.compare_evidence_scope !== 'session' || Boolean(value.compare_evidence_session_tag?.trim()), {
|
|
67
|
+
message: 'compare_evidence_session_tag is required when compare_evidence_scope=session',
|
|
68
|
+
path: ['compare_evidence_session_tag'],
|
|
69
|
+
}).refine((value) => value.compare_semantic_scope !== 'session' || Boolean(value.compare_semantic_session_tag?.trim()), {
|
|
70
|
+
message: 'compare_semantic_session_tag is required when compare_semantic_scope=session',
|
|
71
|
+
path: ['compare_semantic_session_tag'],
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* Tool definition for report.generate
|
|
75
|
+
*/
|
|
76
|
+
export const reportGenerateToolDefinition = {
|
|
77
|
+
name: 'report.generate',
|
|
78
|
+
description: 'Generate comprehensive analysis report aggregating all analysis results. Supports Markdown, JSON, and HTML formats.',
|
|
79
|
+
inputSchema: reportGenerateInputSchema
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Generate Markdown report
|
|
83
|
+
*/
|
|
84
|
+
function generateMarkdownReport(sample, analyses, functions, dynamicEvidence, functionExplanations, evidenceScope, evidenceSessionTag, semanticScope = 'all', semanticSessionTag, provenance, selectionDiffs) {
|
|
85
|
+
const lines = [];
|
|
86
|
+
// Header
|
|
87
|
+
lines.push(`# Analysis Report: ${sample.sha256}`);
|
|
88
|
+
lines.push('');
|
|
89
|
+
lines.push(`**Generated:** ${new Date().toISOString()}`);
|
|
90
|
+
lines.push('');
|
|
91
|
+
// Sample Information
|
|
92
|
+
lines.push('## Sample Information');
|
|
93
|
+
lines.push('');
|
|
94
|
+
lines.push(`- **SHA256:** ${sample.sha256}`);
|
|
95
|
+
lines.push(`- **MD5:** ${sample.md5}`);
|
|
96
|
+
lines.push(`- **Size:** ${sample.size} bytes`);
|
|
97
|
+
lines.push(`- **File Type:** ${sample.file_type || 'Unknown'}`);
|
|
98
|
+
lines.push(`- **Ingested:** ${sample.created_at}`);
|
|
99
|
+
lines.push('');
|
|
100
|
+
// Analysis Summary
|
|
101
|
+
lines.push('## Analysis Summary');
|
|
102
|
+
lines.push('');
|
|
103
|
+
lines.push(`- **Total Analyses:** ${analyses.length}`);
|
|
104
|
+
lines.push(`- **Completed:** ${analyses.filter(a => isGhidraReadyStatus(a.status)).length}`);
|
|
105
|
+
lines.push(`- **Failed:** ${analyses.filter(a => a.status === 'failed').length}`);
|
|
106
|
+
lines.push('');
|
|
107
|
+
// Analyses Details
|
|
108
|
+
for (const analysis of analyses) {
|
|
109
|
+
lines.push(`### ${analysis.stage} (${analysis.backend})`);
|
|
110
|
+
lines.push('');
|
|
111
|
+
lines.push(`- **Status:** ${analysis.status}`);
|
|
112
|
+
lines.push(`- **Started:** ${analysis.started_at || 'N/A'}`);
|
|
113
|
+
lines.push(`- **Finished:** ${analysis.finished_at || 'N/A'}`);
|
|
114
|
+
if (analysis.metrics_json) {
|
|
115
|
+
try {
|
|
116
|
+
const metrics = JSON.parse(analysis.metrics_json);
|
|
117
|
+
lines.push(`- **Duration:** ${metrics.elapsed_ms}ms`);
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
// Ignore parse errors
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (analysis.output_json) {
|
|
124
|
+
try {
|
|
125
|
+
const output = JSON.parse(analysis.output_json);
|
|
126
|
+
if (output.function_count !== undefined) {
|
|
127
|
+
lines.push(`- **Functions Extracted:** ${output.function_count}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
// Ignore parse errors
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
lines.push('');
|
|
135
|
+
}
|
|
136
|
+
// Function Statistics
|
|
137
|
+
if (functions.length > 0) {
|
|
138
|
+
lines.push('## Function Statistics');
|
|
139
|
+
lines.push('');
|
|
140
|
+
lines.push(`- **Total Functions:** ${functions.length}`);
|
|
141
|
+
const avgSize = functions.reduce((sum, f) => sum + (f.size || 0), 0) / functions.length;
|
|
142
|
+
lines.push(`- **Average Size:** ${Math.round(avgSize)} bytes`);
|
|
143
|
+
const entryPoints = functions.filter(f => f.is_entry_point === 1).length;
|
|
144
|
+
lines.push(`- **Entry Points:** ${entryPoints}`);
|
|
145
|
+
const exported = functions.filter(f => f.is_exported === 1).length;
|
|
146
|
+
lines.push(`- **Exported Functions:** ${exported}`);
|
|
147
|
+
lines.push('');
|
|
148
|
+
// Top Functions
|
|
149
|
+
const topFunctions = functions
|
|
150
|
+
.filter(f => f.score > 0)
|
|
151
|
+
.sort((a, b) => b.score - a.score)
|
|
152
|
+
.slice(0, 10);
|
|
153
|
+
if (topFunctions.length > 0) {
|
|
154
|
+
lines.push('### Top 10 Functions by Interest Score');
|
|
155
|
+
lines.push('');
|
|
156
|
+
lines.push('| Rank | Address | Name | Score | Tags |');
|
|
157
|
+
lines.push('|------|---------|------|-------|------|');
|
|
158
|
+
topFunctions.forEach((func, index) => {
|
|
159
|
+
const tags = func.tags ? JSON.parse(func.tags).join(', ') : '';
|
|
160
|
+
lines.push(`| ${index + 1} | ${func.address} | ${func.name || 'unknown'} | ${func.score.toFixed(2)} | ${tags} |`);
|
|
161
|
+
});
|
|
162
|
+
lines.push('');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
lines.push('## Runtime Evidence');
|
|
166
|
+
lines.push('');
|
|
167
|
+
lines.push(`- **Evidence Scope:** ${evidenceScope}`);
|
|
168
|
+
lines.push(`- **Session Selector:** ${evidenceSessionTag || 'N/A'}`);
|
|
169
|
+
if (dynamicEvidence) {
|
|
170
|
+
lines.push(`- **Artifacts Considered:** ${dynamicEvidence.artifact_count}`);
|
|
171
|
+
lines.push(`- **Executed Trace Present:** ${dynamicEvidence.executed ? 'Yes' : 'No'}`);
|
|
172
|
+
lines.push(`- **Latest Imported At:** ${dynamicEvidence.latest_imported_at || 'N/A'}`);
|
|
173
|
+
lines.push(`- **Scope Note:** ${dynamicEvidence.scope_note || 'N/A'}`);
|
|
174
|
+
lines.push(`- **High Signal APIs:** ${dynamicEvidence.high_signal_apis.join(', ') || 'none'}`);
|
|
175
|
+
lines.push(`- **Stages:** ${dynamicEvidence.stages.join(', ') || 'none'}`);
|
|
176
|
+
lines.push(`- **Source Formats:** ${(dynamicEvidence.source_formats || []).join(', ') || 'none'}`);
|
|
177
|
+
lines.push(`- **Source Names:** ${(dynamicEvidence.source_names || []).join(', ') || 'none'}`);
|
|
178
|
+
if ((dynamicEvidence.confidence_layers || []).length > 0) {
|
|
179
|
+
lines.push('');
|
|
180
|
+
lines.push('### Runtime Evidence Lineage');
|
|
181
|
+
lines.push('');
|
|
182
|
+
for (const layer of dynamicEvidence.confidence_layers || []) {
|
|
183
|
+
lines.push(`- **${layer.layer}:** artifacts=${layer.artifact_count}, band=${layer.confidence_band}, latest=${layer.latest_imported_at || 'N/A'}, sources=${layer.source_names.join(', ') || 'none'}`);
|
|
184
|
+
}
|
|
185
|
+
lines.push('');
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
lines.push('');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
lines.push('- **Artifacts Considered:** 0');
|
|
193
|
+
lines.push('- **Scope Note:** No runtime evidence matched the selected scope.');
|
|
194
|
+
lines.push('');
|
|
195
|
+
}
|
|
196
|
+
if (provenance) {
|
|
197
|
+
lines.push('## Analysis Provenance');
|
|
198
|
+
lines.push('');
|
|
199
|
+
lines.push(`- **Runtime Artifact IDs:** ${provenance.runtime.artifact_ids.join(', ') || 'none'}`);
|
|
200
|
+
lines.push(`- **Runtime Session Tags:** ${provenance.runtime.session_tags.join(', ') || 'none'}`);
|
|
201
|
+
lines.push(`- **Runtime Latest Artifact At:** ${provenance.runtime.latest_artifact_at || 'N/A'}`);
|
|
202
|
+
lines.push(`- **Semantic Artifact IDs:** ${provenance.semantic_explanations.artifact_ids.join(', ') || 'none'}`);
|
|
203
|
+
lines.push(`- **Semantic Session Tags:** ${provenance.semantic_explanations.session_tags.join(', ') || 'none'}`);
|
|
204
|
+
lines.push(`- **Semantic Latest Artifact At:** ${provenance.semantic_explanations.latest_artifact_at || 'N/A'}`);
|
|
205
|
+
lines.push('');
|
|
206
|
+
}
|
|
207
|
+
if (selectionDiffs && (selectionDiffs.runtime || selectionDiffs.semantic_explanations)) {
|
|
208
|
+
lines.push('## Selection Diffs');
|
|
209
|
+
lines.push('');
|
|
210
|
+
if (selectionDiffs.runtime) {
|
|
211
|
+
lines.push(`- **Runtime Diff:** ${selectionDiffs.runtime.summary}`);
|
|
212
|
+
}
|
|
213
|
+
if (selectionDiffs.semantic_explanations) {
|
|
214
|
+
lines.push(`- **Semantic Diff:** ${selectionDiffs.semantic_explanations.summary}`);
|
|
215
|
+
}
|
|
216
|
+
lines.push('');
|
|
217
|
+
}
|
|
218
|
+
if (functionExplanations.length > 0) {
|
|
219
|
+
lines.push('## Function Explanations');
|
|
220
|
+
lines.push('');
|
|
221
|
+
lines.push(`- **Semantic Scope:** ${semanticScope}`);
|
|
222
|
+
lines.push(`- **Semantic Session Selector:** ${semanticSessionTag || 'N/A'}`);
|
|
223
|
+
lines.push('');
|
|
224
|
+
for (const explanation of functionExplanations) {
|
|
225
|
+
lines.push(`- **${explanation.behavior}:** ${explanation.summary} (confidence=${explanation.confidence.toFixed(2)}, target=${explanation.function || explanation.address || 'unknown'}, source=${explanation.source || 'unknown'})`);
|
|
226
|
+
if (explanation.rewrite_guidance.length > 0) {
|
|
227
|
+
lines.push(` rewrite_guidance: ${explanation.rewrite_guidance.join(' | ')}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
lines.push('');
|
|
231
|
+
}
|
|
232
|
+
const confidenceSemantics = buildReportConfidenceSemantics({
|
|
233
|
+
score: dynamicEvidence?.executed ? 0.72 : dynamicEvidence ? 0.58 : 0.42,
|
|
234
|
+
evidenceScope,
|
|
235
|
+
runtimeLayers: dynamicEvidence?.confidence_layers?.map((item) => item.layer) || ['static_only'],
|
|
236
|
+
executedTracePresent: dynamicEvidence?.executed || false,
|
|
237
|
+
});
|
|
238
|
+
lines.push('## Confidence Semantics');
|
|
239
|
+
lines.push('');
|
|
240
|
+
lines.push(`- **Score Kind:** ${confidenceSemantics.score_kind}`);
|
|
241
|
+
lines.push(`- **Band:** ${confidenceSemantics.band}`);
|
|
242
|
+
lines.push(`- **Calibrated Probability:** ${confidenceSemantics.calibrated ? 'Yes' : 'No'}`);
|
|
243
|
+
lines.push(`- **Meaning:** ${confidenceSemantics.meaning}`);
|
|
244
|
+
lines.push(`- **Compare Within:** ${confidenceSemantics.compare_within}`);
|
|
245
|
+
lines.push(`- **Caution:** ${confidenceSemantics.caution}`);
|
|
246
|
+
lines.push(`- **Drivers:** ${confidenceSemantics.drivers.join(', ') || 'none'}`);
|
|
247
|
+
lines.push('');
|
|
248
|
+
// Footer
|
|
249
|
+
lines.push('---');
|
|
250
|
+
lines.push('');
|
|
251
|
+
lines.push('*Report generated by Windows EXE Decompiler MCP Server*');
|
|
252
|
+
lines.push('');
|
|
253
|
+
return lines.join('\n');
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Create handler for report.generate tool
|
|
257
|
+
*/
|
|
258
|
+
export function createReportGenerateHandler(workspaceManager, database) {
|
|
259
|
+
return async (args) => {
|
|
260
|
+
try {
|
|
261
|
+
const input = reportGenerateInputSchema.parse(args);
|
|
262
|
+
logger.info({
|
|
263
|
+
sample_id: input.sample_id,
|
|
264
|
+
format: input.format,
|
|
265
|
+
evidence_scope: input.evidence_scope,
|
|
266
|
+
evidence_session_tag: input.evidence_session_tag || null,
|
|
267
|
+
}, 'report.generate tool called');
|
|
268
|
+
// Check if sample exists
|
|
269
|
+
const sample = database.findSample(input.sample_id);
|
|
270
|
+
if (!sample) {
|
|
271
|
+
return {
|
|
272
|
+
content: [{
|
|
273
|
+
type: 'text',
|
|
274
|
+
text: JSON.stringify({
|
|
275
|
+
ok: false,
|
|
276
|
+
errors: [`Sample not found: ${input.sample_id}`]
|
|
277
|
+
}, null, 2)
|
|
278
|
+
}],
|
|
279
|
+
isError: true
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
// Get all analyses for this sample
|
|
283
|
+
const analyses = database.findAnalysesBySample(input.sample_id);
|
|
284
|
+
// Get all functions for this sample
|
|
285
|
+
const functions = database.findFunctions(input.sample_id);
|
|
286
|
+
const dynamicEvidence = await loadDynamicTraceEvidence(workspaceManager, database, input.sample_id, {
|
|
287
|
+
evidenceScope: input.evidence_scope,
|
|
288
|
+
sessionTag: input.evidence_session_tag,
|
|
289
|
+
});
|
|
290
|
+
const functionExplanationIndex = await loadSemanticFunctionExplanationIndex(workspaceManager, database, input.sample_id, {
|
|
291
|
+
scope: input.semantic_scope,
|
|
292
|
+
sessionTag: input.semantic_session_tag,
|
|
293
|
+
});
|
|
294
|
+
const functionExplanations = Array.from(functionExplanationIndex.byAddress.values())
|
|
295
|
+
.sort((a, b) => {
|
|
296
|
+
if (b.confidence !== a.confidence) {
|
|
297
|
+
return b.confidence - a.confidence;
|
|
298
|
+
}
|
|
299
|
+
return (b.created_at || '').localeCompare(a.created_at || '');
|
|
300
|
+
})
|
|
301
|
+
.slice(0, 6)
|
|
302
|
+
.map((item) => ({
|
|
303
|
+
address: item.address,
|
|
304
|
+
function: item.function,
|
|
305
|
+
behavior: item.behavior,
|
|
306
|
+
summary: item.summary,
|
|
307
|
+
confidence: item.confidence,
|
|
308
|
+
rewrite_guidance: item.rewrite_guidance.slice(0, 4),
|
|
309
|
+
source: item.model_name || item.client_name || null,
|
|
310
|
+
}));
|
|
311
|
+
const provenance = {
|
|
312
|
+
runtime: buildRuntimeArtifactProvenance(dynamicEvidence, input.evidence_scope, input.evidence_session_tag),
|
|
313
|
+
semantic_explanations: buildSemanticArtifactProvenance('semantic explanation artifacts', functionExplanationIndex, input.semantic_scope, input.semantic_session_tag),
|
|
314
|
+
};
|
|
315
|
+
const selectionDiffs = {};
|
|
316
|
+
if (input.compare_evidence_scope) {
|
|
317
|
+
const baselineDynamicEvidence = await loadDynamicTraceEvidence(workspaceManager, database, input.sample_id, {
|
|
318
|
+
evidenceScope: input.compare_evidence_scope,
|
|
319
|
+
sessionTag: input.compare_evidence_session_tag,
|
|
320
|
+
});
|
|
321
|
+
selectionDiffs.runtime = buildArtifactSelectionDiff('runtime', provenance.runtime, buildRuntimeArtifactProvenance(baselineDynamicEvidence, input.compare_evidence_scope, input.compare_evidence_session_tag));
|
|
322
|
+
}
|
|
323
|
+
if (input.compare_semantic_scope) {
|
|
324
|
+
const baselineSemanticIndex = await loadSemanticFunctionExplanationIndex(workspaceManager, database, input.sample_id, {
|
|
325
|
+
scope: input.compare_semantic_scope,
|
|
326
|
+
sessionTag: input.compare_semantic_session_tag,
|
|
327
|
+
});
|
|
328
|
+
selectionDiffs.semantic_explanations = buildArtifactSelectionDiff('semantic_explanations', provenance.semantic_explanations, buildSemanticArtifactProvenance('semantic explanation artifacts', baselineSemanticIndex, input.compare_semantic_scope, input.compare_semantic_session_tag));
|
|
329
|
+
}
|
|
330
|
+
// Generate report based on format
|
|
331
|
+
const format = input.format || 'markdown';
|
|
332
|
+
let reportContent;
|
|
333
|
+
let reportExtension;
|
|
334
|
+
let mimeType;
|
|
335
|
+
switch (format) {
|
|
336
|
+
case 'markdown':
|
|
337
|
+
reportContent = generateMarkdownReport(sample, analyses, functions, dynamicEvidence, functionExplanations, input.evidence_scope, input.evidence_session_tag, input.semantic_scope, input.semantic_session_tag, provenance, selectionDiffs);
|
|
338
|
+
reportExtension = 'md';
|
|
339
|
+
mimeType = 'text/markdown';
|
|
340
|
+
break;
|
|
341
|
+
case 'json':
|
|
342
|
+
const confidenceSemantics = buildReportConfidenceSemantics({
|
|
343
|
+
score: dynamicEvidence?.executed ? 0.72 : dynamicEvidence ? 0.58 : 0.42,
|
|
344
|
+
evidenceScope: input.evidence_scope,
|
|
345
|
+
runtimeLayers: dynamicEvidence?.confidence_layers?.map((item) => item.layer) || ['static_only'],
|
|
346
|
+
executedTracePresent: dynamicEvidence?.executed || false,
|
|
347
|
+
});
|
|
348
|
+
reportContent = JSON.stringify({
|
|
349
|
+
sample,
|
|
350
|
+
analyses,
|
|
351
|
+
functions,
|
|
352
|
+
dynamic_evidence: dynamicEvidence,
|
|
353
|
+
function_explanations: functionExplanations,
|
|
354
|
+
evidence_scope: input.evidence_scope,
|
|
355
|
+
evidence_session_tag: input.evidence_session_tag || null,
|
|
356
|
+
semantic_scope: input.semantic_scope,
|
|
357
|
+
semantic_session_tag: input.semantic_session_tag || null,
|
|
358
|
+
provenance,
|
|
359
|
+
selection_diffs: Object.keys(selectionDiffs).length > 0 ? selectionDiffs : undefined,
|
|
360
|
+
confidence_semantics: confidenceSemantics,
|
|
361
|
+
generated_at: new Date().toISOString()
|
|
362
|
+
}, null, 2);
|
|
363
|
+
reportExtension = 'json';
|
|
364
|
+
mimeType = 'application/json';
|
|
365
|
+
break;
|
|
366
|
+
case 'html':
|
|
367
|
+
// Simple HTML wrapper around markdown
|
|
368
|
+
reportContent = `<!DOCTYPE html>
|
|
369
|
+
<html>
|
|
370
|
+
<head>
|
|
371
|
+
<meta charset="UTF-8">
|
|
372
|
+
<title>Analysis Report: ${sample.sha256}</title>
|
|
373
|
+
<style>
|
|
374
|
+
body { font-family: Arial, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; }
|
|
375
|
+
table { border-collapse: collapse; width: 100%; }
|
|
376
|
+
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
|
377
|
+
th { background-color: #f2f2f2; }
|
|
378
|
+
</style>
|
|
379
|
+
</head>
|
|
380
|
+
<body>
|
|
381
|
+
<pre>${generateMarkdownReport(sample, analyses, functions, dynamicEvidence, functionExplanations, input.evidence_scope, input.evidence_session_tag, input.semantic_scope, input.semantic_session_tag, provenance, selectionDiffs)}</pre>
|
|
382
|
+
</body>
|
|
383
|
+
</html>`;
|
|
384
|
+
reportExtension = 'html';
|
|
385
|
+
mimeType = 'text/html';
|
|
386
|
+
break;
|
|
387
|
+
default:
|
|
388
|
+
throw new Error(`Unsupported format: ${format}`);
|
|
389
|
+
}
|
|
390
|
+
// Store report to workspace
|
|
391
|
+
const workspace = await workspaceManager.getWorkspace(input.sample_id);
|
|
392
|
+
const reportFilename = `report_${Date.now()}.${reportExtension}`;
|
|
393
|
+
const reportPath = path.join(workspace.reports, reportFilename);
|
|
394
|
+
// Ensure reports directory exists
|
|
395
|
+
if (!fs.existsSync(workspace.reports)) {
|
|
396
|
+
fs.mkdirSync(workspace.reports, { recursive: true });
|
|
397
|
+
}
|
|
398
|
+
// Write report file
|
|
399
|
+
fs.writeFileSync(reportPath, reportContent, 'utf-8');
|
|
400
|
+
// Compute SHA256 of report
|
|
401
|
+
const reportSha256 = createHash('sha256')
|
|
402
|
+
.update(reportContent)
|
|
403
|
+
.digest('hex');
|
|
404
|
+
// Insert artifact record
|
|
405
|
+
const artifactId = randomUUID();
|
|
406
|
+
database.insertArtifact({
|
|
407
|
+
id: artifactId,
|
|
408
|
+
sample_id: input.sample_id,
|
|
409
|
+
type: `report_${format}`,
|
|
410
|
+
path: `reports/${reportFilename}`,
|
|
411
|
+
sha256: reportSha256,
|
|
412
|
+
mime: mimeType,
|
|
413
|
+
created_at: new Date().toISOString()
|
|
414
|
+
});
|
|
415
|
+
logger.info({
|
|
416
|
+
sample_id: input.sample_id,
|
|
417
|
+
format,
|
|
418
|
+
artifact_id: artifactId,
|
|
419
|
+
path: reportPath
|
|
420
|
+
}, 'Report generated successfully');
|
|
421
|
+
return {
|
|
422
|
+
content: [{
|
|
423
|
+
type: 'text',
|
|
424
|
+
text: JSON.stringify({
|
|
425
|
+
ok: true,
|
|
426
|
+
data: {
|
|
427
|
+
artifact_id: artifactId,
|
|
428
|
+
path: reportPath,
|
|
429
|
+
format,
|
|
430
|
+
size: reportContent.length,
|
|
431
|
+
sha256: reportSha256,
|
|
432
|
+
provenance,
|
|
433
|
+
selection_diffs: Object.keys(selectionDiffs).length > 0 ? selectionDiffs : undefined
|
|
434
|
+
}
|
|
435
|
+
}, null, 2)
|
|
436
|
+
}]
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
catch (error) {
|
|
440
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
441
|
+
logger.error({
|
|
442
|
+
error: errorMessage
|
|
443
|
+
}, 'report.generate tool failed');
|
|
444
|
+
return {
|
|
445
|
+
content: [{
|
|
446
|
+
type: 'text',
|
|
447
|
+
text: JSON.stringify({
|
|
448
|
+
ok: false,
|
|
449
|
+
errors: [errorMessage]
|
|
450
|
+
}, null, 2)
|
|
451
|
+
}],
|
|
452
|
+
isError: true
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
//# sourceMappingURL=report-generate.js.map
|