eva-exploit 2.5__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.
modules/reporting.py ADDED
@@ -0,0 +1,254 @@
1
+ import html
2
+ import re
3
+ import subprocess
4
+ import webbrowser
5
+ from datetime import datetime, timezone
6
+ from pathlib import Path
7
+
8
+ SEVERITY_ORDER = {"critical": 4, "high": 3, "medium": 2, "low": 1, "info": 0}
9
+
10
+
11
+ def _first_nonempty_line(text):
12
+ for line in text.splitlines():
13
+ clean = line.strip()
14
+ if clean:
15
+ return clean
16
+ return ""
17
+
18
+
19
+ def _extract_cves(blob):
20
+ seen = []
21
+ for cve in re.findall(r"CVE-\d{4}-\d{4,7}", blob, flags=re.IGNORECASE):
22
+ cve = cve.upper()
23
+ if cve not in seen:
24
+ seen.append(cve)
25
+ return seen
26
+
27
+
28
+ def _findings_from_timeline(timeline):
29
+ findings = []
30
+ all_text = "\n".join(item.get("content", "") for item in timeline if item.get("type") == "analysis")
31
+
32
+ for cve in _extract_cves(all_text):
33
+ findings.append({
34
+ "title": f"Potential exposure related to {cve}",
35
+ "severity": "high",
36
+ "evidence": f"Referenced by AI analysis: {cve}",
37
+ "impact": "Potential compromise if the affected component/version is confirmed on target assets.",
38
+ "recommendation": "Validate affected versions, apply vendor patch guidance, and retest.",
39
+ })
40
+
41
+ keywords = [
42
+ (r"sql\s*injection|sqli", "Potential SQL Injection", "high"),
43
+ (r"remote\s+code\s+execution|\brce\b", "Potential Remote Code Execution", "critical"),
44
+ (r"xss|cross[- ]site scripting", "Potential Cross-Site Scripting", "medium"),
45
+ (r"command\s+injection", "Potential Command Injection", "high"),
46
+ (r"open\s+redirect", "Potential Open Redirect", "low"),
47
+ (r"weak\s+credentials|default\s+credentials", "Weak or Default Credentials", "high"),
48
+ (r"privilege\s+escalation", "Potential Privilege Escalation Path", "high"),
49
+ ]
50
+
51
+ lowered = all_text.lower()
52
+ for pattern, title, sev in keywords:
53
+ if re.search(pattern, lowered):
54
+ findings.append({
55
+ "title": title,
56
+ "severity": sev,
57
+ "evidence": f"Keyword pattern matched in analysis: /{pattern}/",
58
+ "impact": "Could increase the likelihood of unauthorized access or lateral movement.",
59
+ "recommendation": "Perform targeted validation, prioritize remediation by risk, and enforce defense-in-depth controls.",
60
+ })
61
+
62
+ for item in timeline:
63
+ if item.get("type") != "command":
64
+ continue
65
+ cmd = item.get("cmd", "")
66
+ out = item.get("output", "")
67
+
68
+ for port, service in re.findall(r"(\d{1,5})/tcp\s+open\s+([^\s]+)", out):
69
+ findings.append({
70
+ "title": f"Exposed service detected: {service} on TCP/{port}",
71
+ "severity": "info",
72
+ "evidence": f"Command `{cmd}` reported open service {service} on {port}/tcp.",
73
+ "impact": "Open service increases attack surface and may expose known vulnerabilities if outdated or misconfigured.",
74
+ "recommendation": "Confirm necessity, restrict exposure, and patch/harden the service.",
75
+ })
76
+
77
+ # Deduplicate by title
78
+ dedup = {}
79
+ for f in findings:
80
+ dedup[f["title"]] = f
81
+
82
+ ordered = sorted(
83
+ dedup.values(),
84
+ key=lambda x: SEVERITY_ORDER.get(x["severity"], 0),
85
+ reverse=True,
86
+ )
87
+ return ordered[:12]
88
+
89
+
90
+ def _html_findings_rows(findings):
91
+ rows = []
92
+ for idx, f in enumerate(findings, 1):
93
+ rows.append(
94
+ "<tr>"
95
+ f"<td>{idx}</td>"
96
+ f"<td>{html.escape(f['title'])}</td>"
97
+ f"<td><span class='sev sev-{f['severity']}'>{f['severity'].upper()}</span></td>"
98
+ f"<td>{html.escape(f['evidence'])}</td>"
99
+ f"<td>{html.escape(f['impact'])}</td>"
100
+ f"<td>{html.escape(f['recommendation'])}</td>"
101
+ "</tr>"
102
+ )
103
+ return "\n".join(rows)
104
+
105
+
106
+ def _summary_metrics(findings):
107
+ counts = {"critical": 0, "high": 0, "medium": 0, "low": 0, "info": 0}
108
+ for f in findings:
109
+ sev = f.get("severity", "info")
110
+ if sev not in counts:
111
+ sev = "info"
112
+ counts[sev] += 1
113
+ return counts
114
+
115
+
116
+ def build_html_report(session_name, backend, timeline, version="unknown"):
117
+ findings = _findings_from_timeline(timeline)
118
+ metrics = _summary_metrics(findings)
119
+ now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
120
+
121
+ user_inputs = [item.get("content", "") for item in timeline if item.get("type") == "user"]
122
+ scope_hint = _first_nonempty_line("\n".join(user_inputs)) or "Not explicitly defined"
123
+
124
+ executive = "No explicit findings were derived from current session output." if not findings else (
125
+ f"Assessment identified {len(findings)} potential finding(s), with "
126
+ f"{metrics['critical']} critical and {metrics['high']} high-severity item(s)."
127
+ )
128
+
129
+ rows = _html_findings_rows(findings)
130
+
131
+ return f"""<!doctype html>
132
+ <html lang=\"en\">
133
+ <head>
134
+ <meta charset=\"utf-8\" />
135
+ <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />
136
+ <title>EVA Report - {html.escape(session_name)}</title>
137
+ <style>
138
+ :root {{
139
+ --bg:#f4f7fb;
140
+ --text:#172030;
141
+ --muted:#4e5a70;
142
+ --line:#d8e0ee;
143
+ --brand:#0b5fff;
144
+ --critical:#7f1d1d;
145
+ --high:#b45309;
146
+ --medium:#92400e;
147
+ --low:#1f6d35;
148
+ --info:#0f4c81;
149
+ }}
150
+ * {{ box-sizing:border-box; }}
151
+ body {{ margin:0; font-family: "IBM Plex Sans", "Segoe UI", sans-serif; background:var(--bg); color:var(--text); }}
152
+ .cover {{ min-height:100vh; display:grid; place-items:center; padding:32px; background:linear-gradient(135deg,#e8f0ff,#fefcff 50%,#edf7ff); }}
153
+ .cover-card {{ width:min(980px,100%); border:1px solid var(--line); border-radius:16px; background:#fff; padding:32px; box-shadow:0 20px 60px rgba(15,35,80,.08); }}
154
+ .eyebrow {{ color:var(--brand); font-weight:700; letter-spacing:.12em; text-transform:uppercase; font-size:12px; }}
155
+ h1 {{ margin:6px 0 8px; font-size:36px; line-height:1.1; }}
156
+ .meta {{ color:var(--muted); }}
157
+ main {{ width:min(1200px,100%); margin:0 auto; padding:24px 16px 40px; }}
158
+ .section {{ background:#fff; border:1px solid var(--line); border-radius:14px; padding:20px; margin:14px 0; }}
159
+ .grid {{ display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:10px; }}
160
+ .metric {{ border:1px solid var(--line); border-radius:10px; padding:10px; text-align:center; }}
161
+ .metric strong {{ display:block; font-size:24px; }}
162
+ table {{ width:100%; border-collapse:collapse; font-size:14px; }}
163
+ th, td {{ border:1px solid var(--line); padding:10px; vertical-align:top; text-align:left; }}
164
+ th {{ background:#f7f9fe; }}
165
+ .sev {{ display:inline-block; padding:4px 8px; border-radius:999px; font-size:12px; font-weight:700; }}
166
+ .sev-critical {{ background:#fee2e2; color:var(--critical); }}
167
+ .sev-high {{ background:#ffedd5; color:var(--high); }}
168
+ .sev-medium {{ background:#fef3c7; color:var(--medium); }}
169
+ .sev-low {{ background:#dcfce7; color:var(--low); }}
170
+ .sev-info {{ background:#dbeafe; color:var(--info); }}
171
+ .footer {{ text-align:center; color:var(--muted); font-size:12px; margin:18px 0 8px; }}
172
+ @media (max-width: 900px) {{ .grid {{ grid-template-columns:repeat(2,minmax(0,1fr)); }} }}
173
+ </style>
174
+ </head>
175
+ <body>
176
+ <section class=\"cover\">
177
+ <div class=\"cover-card\">
178
+ <div class=\"eyebrow\">EVA Vulnerability Assessment Report</div>
179
+ <h1>Offensive Security Assessment</h1>
180
+ <p class=\"meta\">Session: <strong>{html.escape(session_name)}</strong><br/>Generated: <strong>{now}</strong><br/>AI Backend: <strong>{html.escape(backend)}</strong></p>
181
+ <p>This report follows a concise structure aligned with PTES and NIST SP 800-115 reporting expectations and OWASP-style risk communication.</p>
182
+ </div>
183
+ </section>
184
+
185
+ <main>
186
+ <section class=\"section\">
187
+ <h2>Executive Summary</h2>
188
+ <p>{html.escape(executive)}</p>
189
+ </section>
190
+
191
+ <section class=\"section\">
192
+ <h2>Scope and Methodology</h2>
193
+ <p><strong>Scope hint:</strong> {html.escape(scope_hint)}</p>
194
+ <p>Methodology: Reconnaissance, analysis, validation, and remediation guidance, mapped to common VAPT reporting structure.</p>
195
+ </section>
196
+
197
+ <section class=\"section\">
198
+ <h2>Risk Summary</h2>
199
+ <div class=\"grid\">
200
+ <div class=\"metric\"><span>Critical</span><strong>{metrics['critical']}</strong></div>
201
+ <div class=\"metric\"><span>High</span><strong>{metrics['high']}</strong></div>
202
+ <div class=\"metric\"><span>Medium</span><strong>{metrics['medium']}</strong></div>
203
+ <div class=\"metric\"><span>Low</span><strong>{metrics['low']}</strong></div>
204
+ <div class=\"metric\"><span>Info</span><strong>{metrics['info']}</strong></div>
205
+ </div>
206
+ </section>
207
+
208
+ <section class=\"section\">
209
+ <h2>Findings</h2>
210
+ <table>
211
+ <thead>
212
+ <tr>
213
+ <th>#</th>
214
+ <th>Finding</th>
215
+ <th>Severity</th>
216
+ <th>Evidence</th>
217
+ <th>Impact</th>
218
+ <th>Recommendation</th>
219
+ </tr>
220
+ </thead>
221
+ <tbody>
222
+ {rows if rows else '<tr><td colspan="6">No findings extracted yet. Continue the session and regenerate.</td></tr>'}
223
+ </tbody>
224
+ </table>
225
+ </section>
226
+
227
+ <section class=\"section\">
228
+ <h2>Conclusion</h2>
229
+ <p>Use this report as a working artifact. Validate each potential issue manually before formal risk acceptance or production remediation.</p>
230
+ </section>
231
+ <div class=\"footer\">EVA v{html.escape(str(version))}<br/>Made by: Arcangelo</div>
232
+ </main>
233
+ </body>
234
+ </html>
235
+ """
236
+
237
+
238
+ def try_generate_pdf(html_path, pdf_path):
239
+ try:
240
+ result = subprocess.run(
241
+ ["wkhtmltopdf", "--quiet", str(html_path), str(pdf_path)],
242
+ capture_output=True,
243
+ text=True,
244
+ )
245
+ return result.returncode == 0
246
+ except FileNotFoundError:
247
+ return False
248
+
249
+
250
+ def open_report_file(path):
251
+ file_path = Path(path).expanduser().resolve()
252
+ if not file_path.exists():
253
+ return False
254
+ return webbrowser.open(file_path.as_uri())
sessions/__init__.py ADDED
File without changes