aiptx 2.0.2__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.
Potentially problematic release.
This version of aiptx might be problematic. Click here for more details.
- aipt_v2/__init__.py +110 -0
- aipt_v2/__main__.py +24 -0
- aipt_v2/agents/AIPTxAgent/__init__.py +10 -0
- aipt_v2/agents/AIPTxAgent/aiptx_agent.py +211 -0
- aipt_v2/agents/__init__.py +24 -0
- aipt_v2/agents/base.py +520 -0
- aipt_v2/agents/ptt.py +406 -0
- aipt_v2/agents/state.py +168 -0
- aipt_v2/app.py +960 -0
- aipt_v2/browser/__init__.py +31 -0
- aipt_v2/browser/automation.py +458 -0
- aipt_v2/browser/crawler.py +453 -0
- aipt_v2/cli.py +321 -0
- aipt_v2/compliance/__init__.py +71 -0
- aipt_v2/compliance/compliance_report.py +449 -0
- aipt_v2/compliance/framework_mapper.py +424 -0
- aipt_v2/compliance/nist_mapping.py +345 -0
- aipt_v2/compliance/owasp_mapping.py +330 -0
- aipt_v2/compliance/pci_mapping.py +297 -0
- aipt_v2/config.py +288 -0
- aipt_v2/core/__init__.py +43 -0
- aipt_v2/core/agent.py +630 -0
- aipt_v2/core/llm.py +395 -0
- aipt_v2/core/memory.py +305 -0
- aipt_v2/core/ptt.py +329 -0
- aipt_v2/database/__init__.py +14 -0
- aipt_v2/database/models.py +232 -0
- aipt_v2/database/repository.py +384 -0
- aipt_v2/docker/__init__.py +23 -0
- aipt_v2/docker/builder.py +260 -0
- aipt_v2/docker/manager.py +222 -0
- aipt_v2/docker/sandbox.py +371 -0
- aipt_v2/evasion/__init__.py +58 -0
- aipt_v2/evasion/request_obfuscator.py +272 -0
- aipt_v2/evasion/tls_fingerprint.py +285 -0
- aipt_v2/evasion/ua_rotator.py +301 -0
- aipt_v2/evasion/waf_bypass.py +439 -0
- aipt_v2/execution/__init__.py +23 -0
- aipt_v2/execution/executor.py +302 -0
- aipt_v2/execution/parser.py +544 -0
- aipt_v2/execution/terminal.py +337 -0
- aipt_v2/health.py +437 -0
- aipt_v2/intelligence/__init__.py +85 -0
- aipt_v2/intelligence/auth.py +520 -0
- aipt_v2/intelligence/chaining.py +775 -0
- aipt_v2/intelligence/cve_aipt.py +334 -0
- aipt_v2/intelligence/cve_info.py +1111 -0
- aipt_v2/intelligence/rag.py +239 -0
- aipt_v2/intelligence/scope.py +442 -0
- aipt_v2/intelligence/searchers/__init__.py +5 -0
- aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
- aipt_v2/intelligence/searchers/github_searcher.py +467 -0
- aipt_v2/intelligence/searchers/google_searcher.py +281 -0
- aipt_v2/intelligence/tools.json +443 -0
- aipt_v2/intelligence/triage.py +670 -0
- aipt_v2/interface/__init__.py +5 -0
- aipt_v2/interface/cli.py +230 -0
- aipt_v2/interface/main.py +501 -0
- aipt_v2/interface/tui.py +1276 -0
- aipt_v2/interface/utils.py +583 -0
- aipt_v2/llm/__init__.py +39 -0
- aipt_v2/llm/config.py +26 -0
- aipt_v2/llm/llm.py +514 -0
- aipt_v2/llm/memory.py +214 -0
- aipt_v2/llm/request_queue.py +89 -0
- aipt_v2/llm/utils.py +89 -0
- aipt_v2/models/__init__.py +15 -0
- aipt_v2/models/findings.py +295 -0
- aipt_v2/models/phase_result.py +224 -0
- aipt_v2/models/scan_config.py +207 -0
- aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
- aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
- aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
- aipt_v2/monitoring/prometheus.yml +60 -0
- aipt_v2/orchestration/__init__.py +52 -0
- aipt_v2/orchestration/pipeline.py +398 -0
- aipt_v2/orchestration/progress.py +300 -0
- aipt_v2/orchestration/scheduler.py +296 -0
- aipt_v2/orchestrator.py +2284 -0
- aipt_v2/payloads/__init__.py +27 -0
- aipt_v2/payloads/cmdi.py +150 -0
- aipt_v2/payloads/sqli.py +263 -0
- aipt_v2/payloads/ssrf.py +204 -0
- aipt_v2/payloads/templates.py +222 -0
- aipt_v2/payloads/traversal.py +166 -0
- aipt_v2/payloads/xss.py +204 -0
- aipt_v2/prompts/__init__.py +60 -0
- aipt_v2/proxy/__init__.py +29 -0
- aipt_v2/proxy/history.py +352 -0
- aipt_v2/proxy/interceptor.py +452 -0
- aipt_v2/recon/__init__.py +44 -0
- aipt_v2/recon/dns.py +241 -0
- aipt_v2/recon/osint.py +367 -0
- aipt_v2/recon/subdomain.py +372 -0
- aipt_v2/recon/tech_detect.py +311 -0
- aipt_v2/reports/__init__.py +17 -0
- aipt_v2/reports/generator.py +313 -0
- aipt_v2/reports/html_report.py +378 -0
- aipt_v2/runtime/__init__.py +44 -0
- aipt_v2/runtime/base.py +30 -0
- aipt_v2/runtime/docker.py +401 -0
- aipt_v2/runtime/local.py +346 -0
- aipt_v2/runtime/tool_server.py +205 -0
- aipt_v2/scanners/__init__.py +28 -0
- aipt_v2/scanners/base.py +273 -0
- aipt_v2/scanners/nikto.py +244 -0
- aipt_v2/scanners/nmap.py +402 -0
- aipt_v2/scanners/nuclei.py +273 -0
- aipt_v2/scanners/web.py +454 -0
- aipt_v2/scripts/security_audit.py +366 -0
- aipt_v2/telemetry/__init__.py +7 -0
- aipt_v2/telemetry/tracer.py +347 -0
- aipt_v2/terminal/__init__.py +28 -0
- aipt_v2/terminal/executor.py +400 -0
- aipt_v2/terminal/sandbox.py +350 -0
- aipt_v2/tools/__init__.py +44 -0
- aipt_v2/tools/active_directory/__init__.py +78 -0
- aipt_v2/tools/active_directory/ad_config.py +238 -0
- aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
- aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
- aipt_v2/tools/active_directory/ldap_enum.py +533 -0
- aipt_v2/tools/active_directory/smb_attacks.py +505 -0
- aipt_v2/tools/agents_graph/__init__.py +19 -0
- aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
- aipt_v2/tools/api_security/__init__.py +76 -0
- aipt_v2/tools/api_security/api_discovery.py +608 -0
- aipt_v2/tools/api_security/graphql_scanner.py +622 -0
- aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
- aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
- aipt_v2/tools/browser/__init__.py +5 -0
- aipt_v2/tools/browser/browser_actions.py +238 -0
- aipt_v2/tools/browser/browser_instance.py +535 -0
- aipt_v2/tools/browser/tab_manager.py +344 -0
- aipt_v2/tools/cloud/__init__.py +70 -0
- aipt_v2/tools/cloud/cloud_config.py +273 -0
- aipt_v2/tools/cloud/cloud_scanner.py +639 -0
- aipt_v2/tools/cloud/prowler_tool.py +571 -0
- aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
- aipt_v2/tools/executor.py +307 -0
- aipt_v2/tools/parser.py +408 -0
- aipt_v2/tools/proxy/__init__.py +5 -0
- aipt_v2/tools/proxy/proxy_actions.py +103 -0
- aipt_v2/tools/proxy/proxy_manager.py +789 -0
- aipt_v2/tools/registry.py +196 -0
- aipt_v2/tools/scanners/__init__.py +343 -0
- aipt_v2/tools/scanners/acunetix_tool.py +712 -0
- aipt_v2/tools/scanners/burp_tool.py +631 -0
- aipt_v2/tools/scanners/config.py +156 -0
- aipt_v2/tools/scanners/nessus_tool.py +588 -0
- aipt_v2/tools/scanners/zap_tool.py +612 -0
- aipt_v2/tools/terminal/__init__.py +5 -0
- aipt_v2/tools/terminal/terminal_actions.py +37 -0
- aipt_v2/tools/terminal/terminal_manager.py +153 -0
- aipt_v2/tools/terminal/terminal_session.py +449 -0
- aipt_v2/tools/tool_processing.py +108 -0
- aipt_v2/utils/__init__.py +17 -0
- aipt_v2/utils/logging.py +201 -0
- aipt_v2/utils/model_manager.py +187 -0
- aipt_v2/utils/searchers/__init__.py +269 -0
- aiptx-2.0.2.dist-info/METADATA +324 -0
- aiptx-2.0.2.dist-info/RECORD +165 -0
- aiptx-2.0.2.dist-info/WHEEL +5 -0
- aiptx-2.0.2.dist-info/entry_points.txt +7 -0
- aiptx-2.0.2.dist-info/licenses/LICENSE +21 -0
- aiptx-2.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPT CVE Intelligence - Vulnerability prioritization and exploit search
|
|
3
|
+
Prioritizes CVEs by actual exploitability, not just CVSS.
|
|
4
|
+
|
|
5
|
+
Inspired by: pentest-agent's CVE scoring formula
|
|
6
|
+
Score = 0.3*CVSS + 0.3*EPSS + 0.2*trending + 0.2*has_poc
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import json
|
|
11
|
+
import time
|
|
12
|
+
import logging
|
|
13
|
+
from typing import Optional
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import requests
|
|
18
|
+
|
|
19
|
+
# Logger for CVE intelligence module
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class CVEInfo:
|
|
25
|
+
"""Structured CVE information"""
|
|
26
|
+
cve_id: str
|
|
27
|
+
cvss: float = 0.0
|
|
28
|
+
epss: float = 0.0
|
|
29
|
+
description: str = ""
|
|
30
|
+
affected_products: list[str] = field(default_factory=list)
|
|
31
|
+
references: list[str] = field(default_factory=list)
|
|
32
|
+
exploit_urls: list[str] = field(default_factory=list)
|
|
33
|
+
is_trending: bool = False
|
|
34
|
+
has_poc: bool = False
|
|
35
|
+
priority_score: float = 0.0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CVEIntelligence:
|
|
39
|
+
"""
|
|
40
|
+
CVE intelligence and prioritization.
|
|
41
|
+
|
|
42
|
+
Features:
|
|
43
|
+
- Multi-source CVE data (CVEMap, NVD)
|
|
44
|
+
- EPSS scoring for exploit probability
|
|
45
|
+
- POC/exploit detection
|
|
46
|
+
- Caching for performance
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
# Scoring weights (from pentest-agent)
|
|
50
|
+
WEIGHT_CVSS = 0.3
|
|
51
|
+
WEIGHT_EPSS = 0.3
|
|
52
|
+
WEIGHT_TRENDING = 0.2
|
|
53
|
+
WEIGHT_HAS_POC = 0.2
|
|
54
|
+
|
|
55
|
+
# API endpoints
|
|
56
|
+
CVEMAP_API = "https://cvedb.shodan.io/cve/{cve_id}"
|
|
57
|
+
NVD_API = "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId={cve_id}"
|
|
58
|
+
EPSS_API = "https://api.first.org/data/v1/epss?cve={cve_id}"
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
cache_dir: Optional[str] = None,
|
|
63
|
+
cache_hours: int = 24,
|
|
64
|
+
):
|
|
65
|
+
self.cache_dir = Path(cache_dir or os.path.expanduser("~/.aipt/cve_cache"))
|
|
66
|
+
self.cache_hours = cache_hours
|
|
67
|
+
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
68
|
+
|
|
69
|
+
def lookup(self, cve_id: str) -> CVEInfo:
|
|
70
|
+
"""
|
|
71
|
+
Look up CVE information.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
cve_id: CVE identifier (e.g., "CVE-2024-1234")
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
CVEInfo with all available data
|
|
78
|
+
"""
|
|
79
|
+
# Normalize CVE ID
|
|
80
|
+
cve_id = cve_id.upper()
|
|
81
|
+
if not cve_id.startswith("CVE-"):
|
|
82
|
+
cve_id = f"CVE-{cve_id}"
|
|
83
|
+
|
|
84
|
+
# Check cache
|
|
85
|
+
cached = self._get_cached(cve_id)
|
|
86
|
+
if cached:
|
|
87
|
+
return cached
|
|
88
|
+
|
|
89
|
+
# Fetch fresh data
|
|
90
|
+
info = self._fetch_cve(cve_id)
|
|
91
|
+
|
|
92
|
+
# Calculate priority score
|
|
93
|
+
info.priority_score = self._calculate_priority(info)
|
|
94
|
+
|
|
95
|
+
# Cache result
|
|
96
|
+
self._cache(cve_id, info)
|
|
97
|
+
|
|
98
|
+
return info
|
|
99
|
+
|
|
100
|
+
def prioritize(self, cve_ids: list[str]) -> list[CVEInfo]:
|
|
101
|
+
"""
|
|
102
|
+
Prioritize a list of CVEs by exploitability.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
cve_ids: List of CVE identifiers
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
List of CVEInfo sorted by priority score (highest first)
|
|
109
|
+
"""
|
|
110
|
+
results = []
|
|
111
|
+
|
|
112
|
+
for cve_id in cve_ids:
|
|
113
|
+
try:
|
|
114
|
+
info = self.lookup(cve_id)
|
|
115
|
+
results.append(info)
|
|
116
|
+
except Exception as e:
|
|
117
|
+
# Create minimal info for failed lookups
|
|
118
|
+
results.append(CVEInfo(
|
|
119
|
+
cve_id=cve_id,
|
|
120
|
+
description=f"Lookup failed: {e}",
|
|
121
|
+
))
|
|
122
|
+
|
|
123
|
+
# Sort by priority score (descending)
|
|
124
|
+
results.sort(key=lambda x: x.priority_score, reverse=True)
|
|
125
|
+
|
|
126
|
+
return results
|
|
127
|
+
|
|
128
|
+
def search_exploits(self, cve_id: str) -> list[str]:
|
|
129
|
+
"""
|
|
130
|
+
Search for public exploits for a CVE.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
cve_id: CVE identifier
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
List of exploit URLs
|
|
137
|
+
"""
|
|
138
|
+
exploits = []
|
|
139
|
+
|
|
140
|
+
# Search GitHub
|
|
141
|
+
github_exploits = self._search_github_exploits(cve_id)
|
|
142
|
+
exploits.extend(github_exploits)
|
|
143
|
+
|
|
144
|
+
# Search ExploitDB
|
|
145
|
+
edb_exploits = self._search_exploitdb(cve_id)
|
|
146
|
+
exploits.extend(edb_exploits)
|
|
147
|
+
|
|
148
|
+
return list(set(exploits)) # Deduplicate
|
|
149
|
+
|
|
150
|
+
def _fetch_cve(self, cve_id: str) -> CVEInfo:
|
|
151
|
+
"""Fetch CVE data from multiple sources"""
|
|
152
|
+
info = CVEInfo(cve_id=cve_id)
|
|
153
|
+
|
|
154
|
+
# Try CVEMap (Shodan) first - faster
|
|
155
|
+
try:
|
|
156
|
+
resp = requests.get(
|
|
157
|
+
self.CVEMAP_API.format(cve_id=cve_id),
|
|
158
|
+
timeout=10,
|
|
159
|
+
)
|
|
160
|
+
if resp.ok:
|
|
161
|
+
data = resp.json()
|
|
162
|
+
info.cvss = float(data.get("cvss", 0) or 0)
|
|
163
|
+
info.description = data.get("summary", "")
|
|
164
|
+
info.references = data.get("references", [])
|
|
165
|
+
info.is_trending = data.get("is_trending", False)
|
|
166
|
+
|
|
167
|
+
# Check for POC
|
|
168
|
+
refs_str = " ".join(info.references).lower()
|
|
169
|
+
info.has_poc = any(kw in refs_str for kw in [
|
|
170
|
+
"exploit", "poc", "github.com", "exploit-db"
|
|
171
|
+
])
|
|
172
|
+
except Exception as e:
|
|
173
|
+
logger.debug("CVEMap lookup failed for %s: %s", cve_id, str(e))
|
|
174
|
+
|
|
175
|
+
# Get EPSS score
|
|
176
|
+
try:
|
|
177
|
+
resp = requests.get(
|
|
178
|
+
self.EPSS_API.format(cve_id=cve_id),
|
|
179
|
+
timeout=10,
|
|
180
|
+
)
|
|
181
|
+
if resp.ok:
|
|
182
|
+
data = resp.json()
|
|
183
|
+
if data.get("data"):
|
|
184
|
+
info.epss = float(data["data"][0].get("epss", 0))
|
|
185
|
+
except Exception as e:
|
|
186
|
+
logger.debug("EPSS lookup failed for %s: %s", cve_id, str(e))
|
|
187
|
+
|
|
188
|
+
# Fallback to NVD if no CVSS
|
|
189
|
+
if info.cvss == 0:
|
|
190
|
+
try:
|
|
191
|
+
resp = requests.get(
|
|
192
|
+
self.NVD_API.format(cve_id=cve_id),
|
|
193
|
+
timeout=15,
|
|
194
|
+
)
|
|
195
|
+
if resp.ok:
|
|
196
|
+
data = resp.json()
|
|
197
|
+
vulns = data.get("vulnerabilities", [])
|
|
198
|
+
if vulns:
|
|
199
|
+
cve_data = vulns[0].get("cve", {})
|
|
200
|
+
metrics = cve_data.get("metrics", {})
|
|
201
|
+
|
|
202
|
+
# Try CVSS v3.1
|
|
203
|
+
if "cvssMetricV31" in metrics:
|
|
204
|
+
info.cvss = metrics["cvssMetricV31"][0]["cvssData"]["baseScore"]
|
|
205
|
+
elif "cvssMetricV30" in metrics:
|
|
206
|
+
info.cvss = metrics["cvssMetricV30"][0]["cvssData"]["baseScore"]
|
|
207
|
+
elif "cvssMetricV2" in metrics:
|
|
208
|
+
info.cvss = metrics["cvssMetricV2"][0]["cvssData"]["baseScore"]
|
|
209
|
+
|
|
210
|
+
# Description
|
|
211
|
+
descriptions = cve_data.get("descriptions", [])
|
|
212
|
+
for desc in descriptions:
|
|
213
|
+
if desc.get("lang") == "en":
|
|
214
|
+
info.description = desc.get("value", "")
|
|
215
|
+
break
|
|
216
|
+
except Exception as e:
|
|
217
|
+
logger.debug("NVD lookup failed for %s: %s", cve_id, str(e))
|
|
218
|
+
|
|
219
|
+
# Search for exploits
|
|
220
|
+
info.exploit_urls = self.search_exploits(cve_id)
|
|
221
|
+
if info.exploit_urls:
|
|
222
|
+
info.has_poc = True
|
|
223
|
+
|
|
224
|
+
return info
|
|
225
|
+
|
|
226
|
+
def calculate_priority(
|
|
227
|
+
self,
|
|
228
|
+
cvss: float = 0.0,
|
|
229
|
+
epss: float = 0.0,
|
|
230
|
+
trending: bool = False,
|
|
231
|
+
has_poc: bool = False,
|
|
232
|
+
) -> float:
|
|
233
|
+
"""
|
|
234
|
+
Calculate priority score using pentest-agent formula.
|
|
235
|
+
|
|
236
|
+
Score = 0.3*CVSS + 0.3*EPSS + 0.2*trending + 0.2*has_poc
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
cvss: CVSS score (0-10)
|
|
240
|
+
epss: EPSS score (0-1)
|
|
241
|
+
trending: Whether CVE is trending
|
|
242
|
+
has_poc: Whether POC exists
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
Priority score (0-1)
|
|
246
|
+
"""
|
|
247
|
+
cvss_normalized = cvss / 10.0 # CVSS is 0-10
|
|
248
|
+
epss_normalized = epss # EPSS is already 0-1
|
|
249
|
+
trending_score = 1.0 if trending else 0.0
|
|
250
|
+
poc_score = 1.0 if has_poc else 0.0
|
|
251
|
+
|
|
252
|
+
score = (
|
|
253
|
+
self.WEIGHT_CVSS * cvss_normalized +
|
|
254
|
+
self.WEIGHT_EPSS * epss_normalized +
|
|
255
|
+
self.WEIGHT_TRENDING * trending_score +
|
|
256
|
+
self.WEIGHT_HAS_POC * poc_score
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
return round(score, 4)
|
|
260
|
+
|
|
261
|
+
def _calculate_priority(self, info: CVEInfo) -> float:
|
|
262
|
+
"""Internal method using CVEInfo object"""
|
|
263
|
+
return self.calculate_priority(
|
|
264
|
+
cvss=info.cvss,
|
|
265
|
+
epss=info.epss,
|
|
266
|
+
trending=info.is_trending,
|
|
267
|
+
has_poc=info.has_poc,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def _search_github_exploits(self, cve_id: str) -> list[str]:
|
|
271
|
+
"""Search GitHub for exploits"""
|
|
272
|
+
exploits = []
|
|
273
|
+
try:
|
|
274
|
+
# GitHub code search API (requires auth for higher rate limits)
|
|
275
|
+
search_url = f"https://api.github.com/search/repositories?q={cve_id}+exploit&per_page=5"
|
|
276
|
+
resp = requests.get(search_url, timeout=10)
|
|
277
|
+
if resp.ok:
|
|
278
|
+
data = resp.json()
|
|
279
|
+
for item in data.get("items", [])[:5]:
|
|
280
|
+
exploits.append(item.get("html_url", ""))
|
|
281
|
+
except Exception as e:
|
|
282
|
+
logger.debug("GitHub exploit search failed for %s: %s", cve_id, str(e))
|
|
283
|
+
return exploits
|
|
284
|
+
|
|
285
|
+
def _search_exploitdb(self, cve_id: str) -> list[str]:
|
|
286
|
+
"""Search ExploitDB for exploits"""
|
|
287
|
+
exploits = []
|
|
288
|
+
try:
|
|
289
|
+
# ExploitDB search
|
|
290
|
+
search_url = f"https://www.exploit-db.com/search?cve={cve_id}"
|
|
291
|
+
exploits.append(search_url) # Return search URL as reference
|
|
292
|
+
except Exception as e:
|
|
293
|
+
logger.debug("ExploitDB search failed for %s: %s", cve_id, str(e))
|
|
294
|
+
return exploits
|
|
295
|
+
|
|
296
|
+
def _get_cached(self, cve_id: str) -> Optional[CVEInfo]:
|
|
297
|
+
"""Get cached CVE data if valid"""
|
|
298
|
+
cache_file = self.cache_dir / f"{cve_id}.json"
|
|
299
|
+
|
|
300
|
+
if not cache_file.exists():
|
|
301
|
+
return None
|
|
302
|
+
|
|
303
|
+
# Check age
|
|
304
|
+
age_hours = (time.time() - cache_file.stat().st_mtime) / 3600
|
|
305
|
+
if age_hours > self.cache_hours:
|
|
306
|
+
return None
|
|
307
|
+
|
|
308
|
+
try:
|
|
309
|
+
with open(cache_file, "r") as f:
|
|
310
|
+
data = json.load(f)
|
|
311
|
+
return CVEInfo(**data)
|
|
312
|
+
except Exception:
|
|
313
|
+
return None
|
|
314
|
+
|
|
315
|
+
def _cache(self, cve_id: str, info: CVEInfo) -> None:
|
|
316
|
+
"""Cache CVE data"""
|
|
317
|
+
cache_file = self.cache_dir / f"{cve_id}.json"
|
|
318
|
+
|
|
319
|
+
try:
|
|
320
|
+
with open(cache_file, "w") as f:
|
|
321
|
+
json.dump({
|
|
322
|
+
"cve_id": info.cve_id,
|
|
323
|
+
"cvss": info.cvss,
|
|
324
|
+
"epss": info.epss,
|
|
325
|
+
"description": info.description,
|
|
326
|
+
"affected_products": info.affected_products,
|
|
327
|
+
"references": info.references,
|
|
328
|
+
"exploit_urls": info.exploit_urls,
|
|
329
|
+
"is_trending": info.is_trending,
|
|
330
|
+
"has_poc": info.has_poc,
|
|
331
|
+
"priority_score": info.priority_score,
|
|
332
|
+
}, f)
|
|
333
|
+
except Exception as e:
|
|
334
|
+
logger.debug("Failed to cache CVE data for %s: %s", info.cve_id, str(e))
|