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,571 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Prowler Integration - AWS Security Best Practices Auditing
|
|
3
|
+
|
|
4
|
+
Prowler is an open-source security tool to perform AWS, GCP, and Azure
|
|
5
|
+
security best practices assessments, audits, incident response,
|
|
6
|
+
continuous monitoring, hardening and forensics readiness.
|
|
7
|
+
|
|
8
|
+
Supports:
|
|
9
|
+
- CIS AWS Foundations Benchmark
|
|
10
|
+
- PCI-DSS
|
|
11
|
+
- HIPAA
|
|
12
|
+
- GDPR
|
|
13
|
+
- AWS Well-Architected Framework
|
|
14
|
+
- 300+ security checks
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
from aipt_v2.tools.cloud import run_prowler
|
|
18
|
+
|
|
19
|
+
findings = await run_prowler(profile="production", compliance=["cis", "pci"])
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import asyncio
|
|
23
|
+
import json
|
|
24
|
+
import csv
|
|
25
|
+
import os
|
|
26
|
+
from dataclasses import dataclass, field
|
|
27
|
+
from datetime import datetime, timezone
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import List, Dict, Any, Optional
|
|
30
|
+
|
|
31
|
+
from aipt_v2.tools.cloud.cloud_config import CloudConfig, get_cloud_config
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class ProwlerConfig:
|
|
36
|
+
"""Prowler configuration."""
|
|
37
|
+
provider: str = "aws" # aws, azure, gcp
|
|
38
|
+
|
|
39
|
+
# AWS options
|
|
40
|
+
aws_profile: str = "default"
|
|
41
|
+
aws_regions: List[str] = field(default_factory=list) # Empty = all
|
|
42
|
+
|
|
43
|
+
# Compliance frameworks
|
|
44
|
+
compliance: List[str] = field(default_factory=list) # cis, pci, hipaa, gdpr
|
|
45
|
+
|
|
46
|
+
# Scanning options
|
|
47
|
+
checks: List[str] = field(default_factory=list) # Specific checks to run
|
|
48
|
+
services: List[str] = field(default_factory=list) # Specific services
|
|
49
|
+
severity: List[str] = field(default_factory=lambda: ["critical", "high", "medium", "low"])
|
|
50
|
+
|
|
51
|
+
# Output options
|
|
52
|
+
output_dir: str = "./prowler_results"
|
|
53
|
+
output_formats: List[str] = field(default_factory=lambda: ["json", "html"])
|
|
54
|
+
|
|
55
|
+
# Performance
|
|
56
|
+
parallel: bool = True
|
|
57
|
+
shodan_api_key: str = "" # Optional Shodan integration
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class ProwlerFinding:
|
|
62
|
+
"""Individual Prowler finding."""
|
|
63
|
+
check_id: str
|
|
64
|
+
check_title: str
|
|
65
|
+
service: str
|
|
66
|
+
severity: str
|
|
67
|
+
status: str # PASS, FAIL, INFO, WARNING
|
|
68
|
+
region: str
|
|
69
|
+
resource_id: str
|
|
70
|
+
resource_arn: str
|
|
71
|
+
description: str
|
|
72
|
+
risk: str
|
|
73
|
+
remediation: str
|
|
74
|
+
compliance: List[str] = field(default_factory=list)
|
|
75
|
+
timestamp: str = ""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class ProwlerResult:
|
|
80
|
+
"""Result of a Prowler scan."""
|
|
81
|
+
provider: str
|
|
82
|
+
status: str
|
|
83
|
+
started_at: str
|
|
84
|
+
finished_at: str
|
|
85
|
+
duration: float
|
|
86
|
+
total_checks: int
|
|
87
|
+
passed: int
|
|
88
|
+
failed: int
|
|
89
|
+
warnings: int
|
|
90
|
+
findings: List[ProwlerFinding]
|
|
91
|
+
report_path: str
|
|
92
|
+
summary: Dict[str, int]
|
|
93
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ProwlerTool:
|
|
97
|
+
"""
|
|
98
|
+
Prowler wrapper for AWS/Azure/GCP security auditing.
|
|
99
|
+
|
|
100
|
+
Provides a Python interface to the Prowler CLI tool
|
|
101
|
+
for automated cloud security assessments against
|
|
102
|
+
compliance frameworks.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def __init__(self, config: Optional[ProwlerConfig] = None):
|
|
106
|
+
"""
|
|
107
|
+
Initialize Prowler tool.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
config: Prowler configuration
|
|
111
|
+
"""
|
|
112
|
+
self.config = config or ProwlerConfig()
|
|
113
|
+
self.output_dir = Path(self.config.output_dir)
|
|
114
|
+
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
115
|
+
self._installed = None
|
|
116
|
+
self._version = None
|
|
117
|
+
|
|
118
|
+
async def check_installed(self) -> bool:
|
|
119
|
+
"""Check if Prowler is installed."""
|
|
120
|
+
if self._installed is not None:
|
|
121
|
+
return self._installed
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
process = await asyncio.create_subprocess_shell(
|
|
125
|
+
"prowler --version",
|
|
126
|
+
stdout=asyncio.subprocess.PIPE,
|
|
127
|
+
stderr=asyncio.subprocess.PIPE
|
|
128
|
+
)
|
|
129
|
+
stdout, _ = await process.communicate()
|
|
130
|
+
self._installed = process.returncode == 0
|
|
131
|
+
if self._installed:
|
|
132
|
+
self._version = stdout.decode().strip()
|
|
133
|
+
except Exception:
|
|
134
|
+
self._installed = False
|
|
135
|
+
|
|
136
|
+
return self._installed
|
|
137
|
+
|
|
138
|
+
async def get_version(self) -> str:
|
|
139
|
+
"""Get Prowler version."""
|
|
140
|
+
if not await self.check_installed():
|
|
141
|
+
return "Not installed"
|
|
142
|
+
return self._version or "Unknown"
|
|
143
|
+
|
|
144
|
+
def _build_command(self) -> str:
|
|
145
|
+
"""Build Prowler command from configuration."""
|
|
146
|
+
cmd_parts = ["prowler", self.config.provider]
|
|
147
|
+
|
|
148
|
+
# Add provider-specific options
|
|
149
|
+
if self.config.provider == "aws":
|
|
150
|
+
if self.config.aws_profile:
|
|
151
|
+
cmd_parts.extend(["--profile", self.config.aws_profile])
|
|
152
|
+
if self.config.aws_regions:
|
|
153
|
+
cmd_parts.extend(["--filter-region", ",".join(self.config.aws_regions)])
|
|
154
|
+
|
|
155
|
+
# Add compliance frameworks
|
|
156
|
+
if self.config.compliance:
|
|
157
|
+
for framework in self.config.compliance:
|
|
158
|
+
cmd_parts.extend(["--compliance", framework])
|
|
159
|
+
|
|
160
|
+
# Add specific checks
|
|
161
|
+
if self.config.checks:
|
|
162
|
+
cmd_parts.extend(["--checks", ",".join(self.config.checks)])
|
|
163
|
+
|
|
164
|
+
# Add service filtering
|
|
165
|
+
if self.config.services:
|
|
166
|
+
cmd_parts.extend(["--services", ",".join(self.config.services)])
|
|
167
|
+
|
|
168
|
+
# Add severity filtering
|
|
169
|
+
if self.config.severity:
|
|
170
|
+
cmd_parts.extend(["--severity", ",".join(self.config.severity)])
|
|
171
|
+
|
|
172
|
+
# Add output options
|
|
173
|
+
cmd_parts.extend(["--output-directory", str(self.output_dir)])
|
|
174
|
+
|
|
175
|
+
if self.config.output_formats:
|
|
176
|
+
cmd_parts.extend(["--output-formats", ",".join(self.config.output_formats)])
|
|
177
|
+
|
|
178
|
+
# Add Shodan integration if configured
|
|
179
|
+
if self.config.shodan_api_key:
|
|
180
|
+
cmd_parts.extend(["--shodan", self.config.shodan_api_key])
|
|
181
|
+
|
|
182
|
+
# Quiet mode for cleaner output
|
|
183
|
+
cmd_parts.append("--no-banner")
|
|
184
|
+
|
|
185
|
+
return " ".join(cmd_parts)
|
|
186
|
+
|
|
187
|
+
async def scan(self, timeout: int = 3600) -> ProwlerResult:
|
|
188
|
+
"""
|
|
189
|
+
Run Prowler scan.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
timeout: Scan timeout in seconds
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
ProwlerResult with findings summary
|
|
196
|
+
"""
|
|
197
|
+
if not await self.check_installed():
|
|
198
|
+
raise RuntimeError("Prowler is not installed. Install with: pip install prowler")
|
|
199
|
+
|
|
200
|
+
started_at = datetime.now(timezone.utc).isoformat()
|
|
201
|
+
start_time = asyncio.get_event_loop().time()
|
|
202
|
+
|
|
203
|
+
cmd = self._build_command()
|
|
204
|
+
print(f"[*] Running: {cmd}")
|
|
205
|
+
|
|
206
|
+
# Set up environment
|
|
207
|
+
env = os.environ.copy()
|
|
208
|
+
|
|
209
|
+
process = await asyncio.create_subprocess_shell(
|
|
210
|
+
cmd,
|
|
211
|
+
stdout=asyncio.subprocess.PIPE,
|
|
212
|
+
stderr=asyncio.subprocess.PIPE,
|
|
213
|
+
env=env
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
stdout, stderr = await asyncio.wait_for(
|
|
218
|
+
process.communicate(),
|
|
219
|
+
timeout=timeout
|
|
220
|
+
)
|
|
221
|
+
except asyncio.TimeoutError:
|
|
222
|
+
process.kill()
|
|
223
|
+
raise TimeoutError(f"Prowler scan timed out after {timeout}s")
|
|
224
|
+
|
|
225
|
+
finished_at = datetime.now(timezone.utc).isoformat()
|
|
226
|
+
duration = asyncio.get_event_loop().time() - start_time
|
|
227
|
+
|
|
228
|
+
# Parse results
|
|
229
|
+
findings = self._parse_findings()
|
|
230
|
+
report_path = self._find_latest_report()
|
|
231
|
+
|
|
232
|
+
# Calculate summary
|
|
233
|
+
total_checks = len(findings)
|
|
234
|
+
passed = sum(1 for f in findings if f.status == "PASS")
|
|
235
|
+
failed = sum(1 for f in findings if f.status == "FAIL")
|
|
236
|
+
warnings = sum(1 for f in findings if f.status in ["WARNING", "INFO"])
|
|
237
|
+
|
|
238
|
+
summary = {
|
|
239
|
+
"critical": sum(1 for f in findings if f.severity == "critical" and f.status == "FAIL"),
|
|
240
|
+
"high": sum(1 for f in findings if f.severity == "high" and f.status == "FAIL"),
|
|
241
|
+
"medium": sum(1 for f in findings if f.severity == "medium" and f.status == "FAIL"),
|
|
242
|
+
"low": sum(1 for f in findings if f.severity == "low" and f.status == "FAIL"),
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
status = "completed" if process.returncode == 0 else "failed"
|
|
246
|
+
|
|
247
|
+
return ProwlerResult(
|
|
248
|
+
provider=self.config.provider,
|
|
249
|
+
status=status,
|
|
250
|
+
started_at=started_at,
|
|
251
|
+
finished_at=finished_at,
|
|
252
|
+
duration=duration,
|
|
253
|
+
total_checks=total_checks,
|
|
254
|
+
passed=passed,
|
|
255
|
+
failed=failed,
|
|
256
|
+
warnings=warnings,
|
|
257
|
+
findings=findings,
|
|
258
|
+
report_path=str(report_path) if report_path else "",
|
|
259
|
+
summary=summary,
|
|
260
|
+
metadata={
|
|
261
|
+
"command": cmd,
|
|
262
|
+
"return_code": process.returncode,
|
|
263
|
+
"version": self._version,
|
|
264
|
+
"stderr": stderr.decode() if process.returncode != 0 else ""
|
|
265
|
+
}
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
def _find_latest_report(self) -> Optional[Path]:
|
|
269
|
+
"""Find the latest Prowler output file."""
|
|
270
|
+
# Prowler creates timestamped directories
|
|
271
|
+
if not self.output_dir.exists():
|
|
272
|
+
return None
|
|
273
|
+
|
|
274
|
+
# Look for JSON output files
|
|
275
|
+
json_files = list(self.output_dir.glob("**/prowler-output*.json"))
|
|
276
|
+
if json_files:
|
|
277
|
+
return max(json_files, key=lambda f: f.stat().st_mtime)
|
|
278
|
+
|
|
279
|
+
# Try CSV files
|
|
280
|
+
csv_files = list(self.output_dir.glob("**/prowler-output*.csv"))
|
|
281
|
+
if csv_files:
|
|
282
|
+
return max(csv_files, key=lambda f: f.stat().st_mtime)
|
|
283
|
+
|
|
284
|
+
# Try HTML files
|
|
285
|
+
html_files = list(self.output_dir.glob("**/prowler-output*.html"))
|
|
286
|
+
if html_files:
|
|
287
|
+
return max(html_files, key=lambda f: f.stat().st_mtime)
|
|
288
|
+
|
|
289
|
+
return None
|
|
290
|
+
|
|
291
|
+
def _parse_findings(self) -> List[ProwlerFinding]:
|
|
292
|
+
"""Parse findings from Prowler output."""
|
|
293
|
+
findings = []
|
|
294
|
+
|
|
295
|
+
# Try JSON first (preferred)
|
|
296
|
+
json_files = list(self.output_dir.glob("**/prowler-output*.json"))
|
|
297
|
+
if json_files:
|
|
298
|
+
latest_json = max(json_files, key=lambda f: f.stat().st_mtime)
|
|
299
|
+
findings.extend(self._parse_json_findings(latest_json))
|
|
300
|
+
|
|
301
|
+
# Fall back to CSV if no JSON
|
|
302
|
+
if not findings:
|
|
303
|
+
csv_files = list(self.output_dir.glob("**/prowler-output*.csv"))
|
|
304
|
+
if csv_files:
|
|
305
|
+
latest_csv = max(csv_files, key=lambda f: f.stat().st_mtime)
|
|
306
|
+
findings.extend(self._parse_csv_findings(latest_csv))
|
|
307
|
+
|
|
308
|
+
return findings
|
|
309
|
+
|
|
310
|
+
def _parse_json_findings(self, json_file: Path) -> List[ProwlerFinding]:
|
|
311
|
+
"""Parse findings from JSON output."""
|
|
312
|
+
findings = []
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
with open(json_file, 'r') as f:
|
|
316
|
+
# Prowler JSON can be JSONL (one JSON per line) or array
|
|
317
|
+
content = f.read().strip()
|
|
318
|
+
|
|
319
|
+
if content.startswith('['):
|
|
320
|
+
# JSON array
|
|
321
|
+
data = json.loads(content)
|
|
322
|
+
else:
|
|
323
|
+
# JSONL format
|
|
324
|
+
data = []
|
|
325
|
+
for line in content.split('\n'):
|
|
326
|
+
if line.strip():
|
|
327
|
+
data.append(json.loads(line))
|
|
328
|
+
|
|
329
|
+
for item in data:
|
|
330
|
+
finding = ProwlerFinding(
|
|
331
|
+
check_id=item.get("CheckID", item.get("check_id", "")),
|
|
332
|
+
check_title=item.get("CheckTitle", item.get("check_title", "")),
|
|
333
|
+
service=item.get("ServiceName", item.get("service", "")),
|
|
334
|
+
severity=item.get("Severity", item.get("severity", "")).lower(),
|
|
335
|
+
status=item.get("Status", item.get("status", "")).upper(),
|
|
336
|
+
region=item.get("Region", item.get("region", "")),
|
|
337
|
+
resource_id=item.get("ResourceId", item.get("resource_id", "")),
|
|
338
|
+
resource_arn=item.get("ResourceArn", item.get("resource_arn", "")),
|
|
339
|
+
description=item.get("StatusExtended", item.get("description", "")),
|
|
340
|
+
risk=item.get("Risk", item.get("risk", "")),
|
|
341
|
+
remediation=item.get("Remediation", {}).get("Recommendation", {}).get("Text", ""),
|
|
342
|
+
compliance=item.get("Compliance", []),
|
|
343
|
+
timestamp=item.get("Timestamp", item.get("timestamp", ""))
|
|
344
|
+
)
|
|
345
|
+
findings.append(finding)
|
|
346
|
+
|
|
347
|
+
except Exception as e:
|
|
348
|
+
print(f"[!] Error parsing Prowler JSON: {e}")
|
|
349
|
+
|
|
350
|
+
return findings
|
|
351
|
+
|
|
352
|
+
def _parse_csv_findings(self, csv_file: Path) -> List[ProwlerFinding]:
|
|
353
|
+
"""Parse findings from CSV output."""
|
|
354
|
+
findings = []
|
|
355
|
+
|
|
356
|
+
try:
|
|
357
|
+
with open(csv_file, 'r', newline='') as f:
|
|
358
|
+
reader = csv.DictReader(f)
|
|
359
|
+
for row in reader:
|
|
360
|
+
finding = ProwlerFinding(
|
|
361
|
+
check_id=row.get("CHECK_ID", row.get("check_id", "")),
|
|
362
|
+
check_title=row.get("CHECK_TITLE", row.get("check_title", "")),
|
|
363
|
+
service=row.get("SERVICE_NAME", row.get("service", "")),
|
|
364
|
+
severity=row.get("SEVERITY", row.get("severity", "")).lower(),
|
|
365
|
+
status=row.get("STATUS", row.get("status", "")).upper(),
|
|
366
|
+
region=row.get("REGION", row.get("region", "")),
|
|
367
|
+
resource_id=row.get("RESOURCE_ID", row.get("resource_id", "")),
|
|
368
|
+
resource_arn=row.get("RESOURCE_ARN", row.get("resource_arn", "")),
|
|
369
|
+
description=row.get("STATUS_EXTENDED", row.get("description", "")),
|
|
370
|
+
risk=row.get("RISK", row.get("risk", "")),
|
|
371
|
+
remediation=row.get("REMEDIATION", row.get("remediation", "")),
|
|
372
|
+
compliance=[],
|
|
373
|
+
timestamp=row.get("TIMESTAMP", row.get("timestamp", ""))
|
|
374
|
+
)
|
|
375
|
+
findings.append(finding)
|
|
376
|
+
|
|
377
|
+
except Exception as e:
|
|
378
|
+
print(f"[!] Error parsing Prowler CSV: {e}")
|
|
379
|
+
|
|
380
|
+
return findings
|
|
381
|
+
|
|
382
|
+
def get_failed_findings(self) -> List[ProwlerFinding]:
|
|
383
|
+
"""Get only failed findings from the latest scan."""
|
|
384
|
+
all_findings = self._parse_findings()
|
|
385
|
+
return [f for f in all_findings if f.status == "FAIL"]
|
|
386
|
+
|
|
387
|
+
def get_findings_by_severity(self, severity: str) -> List[ProwlerFinding]:
|
|
388
|
+
"""Get findings filtered by severity."""
|
|
389
|
+
all_findings = self._parse_findings()
|
|
390
|
+
return [f for f in all_findings if f.severity.lower() == severity.lower()]
|
|
391
|
+
|
|
392
|
+
def get_findings_by_service(self, service: str) -> List[ProwlerFinding]:
|
|
393
|
+
"""Get findings filtered by AWS service."""
|
|
394
|
+
all_findings = self._parse_findings()
|
|
395
|
+
return [f for f in all_findings if service.lower() in f.service.lower()]
|
|
396
|
+
|
|
397
|
+
def get_compliance_summary(self) -> Dict[str, Dict[str, int]]:
|
|
398
|
+
"""Get compliance framework summary."""
|
|
399
|
+
findings = self._parse_findings()
|
|
400
|
+
compliance_summary = {}
|
|
401
|
+
|
|
402
|
+
for finding in findings:
|
|
403
|
+
for framework in finding.compliance:
|
|
404
|
+
if framework not in compliance_summary:
|
|
405
|
+
compliance_summary[framework] = {"passed": 0, "failed": 0}
|
|
406
|
+
|
|
407
|
+
if finding.status == "PASS":
|
|
408
|
+
compliance_summary[framework]["passed"] += 1
|
|
409
|
+
elif finding.status == "FAIL":
|
|
410
|
+
compliance_summary[framework]["failed"] += 1
|
|
411
|
+
|
|
412
|
+
return compliance_summary
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
async def list_available_checks(provider: str = "aws") -> List[Dict[str, str]]:
|
|
416
|
+
"""
|
|
417
|
+
List all available Prowler checks for a provider.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
provider: Cloud provider (aws, azure, gcp)
|
|
421
|
+
|
|
422
|
+
Returns:
|
|
423
|
+
List of check definitions
|
|
424
|
+
"""
|
|
425
|
+
try:
|
|
426
|
+
process = await asyncio.create_subprocess_shell(
|
|
427
|
+
f"prowler {provider} --list-checks --no-banner",
|
|
428
|
+
stdout=asyncio.subprocess.PIPE,
|
|
429
|
+
stderr=asyncio.subprocess.PIPE
|
|
430
|
+
)
|
|
431
|
+
stdout, _ = await process.communicate()
|
|
432
|
+
|
|
433
|
+
checks = []
|
|
434
|
+
for line in stdout.decode().split('\n'):
|
|
435
|
+
if line.strip() and not line.startswith(('-', '=')):
|
|
436
|
+
parts = line.split(' - ', 1)
|
|
437
|
+
if len(parts) == 2:
|
|
438
|
+
checks.append({
|
|
439
|
+
"id": parts[0].strip(),
|
|
440
|
+
"title": parts[1].strip()
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
return checks
|
|
444
|
+
except Exception as e:
|
|
445
|
+
print(f"[!] Error listing checks: {e}")
|
|
446
|
+
return []
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
async def list_compliance_frameworks(provider: str = "aws") -> List[str]:
|
|
450
|
+
"""
|
|
451
|
+
List available compliance frameworks.
|
|
452
|
+
|
|
453
|
+
Args:
|
|
454
|
+
provider: Cloud provider
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
List of framework names
|
|
458
|
+
"""
|
|
459
|
+
try:
|
|
460
|
+
process = await asyncio.create_subprocess_shell(
|
|
461
|
+
f"prowler {provider} --list-compliance --no-banner",
|
|
462
|
+
stdout=asyncio.subprocess.PIPE,
|
|
463
|
+
stderr=asyncio.subprocess.PIPE
|
|
464
|
+
)
|
|
465
|
+
stdout, _ = await process.communicate()
|
|
466
|
+
|
|
467
|
+
frameworks = []
|
|
468
|
+
for line in stdout.decode().split('\n'):
|
|
469
|
+
line = line.strip()
|
|
470
|
+
if line and not line.startswith(('-', '=', 'Available')):
|
|
471
|
+
frameworks.append(line)
|
|
472
|
+
|
|
473
|
+
return frameworks
|
|
474
|
+
except Exception:
|
|
475
|
+
# Return default frameworks if listing fails
|
|
476
|
+
return [
|
|
477
|
+
"cis_1.4_aws",
|
|
478
|
+
"cis_1.5_aws",
|
|
479
|
+
"cis_2.0_aws",
|
|
480
|
+
"pci_3.2.1_aws",
|
|
481
|
+
"hipaa_aws",
|
|
482
|
+
"gdpr_aws",
|
|
483
|
+
"aws_well_architected_framework",
|
|
484
|
+
"nist_800_53_revision_5_aws"
|
|
485
|
+
]
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
# Convenience function
|
|
489
|
+
async def run_prowler(
|
|
490
|
+
profile: Optional[str] = None,
|
|
491
|
+
regions: Optional[List[str]] = None,
|
|
492
|
+
compliance: Optional[List[str]] = None,
|
|
493
|
+
services: Optional[List[str]] = None,
|
|
494
|
+
severity: Optional[List[str]] = None,
|
|
495
|
+
output_dir: str = "./prowler_results",
|
|
496
|
+
timeout: int = 3600
|
|
497
|
+
) -> ProwlerResult:
|
|
498
|
+
"""
|
|
499
|
+
Run Prowler AWS security scan.
|
|
500
|
+
|
|
501
|
+
Args:
|
|
502
|
+
profile: AWS profile name
|
|
503
|
+
regions: Specific regions to scan
|
|
504
|
+
compliance: Compliance frameworks to check (cis, pci, hipaa, gdpr)
|
|
505
|
+
services: Specific AWS services to scan
|
|
506
|
+
severity: Severity levels to include
|
|
507
|
+
output_dir: Output directory for reports
|
|
508
|
+
timeout: Scan timeout in seconds
|
|
509
|
+
|
|
510
|
+
Returns:
|
|
511
|
+
ProwlerResult
|
|
512
|
+
"""
|
|
513
|
+
config = ProwlerConfig(
|
|
514
|
+
provider="aws",
|
|
515
|
+
aws_profile=profile or "default",
|
|
516
|
+
aws_regions=regions or [],
|
|
517
|
+
compliance=compliance or [],
|
|
518
|
+
services=services or [],
|
|
519
|
+
severity=severity or ["critical", "high", "medium", "low"],
|
|
520
|
+
output_dir=output_dir
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
tool = ProwlerTool(config)
|
|
524
|
+
return await tool.scan(timeout=timeout)
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
# Quick check functions
|
|
528
|
+
async def quick_iam_check(profile: str = "default") -> ProwlerResult:
|
|
529
|
+
"""Quick IAM security check."""
|
|
530
|
+
config = ProwlerConfig(
|
|
531
|
+
aws_profile=profile,
|
|
532
|
+
services=["iam"],
|
|
533
|
+
severity=["critical", "high"]
|
|
534
|
+
)
|
|
535
|
+
tool = ProwlerTool(config)
|
|
536
|
+
return await tool.scan(timeout=600)
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
async def quick_s3_check(profile: str = "default") -> ProwlerResult:
|
|
540
|
+
"""Quick S3 security check."""
|
|
541
|
+
config = ProwlerConfig(
|
|
542
|
+
aws_profile=profile,
|
|
543
|
+
services=["s3"],
|
|
544
|
+
severity=["critical", "high"]
|
|
545
|
+
)
|
|
546
|
+
tool = ProwlerTool(config)
|
|
547
|
+
return await tool.scan(timeout=600)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
async def quick_network_check(profile: str = "default") -> ProwlerResult:
|
|
551
|
+
"""Quick network/VPC security check."""
|
|
552
|
+
config = ProwlerConfig(
|
|
553
|
+
aws_profile=profile,
|
|
554
|
+
services=["ec2", "vpc"],
|
|
555
|
+
severity=["critical", "high"]
|
|
556
|
+
)
|
|
557
|
+
tool = ProwlerTool(config)
|
|
558
|
+
return await tool.scan(timeout=900)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
async def compliance_scan(
|
|
562
|
+
profile: str = "default",
|
|
563
|
+
framework: str = "cis_2.0_aws"
|
|
564
|
+
) -> ProwlerResult:
|
|
565
|
+
"""Run compliance-focused scan."""
|
|
566
|
+
config = ProwlerConfig(
|
|
567
|
+
aws_profile=profile,
|
|
568
|
+
compliance=[framework]
|
|
569
|
+
)
|
|
570
|
+
tool = ProwlerTool(config)
|
|
571
|
+
return await tool.scan(timeout=3600)
|