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.
Files changed (79) hide show
  1. iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/METADATA +543 -0
  2. iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/RECORD +79 -0
  3. iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/WHEEL +5 -0
  4. iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/entry_points.txt +2 -0
  5. iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/licenses/LICENSE +21 -0
  6. iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/top_level.txt +1 -0
  7. reversecore_mcp/__init__.py +9 -0
  8. reversecore_mcp/core/__init__.py +78 -0
  9. reversecore_mcp/core/audit.py +101 -0
  10. reversecore_mcp/core/binary_cache.py +138 -0
  11. reversecore_mcp/core/command_spec.py +357 -0
  12. reversecore_mcp/core/config.py +432 -0
  13. reversecore_mcp/core/container.py +288 -0
  14. reversecore_mcp/core/decorators.py +152 -0
  15. reversecore_mcp/core/error_formatting.py +93 -0
  16. reversecore_mcp/core/error_handling.py +142 -0
  17. reversecore_mcp/core/evidence.py +229 -0
  18. reversecore_mcp/core/exceptions.py +296 -0
  19. reversecore_mcp/core/execution.py +240 -0
  20. reversecore_mcp/core/ghidra.py +642 -0
  21. reversecore_mcp/core/ghidra_helper.py +481 -0
  22. reversecore_mcp/core/ghidra_manager.py +234 -0
  23. reversecore_mcp/core/json_utils.py +131 -0
  24. reversecore_mcp/core/loader.py +73 -0
  25. reversecore_mcp/core/logging_config.py +206 -0
  26. reversecore_mcp/core/memory.py +721 -0
  27. reversecore_mcp/core/metrics.py +198 -0
  28. reversecore_mcp/core/mitre_mapper.py +365 -0
  29. reversecore_mcp/core/plugin.py +45 -0
  30. reversecore_mcp/core/r2_helpers.py +404 -0
  31. reversecore_mcp/core/r2_pool.py +403 -0
  32. reversecore_mcp/core/report_generator.py +268 -0
  33. reversecore_mcp/core/resilience.py +252 -0
  34. reversecore_mcp/core/resource_manager.py +169 -0
  35. reversecore_mcp/core/result.py +132 -0
  36. reversecore_mcp/core/security.py +213 -0
  37. reversecore_mcp/core/validators.py +238 -0
  38. reversecore_mcp/dashboard/__init__.py +221 -0
  39. reversecore_mcp/prompts/__init__.py +56 -0
  40. reversecore_mcp/prompts/common.py +24 -0
  41. reversecore_mcp/prompts/game.py +280 -0
  42. reversecore_mcp/prompts/malware.py +1219 -0
  43. reversecore_mcp/prompts/report.py +150 -0
  44. reversecore_mcp/prompts/security.py +136 -0
  45. reversecore_mcp/resources.py +329 -0
  46. reversecore_mcp/server.py +727 -0
  47. reversecore_mcp/tools/__init__.py +49 -0
  48. reversecore_mcp/tools/analysis/__init__.py +74 -0
  49. reversecore_mcp/tools/analysis/capa_tools.py +215 -0
  50. reversecore_mcp/tools/analysis/die_tools.py +180 -0
  51. reversecore_mcp/tools/analysis/diff_tools.py +643 -0
  52. reversecore_mcp/tools/analysis/lief_tools.py +272 -0
  53. reversecore_mcp/tools/analysis/signature_tools.py +591 -0
  54. reversecore_mcp/tools/analysis/static_analysis.py +479 -0
  55. reversecore_mcp/tools/common/__init__.py +58 -0
  56. reversecore_mcp/tools/common/file_operations.py +352 -0
  57. reversecore_mcp/tools/common/memory_tools.py +516 -0
  58. reversecore_mcp/tools/common/patch_explainer.py +230 -0
  59. reversecore_mcp/tools/common/server_tools.py +115 -0
  60. reversecore_mcp/tools/ghidra/__init__.py +19 -0
  61. reversecore_mcp/tools/ghidra/decompilation.py +975 -0
  62. reversecore_mcp/tools/ghidra/ghidra_tools.py +1052 -0
  63. reversecore_mcp/tools/malware/__init__.py +61 -0
  64. reversecore_mcp/tools/malware/adaptive_vaccine.py +579 -0
  65. reversecore_mcp/tools/malware/dormant_detector.py +756 -0
  66. reversecore_mcp/tools/malware/ioc_tools.py +228 -0
  67. reversecore_mcp/tools/malware/vulnerability_hunter.py +519 -0
  68. reversecore_mcp/tools/malware/yara_tools.py +214 -0
  69. reversecore_mcp/tools/patch_explainer.py +19 -0
  70. reversecore_mcp/tools/radare2/__init__.py +13 -0
  71. reversecore_mcp/tools/radare2/r2_analysis.py +972 -0
  72. reversecore_mcp/tools/radare2/r2_session.py +376 -0
  73. reversecore_mcp/tools/radare2/radare2_mcp_tools.py +1183 -0
  74. reversecore_mcp/tools/report/__init__.py +4 -0
  75. reversecore_mcp/tools/report/email.py +82 -0
  76. reversecore_mcp/tools/report/report_mcp_tools.py +344 -0
  77. reversecore_mcp/tools/report/report_tools.py +1076 -0
  78. reversecore_mcp/tools/report/session.py +194 -0
  79. reversecore_mcp/tools/report_tools.py +11 -0
@@ -0,0 +1,4 @@
1
+ """Report generation tools package."""
2
+ from reversecore_mcp.tools.report.report_mcp_tools import ReportToolsPlugin
3
+
4
+ __all__ = ["ReportToolsPlugin"]
@@ -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