aiptx 2.0.7__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.
- 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 +46 -0
- aipt_v2/agents/base.py +520 -0
- aipt_v2/agents/exploit_agent.py +688 -0
- aipt_v2/agents/ptt.py +406 -0
- aipt_v2/agents/state.py +168 -0
- aipt_v2/app.py +957 -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 +2933 -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 +341 -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 +194 -0
- aipt_v2/intelligence/adaptation.py +474 -0
- aipt_v2/intelligence/auth.py +520 -0
- aipt_v2/intelligence/chaining.py +775 -0
- aipt_v2/intelligence/correlation.py +536 -0
- aipt_v2/intelligence/cve_aipt.py +334 -0
- aipt_v2/intelligence/cve_info.py +1111 -0
- aipt_v2/intelligence/knowledge_graph.py +590 -0
- aipt_v2/intelligence/learning.py +626 -0
- aipt_v2/intelligence/llm_analyzer.py +502 -0
- aipt_v2/intelligence/llm_tool_selector.py +518 -0
- aipt_v2/intelligence/payload_generator.py +562 -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/interactive_shell.py +559 -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/local_tool_installer.py +1467 -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 +2427 -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 +53 -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/runtime/vps.py +830 -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/setup_wizard.py +941 -0
- aipt_v2/skills/__init__.py +80 -0
- aipt_v2/skills/agents/__init__.py +14 -0
- aipt_v2/skills/agents/api_tester.py +706 -0
- aipt_v2/skills/agents/base.py +477 -0
- aipt_v2/skills/agents/code_review.py +459 -0
- aipt_v2/skills/agents/security_agent.py +336 -0
- aipt_v2/skills/agents/web_pentest.py +818 -0
- aipt_v2/skills/prompts/__init__.py +647 -0
- aipt_v2/system_detector.py +539 -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 +202 -0
- aipt_v2/utils/model_manager.py +187 -0
- aipt_v2/utils/searchers/__init__.py +269 -0
- aipt_v2/verify_install.py +793 -0
- aiptx-2.0.7.dist-info/METADATA +345 -0
- aiptx-2.0.7.dist-info/RECORD +187 -0
- aiptx-2.0.7.dist-info/WHEEL +5 -0
- aiptx-2.0.7.dist-info/entry_points.txt +7 -0
- aiptx-2.0.7.dist-info/licenses/LICENSE +21 -0
- aiptx-2.0.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPT LLM-Powered Vulnerability Analyzer
|
|
3
|
+
|
|
4
|
+
Uses LLM intelligence to perform deep analysis of findings:
|
|
5
|
+
- Discover novel attack chains beyond predefined rules
|
|
6
|
+
- Assess real-world exploitability with context
|
|
7
|
+
- Generate attack narratives for reports
|
|
8
|
+
- Identify implicit vulnerabilities from patterns
|
|
9
|
+
|
|
10
|
+
This enhances the rule-based chaining with intelligent reasoning.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from typing import Any, Optional
|
|
20
|
+
|
|
21
|
+
from aipt_v2.models.findings import Finding, Severity, VulnerabilityType
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
ANALYSIS_PROMPT = """You are an elite penetration tester analyzing vulnerability findings.
|
|
27
|
+
|
|
28
|
+
## Findings to Analyze
|
|
29
|
+
```json
|
|
30
|
+
{findings_json}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Target Context
|
|
34
|
+
- **Target**: {target}
|
|
35
|
+
- **Technology Stack**: {tech_stack}
|
|
36
|
+
- **Business Type**: {business_context}
|
|
37
|
+
|
|
38
|
+
## Your Analysis Tasks
|
|
39
|
+
|
|
40
|
+
### 1. Attack Chain Discovery
|
|
41
|
+
Identify which vulnerabilities can be CHAINED together for greater impact.
|
|
42
|
+
Look for chains that go beyond obvious patterns:
|
|
43
|
+
- Can a low-severity finding enable a critical attack?
|
|
44
|
+
- Are there implicit trust relationships being exploited?
|
|
45
|
+
- Can multiple medium findings combine into a critical issue?
|
|
46
|
+
|
|
47
|
+
### 2. Implicit Vulnerability Detection
|
|
48
|
+
Based on patterns in the findings, identify IMPLICIT vulnerabilities:
|
|
49
|
+
- If SQLi exists, input validation is likely weak everywhere
|
|
50
|
+
- If one IDOR exists, authorization logic may be flawed globally
|
|
51
|
+
- If secrets are exposed, key management is likely poor
|
|
52
|
+
|
|
53
|
+
### 3. Real-World Exploitation Assessment
|
|
54
|
+
For each significant finding, assess:
|
|
55
|
+
- How easy would this be to exploit in practice?
|
|
56
|
+
- What would an attacker need (skills, tools, time)?
|
|
57
|
+
- What's the realistic business impact?
|
|
58
|
+
|
|
59
|
+
### 4. Executive Risk Summary
|
|
60
|
+
Provide a business-focused summary suitable for executives.
|
|
61
|
+
|
|
62
|
+
## Output Format (JSON)
|
|
63
|
+
```json
|
|
64
|
+
{{
|
|
65
|
+
"attack_chains": [
|
|
66
|
+
{{
|
|
67
|
+
"name": "Descriptive chain name",
|
|
68
|
+
"steps": [
|
|
69
|
+
{{
|
|
70
|
+
"finding_title": "Name of finding",
|
|
71
|
+
"action": "What attacker does",
|
|
72
|
+
"outcome": "What attacker achieves"
|
|
73
|
+
}}
|
|
74
|
+
],
|
|
75
|
+
"final_impact": "Ultimate impact if chain exploited",
|
|
76
|
+
"confidence": "high|medium|low",
|
|
77
|
+
"reasoning": "Why this chain works",
|
|
78
|
+
"cvss_amplification": 1.5
|
|
79
|
+
}}
|
|
80
|
+
],
|
|
81
|
+
"implicit_vulnerabilities": [
|
|
82
|
+
{{
|
|
83
|
+
"type": "vulnerability_type",
|
|
84
|
+
"reasoning": "Why we suspect this exists",
|
|
85
|
+
"indicators": ["evidence1", "evidence2"],
|
|
86
|
+
"recommended_test": "How to verify"
|
|
87
|
+
}}
|
|
88
|
+
],
|
|
89
|
+
"exploitation_assessments": [
|
|
90
|
+
{{
|
|
91
|
+
"finding_title": "Finding name",
|
|
92
|
+
"real_world_difficulty": "trivial|easy|moderate|difficult|theoretical",
|
|
93
|
+
"required_skills": "Description of needed skills",
|
|
94
|
+
"time_estimate": "Minutes/hours to exploit",
|
|
95
|
+
"impact_assessment": "Business impact description"
|
|
96
|
+
}}
|
|
97
|
+
],
|
|
98
|
+
"executive_summary": "2-3 paragraph executive summary",
|
|
99
|
+
"top_risks": ["risk1", "risk2", "risk3"],
|
|
100
|
+
"immediate_actions": ["action1", "action2"]
|
|
101
|
+
}}
|
|
102
|
+
```"""
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class DiscoveredChain:
|
|
107
|
+
"""An attack chain discovered by LLM analysis."""
|
|
108
|
+
name: str
|
|
109
|
+
steps: list[dict[str, str]]
|
|
110
|
+
final_impact: str
|
|
111
|
+
confidence: str
|
|
112
|
+
reasoning: str
|
|
113
|
+
cvss_amplification: float = 1.5
|
|
114
|
+
|
|
115
|
+
def to_dict(self) -> dict[str, Any]:
|
|
116
|
+
return {
|
|
117
|
+
"name": self.name,
|
|
118
|
+
"steps": self.steps,
|
|
119
|
+
"final_impact": self.final_impact,
|
|
120
|
+
"confidence": self.confidence,
|
|
121
|
+
"reasoning": self.reasoning,
|
|
122
|
+
"cvss_amplification": self.cvss_amplification,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@dataclass
|
|
127
|
+
class ImplicitVulnerability:
|
|
128
|
+
"""A suspected vulnerability inferred from patterns."""
|
|
129
|
+
vuln_type: str
|
|
130
|
+
reasoning: str
|
|
131
|
+
indicators: list[str]
|
|
132
|
+
recommended_test: str
|
|
133
|
+
|
|
134
|
+
def to_dict(self) -> dict[str, Any]:
|
|
135
|
+
return {
|
|
136
|
+
"type": self.vuln_type,
|
|
137
|
+
"reasoning": self.reasoning,
|
|
138
|
+
"indicators": self.indicators,
|
|
139
|
+
"recommended_test": self.recommended_test,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclass
|
|
144
|
+
class ExploitationAssessment:
|
|
145
|
+
"""Real-world exploitation assessment for a finding."""
|
|
146
|
+
finding_title: str
|
|
147
|
+
real_world_difficulty: str
|
|
148
|
+
required_skills: str
|
|
149
|
+
time_estimate: str
|
|
150
|
+
impact_assessment: str
|
|
151
|
+
|
|
152
|
+
def to_dict(self) -> dict[str, Any]:
|
|
153
|
+
return {
|
|
154
|
+
"finding_title": self.finding_title,
|
|
155
|
+
"real_world_difficulty": self.real_world_difficulty,
|
|
156
|
+
"required_skills": self.required_skills,
|
|
157
|
+
"time_estimate": self.time_estimate,
|
|
158
|
+
"impact_assessment": self.impact_assessment,
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@dataclass
|
|
163
|
+
class LLMAnalysisResult:
|
|
164
|
+
"""Complete result of LLM vulnerability analysis."""
|
|
165
|
+
attack_chains: list[DiscoveredChain]
|
|
166
|
+
implicit_vulnerabilities: list[ImplicitVulnerability]
|
|
167
|
+
exploitation_assessments: list[ExploitationAssessment]
|
|
168
|
+
executive_summary: str
|
|
169
|
+
top_risks: list[str]
|
|
170
|
+
immediate_actions: list[str]
|
|
171
|
+
analyzed_at: datetime = field(default_factory=datetime.utcnow)
|
|
172
|
+
llm_model: str = ""
|
|
173
|
+
|
|
174
|
+
def to_dict(self) -> dict[str, Any]:
|
|
175
|
+
return {
|
|
176
|
+
"attack_chains": [c.to_dict() for c in self.attack_chains],
|
|
177
|
+
"implicit_vulnerabilities": [v.to_dict() for v in self.implicit_vulnerabilities],
|
|
178
|
+
"exploitation_assessments": [a.to_dict() for a in self.exploitation_assessments],
|
|
179
|
+
"executive_summary": self.executive_summary,
|
|
180
|
+
"top_risks": self.top_risks,
|
|
181
|
+
"immediate_actions": self.immediate_actions,
|
|
182
|
+
"analyzed_at": self.analyzed_at.isoformat(),
|
|
183
|
+
"llm_model": self.llm_model,
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
def get_high_confidence_chains(self) -> list[DiscoveredChain]:
|
|
187
|
+
"""Get only high-confidence attack chains."""
|
|
188
|
+
return [c for c in self.attack_chains if c.confidence == "high"]
|
|
189
|
+
|
|
190
|
+
def get_critical_actions(self) -> list[str]:
|
|
191
|
+
"""Get actions that require immediate attention."""
|
|
192
|
+
return self.immediate_actions[:5]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class LLMVulnerabilityAnalyzer:
|
|
196
|
+
"""
|
|
197
|
+
LLM-powered deep vulnerability analysis.
|
|
198
|
+
|
|
199
|
+
Goes beyond rule-based chaining to discover novel attack paths
|
|
200
|
+
and assess real-world exploitability.
|
|
201
|
+
|
|
202
|
+
Example:
|
|
203
|
+
analyzer = LLMVulnerabilityAnalyzer()
|
|
204
|
+
result = await analyzer.analyze(
|
|
205
|
+
findings=findings,
|
|
206
|
+
target="https://example.com",
|
|
207
|
+
tech_stack=["WordPress", "PHP", "MySQL"],
|
|
208
|
+
business_context="E-commerce platform handling payments"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
for chain in result.get_high_confidence_chains():
|
|
212
|
+
print(f"Attack Chain: {chain.name}")
|
|
213
|
+
print(f"Impact: {chain.final_impact}")
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
def __init__(
|
|
217
|
+
self,
|
|
218
|
+
llm_provider: str = "anthropic",
|
|
219
|
+
llm_model: str = "claude-3-5-sonnet-20241022",
|
|
220
|
+
):
|
|
221
|
+
self.llm_provider = llm_provider
|
|
222
|
+
self.llm_model = llm_model
|
|
223
|
+
self._llm = None
|
|
224
|
+
|
|
225
|
+
async def _get_llm(self):
|
|
226
|
+
"""Get or create LLM client."""
|
|
227
|
+
if self._llm is None:
|
|
228
|
+
try:
|
|
229
|
+
import litellm
|
|
230
|
+
self._llm = litellm
|
|
231
|
+
except ImportError:
|
|
232
|
+
logger.warning("litellm not installed")
|
|
233
|
+
return None
|
|
234
|
+
return self._llm
|
|
235
|
+
|
|
236
|
+
async def analyze(
|
|
237
|
+
self,
|
|
238
|
+
findings: list[Finding],
|
|
239
|
+
target: str = "",
|
|
240
|
+
tech_stack: list[str] = None,
|
|
241
|
+
business_context: str = "",
|
|
242
|
+
) -> LLMAnalysisResult:
|
|
243
|
+
"""
|
|
244
|
+
Perform deep LLM analysis of findings.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
findings: List of vulnerability findings to analyze
|
|
248
|
+
target: Target URL or domain
|
|
249
|
+
tech_stack: Detected technologies
|
|
250
|
+
business_context: Business context for impact assessment
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
LLMAnalysisResult with discovered chains and assessments
|
|
254
|
+
"""
|
|
255
|
+
if not findings:
|
|
256
|
+
return self._empty_result()
|
|
257
|
+
|
|
258
|
+
llm = await self._get_llm()
|
|
259
|
+
if llm is None or not self._has_api_key():
|
|
260
|
+
logger.warning("LLM not available, returning basic analysis")
|
|
261
|
+
return self._basic_analysis(findings)
|
|
262
|
+
|
|
263
|
+
# Prepare findings for LLM (limit to top 30 to fit context)
|
|
264
|
+
sorted_findings = sorted(findings, key=lambda f: f.severity, reverse=True)
|
|
265
|
+
top_findings = sorted_findings[:30]
|
|
266
|
+
|
|
267
|
+
findings_json = json.dumps(
|
|
268
|
+
[f.to_dict() for f in top_findings],
|
|
269
|
+
indent=2,
|
|
270
|
+
default=str
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
prompt = ANALYSIS_PROMPT.format(
|
|
274
|
+
findings_json=findings_json,
|
|
275
|
+
target=target,
|
|
276
|
+
tech_stack=", ".join(tech_stack) if tech_stack else "Unknown",
|
|
277
|
+
business_context=business_context or "General web application",
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
response = await self._call_llm(prompt)
|
|
282
|
+
result = self._parse_response(response)
|
|
283
|
+
result.llm_model = self.llm_model
|
|
284
|
+
return result
|
|
285
|
+
except Exception as e:
|
|
286
|
+
logger.error(f"LLM analysis failed: {e}")
|
|
287
|
+
return self._basic_analysis(findings)
|
|
288
|
+
|
|
289
|
+
async def analyze_single_finding(
|
|
290
|
+
self,
|
|
291
|
+
finding: Finding,
|
|
292
|
+
context: str = "",
|
|
293
|
+
) -> ExploitationAssessment:
|
|
294
|
+
"""
|
|
295
|
+
Analyze a single finding for real-world exploitability.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
finding: The finding to analyze
|
|
299
|
+
context: Additional context about the target
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
ExploitationAssessment for the finding
|
|
303
|
+
"""
|
|
304
|
+
prompt = f"""Analyze this vulnerability for real-world exploitability:
|
|
305
|
+
|
|
306
|
+
Finding: {finding.title}
|
|
307
|
+
Type: {finding.vuln_type.value}
|
|
308
|
+
Severity: {finding.severity.value}
|
|
309
|
+
URL: {finding.url}
|
|
310
|
+
Evidence: {finding.evidence[:500] if finding.evidence else 'None'}
|
|
311
|
+
Context: {context}
|
|
312
|
+
|
|
313
|
+
Provide a JSON response:
|
|
314
|
+
{{
|
|
315
|
+
"real_world_difficulty": "trivial|easy|moderate|difficult|theoretical",
|
|
316
|
+
"required_skills": "Description",
|
|
317
|
+
"time_estimate": "Time to exploit",
|
|
318
|
+
"impact_assessment": "Business impact"
|
|
319
|
+
}}"""
|
|
320
|
+
|
|
321
|
+
try:
|
|
322
|
+
response = await self._call_llm(prompt)
|
|
323
|
+
data = self._extract_json(response)
|
|
324
|
+
return ExploitationAssessment(
|
|
325
|
+
finding_title=finding.title,
|
|
326
|
+
real_world_difficulty=data.get("real_world_difficulty", "moderate"),
|
|
327
|
+
required_skills=data.get("required_skills", "Security knowledge"),
|
|
328
|
+
time_estimate=data.get("time_estimate", "Unknown"),
|
|
329
|
+
impact_assessment=data.get("impact_assessment", finding.description),
|
|
330
|
+
)
|
|
331
|
+
except Exception as e:
|
|
332
|
+
logger.warning(f"Single finding analysis failed: {e}")
|
|
333
|
+
return self._default_assessment(finding)
|
|
334
|
+
|
|
335
|
+
async def discover_chains(
|
|
336
|
+
self,
|
|
337
|
+
findings: list[Finding],
|
|
338
|
+
) -> list[DiscoveredChain]:
|
|
339
|
+
"""
|
|
340
|
+
Focus specifically on discovering attack chains.
|
|
341
|
+
|
|
342
|
+
This is useful when you just want chain discovery without
|
|
343
|
+
full analysis.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
findings: Findings to analyze for chains
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
List of discovered attack chains
|
|
350
|
+
"""
|
|
351
|
+
result = await self.analyze(findings)
|
|
352
|
+
return result.attack_chains
|
|
353
|
+
|
|
354
|
+
async def _call_llm(self, prompt: str) -> str:
|
|
355
|
+
"""Call LLM and get response."""
|
|
356
|
+
llm = await self._get_llm()
|
|
357
|
+
|
|
358
|
+
model_str = f"{self.llm_provider}/{self.llm_model}"
|
|
359
|
+
if self.llm_provider == "anthropic" and not self.llm_model.startswith("anthropic/"):
|
|
360
|
+
model_str = f"anthropic/{self.llm_model}"
|
|
361
|
+
elif self.llm_provider == "openai" and not self.llm_model.startswith("openai/"):
|
|
362
|
+
model_str = f"openai/{self.llm_model}"
|
|
363
|
+
|
|
364
|
+
response = await llm.acompletion(
|
|
365
|
+
model=model_str,
|
|
366
|
+
messages=[{"role": "user", "content": prompt}],
|
|
367
|
+
max_tokens=4000,
|
|
368
|
+
temperature=0.3,
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
return response.choices[0].message.content
|
|
372
|
+
|
|
373
|
+
def _parse_response(self, response: str) -> LLMAnalysisResult:
|
|
374
|
+
"""Parse LLM response into structured result."""
|
|
375
|
+
data = self._extract_json(response)
|
|
376
|
+
|
|
377
|
+
# Parse attack chains
|
|
378
|
+
chains = []
|
|
379
|
+
for chain_data in data.get("attack_chains", []):
|
|
380
|
+
chains.append(DiscoveredChain(
|
|
381
|
+
name=chain_data.get("name", "Unknown Chain"),
|
|
382
|
+
steps=chain_data.get("steps", []),
|
|
383
|
+
final_impact=chain_data.get("final_impact", ""),
|
|
384
|
+
confidence=chain_data.get("confidence", "medium"),
|
|
385
|
+
reasoning=chain_data.get("reasoning", ""),
|
|
386
|
+
cvss_amplification=chain_data.get("cvss_amplification", 1.5),
|
|
387
|
+
))
|
|
388
|
+
|
|
389
|
+
# Parse implicit vulnerabilities
|
|
390
|
+
implicit = []
|
|
391
|
+
for vuln_data in data.get("implicit_vulnerabilities", []):
|
|
392
|
+
implicit.append(ImplicitVulnerability(
|
|
393
|
+
vuln_type=vuln_data.get("type", "unknown"),
|
|
394
|
+
reasoning=vuln_data.get("reasoning", ""),
|
|
395
|
+
indicators=vuln_data.get("indicators", []),
|
|
396
|
+
recommended_test=vuln_data.get("recommended_test", ""),
|
|
397
|
+
))
|
|
398
|
+
|
|
399
|
+
# Parse exploitation assessments
|
|
400
|
+
assessments = []
|
|
401
|
+
for assess_data in data.get("exploitation_assessments", []):
|
|
402
|
+
assessments.append(ExploitationAssessment(
|
|
403
|
+
finding_title=assess_data.get("finding_title", ""),
|
|
404
|
+
real_world_difficulty=assess_data.get("real_world_difficulty", "moderate"),
|
|
405
|
+
required_skills=assess_data.get("required_skills", ""),
|
|
406
|
+
time_estimate=assess_data.get("time_estimate", ""),
|
|
407
|
+
impact_assessment=assess_data.get("impact_assessment", ""),
|
|
408
|
+
))
|
|
409
|
+
|
|
410
|
+
return LLMAnalysisResult(
|
|
411
|
+
attack_chains=chains,
|
|
412
|
+
implicit_vulnerabilities=implicit,
|
|
413
|
+
exploitation_assessments=assessments,
|
|
414
|
+
executive_summary=data.get("executive_summary", "Analysis complete."),
|
|
415
|
+
top_risks=data.get("top_risks", []),
|
|
416
|
+
immediate_actions=data.get("immediate_actions", []),
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
def _extract_json(self, response: str) -> dict:
|
|
420
|
+
"""Extract JSON from LLM response."""
|
|
421
|
+
# Try to find JSON block
|
|
422
|
+
json_start = response.find("{")
|
|
423
|
+
json_end = response.rfind("}") + 1
|
|
424
|
+
|
|
425
|
+
if json_start >= 0 and json_end > json_start:
|
|
426
|
+
json_str = response[json_start:json_end]
|
|
427
|
+
return json.loads(json_str)
|
|
428
|
+
|
|
429
|
+
# Try to find code block
|
|
430
|
+
if "```json" in response:
|
|
431
|
+
start = response.find("```json") + 7
|
|
432
|
+
end = response.find("```", start)
|
|
433
|
+
if end > start:
|
|
434
|
+
return json.loads(response[start:end].strip())
|
|
435
|
+
|
|
436
|
+
raise ValueError("No valid JSON found in response")
|
|
437
|
+
|
|
438
|
+
def _empty_result(self) -> LLMAnalysisResult:
|
|
439
|
+
"""Return empty result for no findings."""
|
|
440
|
+
return LLMAnalysisResult(
|
|
441
|
+
attack_chains=[],
|
|
442
|
+
implicit_vulnerabilities=[],
|
|
443
|
+
exploitation_assessments=[],
|
|
444
|
+
executive_summary="No findings to analyze.",
|
|
445
|
+
top_risks=[],
|
|
446
|
+
immediate_actions=[],
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
def _basic_analysis(self, findings: list[Finding]) -> LLMAnalysisResult:
|
|
450
|
+
"""Provide basic analysis without LLM."""
|
|
451
|
+
# Count by severity for summary
|
|
452
|
+
critical = sum(1 for f in findings if f.severity == Severity.CRITICAL)
|
|
453
|
+
high = sum(1 for f in findings if f.severity == Severity.HIGH)
|
|
454
|
+
|
|
455
|
+
summary = f"Analysis identified {len(findings)} findings: {critical} critical, {high} high severity."
|
|
456
|
+
|
|
457
|
+
# Basic risk identification
|
|
458
|
+
risks = []
|
|
459
|
+
if critical > 0:
|
|
460
|
+
risks.append(f"{critical} critical vulnerabilities require immediate remediation")
|
|
461
|
+
if high > 0:
|
|
462
|
+
risks.append(f"{high} high-severity issues pose significant risk")
|
|
463
|
+
|
|
464
|
+
# Basic actions
|
|
465
|
+
actions = []
|
|
466
|
+
for f in sorted(findings, key=lambda x: x.severity, reverse=True)[:3]:
|
|
467
|
+
actions.append(f"Address {f.vuln_type.value}: {f.title}")
|
|
468
|
+
|
|
469
|
+
return LLMAnalysisResult(
|
|
470
|
+
attack_chains=[],
|
|
471
|
+
implicit_vulnerabilities=[],
|
|
472
|
+
exploitation_assessments=[],
|
|
473
|
+
executive_summary=summary,
|
|
474
|
+
top_risks=risks[:5],
|
|
475
|
+
immediate_actions=actions[:5],
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
def _default_assessment(self, finding: Finding) -> ExploitationAssessment:
|
|
479
|
+
"""Create default assessment for a finding."""
|
|
480
|
+
difficulty_map = {
|
|
481
|
+
Severity.CRITICAL: "easy",
|
|
482
|
+
Severity.HIGH: "moderate",
|
|
483
|
+
Severity.MEDIUM: "moderate",
|
|
484
|
+
Severity.LOW: "difficult",
|
|
485
|
+
Severity.INFO: "theoretical",
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return ExploitationAssessment(
|
|
489
|
+
finding_title=finding.title,
|
|
490
|
+
real_world_difficulty=difficulty_map.get(finding.severity, "moderate"),
|
|
491
|
+
required_skills="Security testing knowledge",
|
|
492
|
+
time_estimate="Varies",
|
|
493
|
+
impact_assessment=finding.description or f"{finding.severity.value} impact",
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
def _has_api_key(self) -> bool:
|
|
497
|
+
"""Check if API key is available."""
|
|
498
|
+
if self.llm_provider == "anthropic":
|
|
499
|
+
return bool(os.getenv("ANTHROPIC_API_KEY"))
|
|
500
|
+
if self.llm_provider == "openai":
|
|
501
|
+
return bool(os.getenv("OPENAI_API_KEY"))
|
|
502
|
+
return False
|