iflow-mcp_developermode-korea_reversecore-mcp 1.0.0__py3-none-any.whl
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.
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/METADATA +543 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/RECORD +79 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/WHEEL +5 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/entry_points.txt +2 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/licenses/LICENSE +21 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/top_level.txt +1 -0
- reversecore_mcp/__init__.py +9 -0
- reversecore_mcp/core/__init__.py +78 -0
- reversecore_mcp/core/audit.py +101 -0
- reversecore_mcp/core/binary_cache.py +138 -0
- reversecore_mcp/core/command_spec.py +357 -0
- reversecore_mcp/core/config.py +432 -0
- reversecore_mcp/core/container.py +288 -0
- reversecore_mcp/core/decorators.py +152 -0
- reversecore_mcp/core/error_formatting.py +93 -0
- reversecore_mcp/core/error_handling.py +142 -0
- reversecore_mcp/core/evidence.py +229 -0
- reversecore_mcp/core/exceptions.py +296 -0
- reversecore_mcp/core/execution.py +240 -0
- reversecore_mcp/core/ghidra.py +642 -0
- reversecore_mcp/core/ghidra_helper.py +481 -0
- reversecore_mcp/core/ghidra_manager.py +234 -0
- reversecore_mcp/core/json_utils.py +131 -0
- reversecore_mcp/core/loader.py +73 -0
- reversecore_mcp/core/logging_config.py +206 -0
- reversecore_mcp/core/memory.py +721 -0
- reversecore_mcp/core/metrics.py +198 -0
- reversecore_mcp/core/mitre_mapper.py +365 -0
- reversecore_mcp/core/plugin.py +45 -0
- reversecore_mcp/core/r2_helpers.py +404 -0
- reversecore_mcp/core/r2_pool.py +403 -0
- reversecore_mcp/core/report_generator.py +268 -0
- reversecore_mcp/core/resilience.py +252 -0
- reversecore_mcp/core/resource_manager.py +169 -0
- reversecore_mcp/core/result.py +132 -0
- reversecore_mcp/core/security.py +213 -0
- reversecore_mcp/core/validators.py +238 -0
- reversecore_mcp/dashboard/__init__.py +221 -0
- reversecore_mcp/prompts/__init__.py +56 -0
- reversecore_mcp/prompts/common.py +24 -0
- reversecore_mcp/prompts/game.py +280 -0
- reversecore_mcp/prompts/malware.py +1219 -0
- reversecore_mcp/prompts/report.py +150 -0
- reversecore_mcp/prompts/security.py +136 -0
- reversecore_mcp/resources.py +329 -0
- reversecore_mcp/server.py +727 -0
- reversecore_mcp/tools/__init__.py +49 -0
- reversecore_mcp/tools/analysis/__init__.py +74 -0
- reversecore_mcp/tools/analysis/capa_tools.py +215 -0
- reversecore_mcp/tools/analysis/die_tools.py +180 -0
- reversecore_mcp/tools/analysis/diff_tools.py +643 -0
- reversecore_mcp/tools/analysis/lief_tools.py +272 -0
- reversecore_mcp/tools/analysis/signature_tools.py +591 -0
- reversecore_mcp/tools/analysis/static_analysis.py +479 -0
- reversecore_mcp/tools/common/__init__.py +58 -0
- reversecore_mcp/tools/common/file_operations.py +352 -0
- reversecore_mcp/tools/common/memory_tools.py +516 -0
- reversecore_mcp/tools/common/patch_explainer.py +230 -0
- reversecore_mcp/tools/common/server_tools.py +115 -0
- reversecore_mcp/tools/ghidra/__init__.py +19 -0
- reversecore_mcp/tools/ghidra/decompilation.py +975 -0
- reversecore_mcp/tools/ghidra/ghidra_tools.py +1052 -0
- reversecore_mcp/tools/malware/__init__.py +61 -0
- reversecore_mcp/tools/malware/adaptive_vaccine.py +579 -0
- reversecore_mcp/tools/malware/dormant_detector.py +756 -0
- reversecore_mcp/tools/malware/ioc_tools.py +228 -0
- reversecore_mcp/tools/malware/vulnerability_hunter.py +519 -0
- reversecore_mcp/tools/malware/yara_tools.py +214 -0
- reversecore_mcp/tools/patch_explainer.py +19 -0
- reversecore_mcp/tools/radare2/__init__.py +13 -0
- reversecore_mcp/tools/radare2/r2_analysis.py +972 -0
- reversecore_mcp/tools/radare2/r2_session.py +376 -0
- reversecore_mcp/tools/radare2/radare2_mcp_tools.py +1183 -0
- reversecore_mcp/tools/report/__init__.py +4 -0
- reversecore_mcp/tools/report/email.py +82 -0
- reversecore_mcp/tools/report/report_mcp_tools.py +344 -0
- reversecore_mcp/tools/report/report_tools.py +1076 -0
- reversecore_mcp/tools/report/session.py +194 -0
- reversecore_mcp/tools/report_tools.py +11 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Email configuration and utilities for report delivery.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class EmailConfig:
|
|
14
|
+
"""Email configuration"""
|
|
15
|
+
smtp_server: str = ""
|
|
16
|
+
smtp_port: int = 587
|
|
17
|
+
username: str = ""
|
|
18
|
+
password: str = ""
|
|
19
|
+
use_tls: bool = True
|
|
20
|
+
sender_name: str = "Reversecore_MCP"
|
|
21
|
+
default_recipients: list[str] = field(default_factory=list)
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def from_env(cls) -> "EmailConfig":
|
|
25
|
+
"""Load email configuration from environment variables"""
|
|
26
|
+
smtp_server = os.getenv("REPORT_SMTP_SERVER", "")
|
|
27
|
+
|
|
28
|
+
# Return disabled state if SMTP server is not configured
|
|
29
|
+
if not smtp_server:
|
|
30
|
+
logger.info("📧 Email not configured (REPORT_SMTP_SERVER not set)")
|
|
31
|
+
return cls()
|
|
32
|
+
|
|
33
|
+
config = cls(
|
|
34
|
+
smtp_server=smtp_server,
|
|
35
|
+
smtp_port=int(os.getenv("REPORT_SMTP_PORT", "587")),
|
|
36
|
+
username=os.getenv("REPORT_SMTP_USERNAME", ""),
|
|
37
|
+
password=os.getenv("REPORT_SMTP_PASSWORD", ""),
|
|
38
|
+
use_tls=os.getenv("REPORT_SMTP_USE_TLS", "true").lower() in ("true", "1", "yes"),
|
|
39
|
+
sender_name=os.getenv("REPORT_SENDER_NAME", "Reversecore_MCP"),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
logger.info(f"📧 Email configured: {smtp_server}:{config.smtp_port}")
|
|
43
|
+
return config
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def is_configured(self) -> bool:
|
|
47
|
+
"""Check if email is configured.
|
|
48
|
+
|
|
49
|
+
Requires smtp_server and username. If username is set, password must also be set
|
|
50
|
+
to avoid SMTPAuthenticationError at runtime.
|
|
51
|
+
"""
|
|
52
|
+
if not self.smtp_server:
|
|
53
|
+
return False
|
|
54
|
+
if self.username and not self.password:
|
|
55
|
+
return False # Username without password will fail auth
|
|
56
|
+
return True
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def load_quick_contacts_from_env() -> dict[str, dict[str, str]]:
|
|
60
|
+
"""Load quick contacts from environment variables
|
|
61
|
+
|
|
62
|
+
Format: REPORT_QUICK_CONTACTS=name1:email1:role1,name2:email2:role2
|
|
63
|
+
"""
|
|
64
|
+
contacts_str = os.getenv("REPORT_QUICK_CONTACTS", "")
|
|
65
|
+
contacts = {}
|
|
66
|
+
|
|
67
|
+
if not contacts_str:
|
|
68
|
+
return contacts
|
|
69
|
+
|
|
70
|
+
for entry in contacts_str.split(","):
|
|
71
|
+
parts = entry.strip().split(":")
|
|
72
|
+
if len(parts) >= 2:
|
|
73
|
+
name = parts[0].strip()
|
|
74
|
+
email = parts[1].strip()
|
|
75
|
+
role = parts[2].strip() if len(parts) > 2 else "Contact"
|
|
76
|
+
contacts[name] = {"email": email, "role": role}
|
|
77
|
+
logger.debug(f"Loaded quick contact: {name} -> {email}")
|
|
78
|
+
|
|
79
|
+
if contacts:
|
|
80
|
+
logger.info(f"📇 Loaded {len(contacts)} quick contacts from environment")
|
|
81
|
+
|
|
82
|
+
return contacts
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Tool Definitions for Report Generation
|
|
3
|
+
Register these tools in your MCP server
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from reversecore_mcp.core import json_utils as json
|
|
10
|
+
from reversecore_mcp.core.logging_config import get_logger
|
|
11
|
+
from reversecore_mcp.core.plugin import Plugin
|
|
12
|
+
|
|
13
|
+
from .report_tools import EmailConfig, ReportTools
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
# Global report_tools instance (initialized on first plugin registration)
|
|
18
|
+
_report_tools: ReportTools | None = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_report_tools(
|
|
22
|
+
template_dir: Path | None = None,
|
|
23
|
+
output_dir: Path | None = None,
|
|
24
|
+
default_timezone: str = "Asia/Seoul",
|
|
25
|
+
) -> ReportTools:
|
|
26
|
+
"""Get or create ReportTools singleton instance."""
|
|
27
|
+
global _report_tools
|
|
28
|
+
if _report_tools is None:
|
|
29
|
+
# Use package-relative path for templates (works regardless of CWD)
|
|
30
|
+
if template_dir is None:
|
|
31
|
+
# Check Docker path first (/app/templates/reports)
|
|
32
|
+
docker_path = Path("/app/templates/reports")
|
|
33
|
+
if docker_path.exists():
|
|
34
|
+
template_dir = docker_path
|
|
35
|
+
else:
|
|
36
|
+
# Fallback: Get the package root directory for local development
|
|
37
|
+
package_root = Path(
|
|
38
|
+
__file__
|
|
39
|
+
).parent.parent.parent # tools/report -> tools -> reversecore_mcp
|
|
40
|
+
project_root = package_root.parent # reversecore_mcp -> Reversecore_MCP
|
|
41
|
+
template_dir = project_root / "templates" / "reports"
|
|
42
|
+
|
|
43
|
+
_report_tools = ReportTools(
|
|
44
|
+
template_dir=template_dir,
|
|
45
|
+
output_dir=output_dir or Path("reports"),
|
|
46
|
+
default_timezone=default_timezone,
|
|
47
|
+
email_config=EmailConfig.from_env(),
|
|
48
|
+
)
|
|
49
|
+
return _report_tools
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# =============================================================================
|
|
53
|
+
# Tool Functions (defined at module level for proper decoration)
|
|
54
|
+
# =============================================================================
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def get_system_time() -> str:
|
|
58
|
+
"""
|
|
59
|
+
Get accurate system timestamp from the server.
|
|
60
|
+
|
|
61
|
+
Returns OS-level time data to prevent AI date hallucination.
|
|
62
|
+
Includes multiple formats (ISO, Unix, human-readable) and timezone info.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
JSON with report_id, date formats, time formats, timezone info
|
|
66
|
+
"""
|
|
67
|
+
report_tools = get_report_tools()
|
|
68
|
+
result = await report_tools.get_current_time()
|
|
69
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def set_timezone(timezone: str) -> str:
|
|
73
|
+
"""
|
|
74
|
+
Set the default timezone for timestamps.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
timezone: Timezone name (UTC, Asia/Seoul, Asia/Tokyo, etc.)
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
JSON with success status and current time in new timezone
|
|
81
|
+
"""
|
|
82
|
+
report_tools = get_report_tools()
|
|
83
|
+
result = report_tools.set_timezone(timezone)
|
|
84
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async def get_timezone_info() -> str:
|
|
88
|
+
"""
|
|
89
|
+
Get current timezone configuration and available options.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
JSON with current timezone, offset, and all available timezones
|
|
93
|
+
"""
|
|
94
|
+
report_tools = get_report_tools()
|
|
95
|
+
result = report_tools.get_timezone_info()
|
|
96
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
async def start_analysis_session(
|
|
100
|
+
sample_path: str = "",
|
|
101
|
+
analyst: str = "Security Researcher",
|
|
102
|
+
severity: str = "medium",
|
|
103
|
+
malware_family: str = "",
|
|
104
|
+
tags: str = "",
|
|
105
|
+
) -> str:
|
|
106
|
+
"""
|
|
107
|
+
Start a new malware analysis session.
|
|
108
|
+
|
|
109
|
+
Automatically tracks start time, sample hashes, IOCs, and MITRE techniques.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
sample_path: Path to the malware sample to analyze
|
|
113
|
+
analyst: Name of the analyst
|
|
114
|
+
severity: Initial severity (low, medium, high, critical)
|
|
115
|
+
malware_family: Known malware family name
|
|
116
|
+
tags: Comma-separated tags
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Session ID and initial information
|
|
120
|
+
"""
|
|
121
|
+
report_tools = get_report_tools()
|
|
122
|
+
tags_list = [t.strip() for t in tags.split(",") if t.strip()] if tags else None
|
|
123
|
+
result = await report_tools.start_session(
|
|
124
|
+
sample_path=sample_path if sample_path else None,
|
|
125
|
+
analyst=analyst,
|
|
126
|
+
severity=severity,
|
|
127
|
+
malware_family=malware_family if malware_family else None,
|
|
128
|
+
tags=tags_list,
|
|
129
|
+
)
|
|
130
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
async def end_analysis_session(
|
|
134
|
+
session_id: str = "", status: str = "completed", summary: str = ""
|
|
135
|
+
) -> str:
|
|
136
|
+
"""
|
|
137
|
+
End the current analysis session.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
session_id: Session ID to end (uses current if not specified)
|
|
141
|
+
status: Final status - "completed" or "aborted"
|
|
142
|
+
summary: Brief summary of findings
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Session summary with duration and collected data stats
|
|
146
|
+
"""
|
|
147
|
+
report_tools = get_report_tools()
|
|
148
|
+
result = await report_tools.end_session(
|
|
149
|
+
session_id=session_id if session_id else None,
|
|
150
|
+
status=status,
|
|
151
|
+
summary=summary if summary else None,
|
|
152
|
+
)
|
|
153
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
async def get_session_status(session_id: str = "") -> str:
|
|
157
|
+
"""
|
|
158
|
+
Get current session information and collected data.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
session_id: Session ID to query (uses current if not specified)
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Complete session data including IOCs, techniques, notes, duration
|
|
165
|
+
"""
|
|
166
|
+
report_tools = get_report_tools()
|
|
167
|
+
result = await report_tools.get_session_info(session_id=session_id if session_id else None)
|
|
168
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
async def list_analysis_sessions() -> str:
|
|
172
|
+
"""
|
|
173
|
+
List all analysis sessions with their status and duration.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
List of all sessions with summary information
|
|
177
|
+
"""
|
|
178
|
+
report_tools = get_report_tools()
|
|
179
|
+
result = await report_tools.list_sessions()
|
|
180
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
async def add_ioc(ioc_type: str, value: str, session_id: str = "") -> str:
|
|
184
|
+
"""
|
|
185
|
+
Add an Indicator of Compromise to the current session.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
ioc_type: Type of IOC (hashes, ips, domains, urls, files, registry, mutexes, emails)
|
|
189
|
+
value: The IOC value
|
|
190
|
+
session_id: Session ID (uses current if not specified)
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Confirmation with total IOC count
|
|
194
|
+
"""
|
|
195
|
+
report_tools = get_report_tools()
|
|
196
|
+
result = await report_tools.add_session_ioc(
|
|
197
|
+
ioc_type=ioc_type, value=value, session_id=session_id if session_id else None
|
|
198
|
+
)
|
|
199
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
async def add_analysis_note(note: str, category: str = "general", session_id: str = "") -> str:
|
|
203
|
+
"""
|
|
204
|
+
Add a timestamped note to the analysis session.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
note: The analysis note
|
|
208
|
+
category: Note category (general, finding, todo, warning)
|
|
209
|
+
session_id: Session ID (uses current if not specified)
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
Confirmation with timestamped note
|
|
213
|
+
"""
|
|
214
|
+
report_tools = get_report_tools()
|
|
215
|
+
result = await report_tools.add_session_note(
|
|
216
|
+
note=note, category=category, session_id=session_id if session_id else None
|
|
217
|
+
)
|
|
218
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
async def add_mitre_technique(
|
|
222
|
+
technique_id: str, technique_name: str, tactic: str, session_id: str = ""
|
|
223
|
+
) -> str:
|
|
224
|
+
"""
|
|
225
|
+
Add a MITRE ATT&CK technique to the session.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
technique_id: MITRE ID (e.g., "T1055", "T1547.001")
|
|
229
|
+
technique_name: Technique name
|
|
230
|
+
tactic: MITRE tactic (e.g., "Defense Evasion", "Persistence")
|
|
231
|
+
session_id: Session ID (uses current if not specified)
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Confirmation with technique count
|
|
235
|
+
"""
|
|
236
|
+
report_tools = get_report_tools()
|
|
237
|
+
result = await report_tools.add_session_mitre(
|
|
238
|
+
technique_id=technique_id,
|
|
239
|
+
technique_name=technique_name,
|
|
240
|
+
tactic=tactic,
|
|
241
|
+
session_id=session_id if session_id else None,
|
|
242
|
+
)
|
|
243
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
async def set_severity(severity: str, session_id: str = "") -> str:
|
|
247
|
+
"""
|
|
248
|
+
Update the severity level of the analysis.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
severity: Severity level (low, medium, high, critical)
|
|
252
|
+
session_id: Session ID (uses current if not specified)
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
Confirmation with new severity
|
|
256
|
+
"""
|
|
257
|
+
report_tools = get_report_tools()
|
|
258
|
+
result = await report_tools.set_session_severity(
|
|
259
|
+
severity=severity, session_id=session_id if session_id else None
|
|
260
|
+
)
|
|
261
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
async def create_analysis_report(
|
|
265
|
+
template_type: str = "full_analysis",
|
|
266
|
+
session_id: str = "",
|
|
267
|
+
sample_path: str = "",
|
|
268
|
+
analyst: str = "Security Researcher",
|
|
269
|
+
classification: str = "TLP:AMBER",
|
|
270
|
+
output_format: str = "markdown",
|
|
271
|
+
) -> str:
|
|
272
|
+
"""
|
|
273
|
+
Generate a comprehensive analysis report.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
template_type: Report template (full_analysis, executive_summary, ioc_report, quick_scan)
|
|
277
|
+
session_id: Session ID to include data from
|
|
278
|
+
sample_path: Path to sample (if not using session)
|
|
279
|
+
analyst: Analyst name
|
|
280
|
+
classification: Classification level
|
|
281
|
+
output_format: Output format (markdown, json)
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
Generated report content and file path
|
|
285
|
+
"""
|
|
286
|
+
report_tools = get_report_tools()
|
|
287
|
+
result = await report_tools.create_report(
|
|
288
|
+
template_type=template_type,
|
|
289
|
+
session_id=session_id if session_id else None,
|
|
290
|
+
sample_path=sample_path if sample_path else None,
|
|
291
|
+
analyst=analyst,
|
|
292
|
+
classification=classification,
|
|
293
|
+
output_format=output_format,
|
|
294
|
+
)
|
|
295
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# =============================================================================
|
|
299
|
+
# Plugin Class
|
|
300
|
+
# =============================================================================
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class ReportToolsPlugin(Plugin):
|
|
304
|
+
"""Plugin for Report Generation tools."""
|
|
305
|
+
|
|
306
|
+
name = "report_tools"
|
|
307
|
+
description = "Malware analysis report generation tools with session tracking, IOC collection, and email delivery."
|
|
308
|
+
|
|
309
|
+
def __init__(self):
|
|
310
|
+
self._report_tools = None
|
|
311
|
+
|
|
312
|
+
def register(self, mcp_server: Any) -> None:
|
|
313
|
+
"""Register report tools with the MCP server."""
|
|
314
|
+
# Initialize ReportTools singleton
|
|
315
|
+
self._report_tools = get_report_tools()
|
|
316
|
+
|
|
317
|
+
# Register all tools
|
|
318
|
+
mcp_server.tool(get_system_time)
|
|
319
|
+
mcp_server.tool(set_timezone)
|
|
320
|
+
mcp_server.tool(get_timezone_info)
|
|
321
|
+
mcp_server.tool(start_analysis_session)
|
|
322
|
+
mcp_server.tool(end_analysis_session)
|
|
323
|
+
mcp_server.tool(get_session_status)
|
|
324
|
+
mcp_server.tool(list_analysis_sessions)
|
|
325
|
+
mcp_server.tool(add_ioc)
|
|
326
|
+
mcp_server.tool(add_analysis_note)
|
|
327
|
+
mcp_server.tool(add_mitre_technique)
|
|
328
|
+
mcp_server.tool(set_severity)
|
|
329
|
+
mcp_server.tool(create_analysis_report)
|
|
330
|
+
|
|
331
|
+
logger.info(f"Registered {self.name} plugin with 12 report tools")
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
# Legacy function for backward compatibility
|
|
335
|
+
def register_report_tools(
|
|
336
|
+
mcp_server, template_dir: Path | None = None, output_dir: Path | None = None
|
|
337
|
+
):
|
|
338
|
+
"""
|
|
339
|
+
Legacy function for registering report tools.
|
|
340
|
+
Use ReportToolsPlugin class instead for Plugin pattern.
|
|
341
|
+
"""
|
|
342
|
+
plugin = ReportToolsPlugin()
|
|
343
|
+
plugin.register(mcp_server)
|
|
344
|
+
return plugin._report_tools
|