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,366 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
AIPT v2 Security Audit Script
|
|
4
|
+
=============================
|
|
5
|
+
|
|
6
|
+
Performs comprehensive security checks on the AIPT codebase.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python scripts/security_audit.py [--fix] [--verbose]
|
|
10
|
+
|
|
11
|
+
Checks:
|
|
12
|
+
1. Bandit SAST scan for vulnerabilities
|
|
13
|
+
2. Dependency vulnerability check with safety
|
|
14
|
+
3. Hardcoded secrets detection
|
|
15
|
+
4. Outdated dependencies
|
|
16
|
+
5. Shell command patterns
|
|
17
|
+
6. Request timeout verification
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import subprocess
|
|
22
|
+
import sys
|
|
23
|
+
import re
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import List, Tuple
|
|
26
|
+
|
|
27
|
+
# Colors for output
|
|
28
|
+
RED = "\033[91m"
|
|
29
|
+
GREEN = "\033[92m"
|
|
30
|
+
YELLOW = "\033[93m"
|
|
31
|
+
BLUE = "\033[94m"
|
|
32
|
+
RESET = "\033[0m"
|
|
33
|
+
BOLD = "\033[1m"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def print_header(title: str):
|
|
37
|
+
"""Print a section header."""
|
|
38
|
+
print(f"\n{BOLD}{BLUE}{'='*60}{RESET}")
|
|
39
|
+
print(f"{BOLD}{BLUE} {title}{RESET}")
|
|
40
|
+
print(f"{BOLD}{BLUE}{'='*60}{RESET}\n")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def print_result(passed: bool, message: str):
|
|
44
|
+
"""Print a check result."""
|
|
45
|
+
status = f"{GREEN}[PASS]{RESET}" if passed else f"{RED}[FAIL]{RESET}"
|
|
46
|
+
print(f" {status} {message}")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def print_warning(message: str):
|
|
50
|
+
"""Print a warning."""
|
|
51
|
+
print(f" {YELLOW}[WARN]{RESET} {message}")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def run_command(cmd: List[str], timeout: int = 120) -> Tuple[int, str, str]:
|
|
55
|
+
"""Run a command and return exit code, stdout, stderr."""
|
|
56
|
+
try:
|
|
57
|
+
result = subprocess.run(
|
|
58
|
+
cmd,
|
|
59
|
+
capture_output=True,
|
|
60
|
+
text=True,
|
|
61
|
+
timeout=timeout
|
|
62
|
+
)
|
|
63
|
+
return result.returncode, result.stdout, result.stderr
|
|
64
|
+
except subprocess.TimeoutExpired:
|
|
65
|
+
return -1, "", "Command timed out"
|
|
66
|
+
except FileNotFoundError:
|
|
67
|
+
return -1, "", f"Command not found: {cmd[0]}"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def check_bandit(verbose: bool = False) -> Tuple[bool, int, int, int]:
|
|
71
|
+
"""Run Bandit security scanner."""
|
|
72
|
+
print_header("Bandit SAST Scan")
|
|
73
|
+
|
|
74
|
+
code, stdout, stderr = run_command([
|
|
75
|
+
"python3", "-m", "bandit",
|
|
76
|
+
"-r", ".",
|
|
77
|
+
"--exclude", "./htmlcov,./venv,./.venv,./tests",
|
|
78
|
+
"-f", "custom",
|
|
79
|
+
"--msg-template", "{severity}: {test_id} at {relpath}:{line} - {msg}"
|
|
80
|
+
])
|
|
81
|
+
|
|
82
|
+
high = stdout.count("HIGH:")
|
|
83
|
+
medium = stdout.count("MEDIUM:")
|
|
84
|
+
low = stdout.count("LOW:")
|
|
85
|
+
|
|
86
|
+
print_result(high == 0, f"No HIGH severity issues")
|
|
87
|
+
print_result(medium <= 5, f"Medium severity issues: {medium}")
|
|
88
|
+
print_result(True, f"Low severity issues: {low}")
|
|
89
|
+
|
|
90
|
+
if verbose and (high > 0 or medium > 0):
|
|
91
|
+
print(f"\n{stdout}")
|
|
92
|
+
|
|
93
|
+
return high == 0, high, medium, low
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def check_secrets() -> bool:
|
|
97
|
+
"""Check for hardcoded secrets."""
|
|
98
|
+
print_header("Hardcoded Secrets Check")
|
|
99
|
+
|
|
100
|
+
# Patterns for actual hardcoded secrets (not variable names or field definitions)
|
|
101
|
+
patterns = [
|
|
102
|
+
# Look for actual string assignments that look like secrets
|
|
103
|
+
(r'(?:password|passwd|pwd)\s*=\s*["\'](?![\'"]\s*$)[a-zA-Z0-9!@#$%^&*]{8,}["\']', "password"),
|
|
104
|
+
(r'(?:api_key|apikey)\s*=\s*["\'](?![\'"]\s*$)[a-zA-Z0-9_-]{20,}["\']', "api_key"),
|
|
105
|
+
(r'(?:secret|secret_key)\s*=\s*["\'](?![\'"]\s*$)[a-zA-Z0-9_-]{16,}["\']', "secret"),
|
|
106
|
+
(r'(?:auth_token|bearer_token)\s*=\s*["\'](?![\'"]\s*$)[a-zA-Z0-9._-]{20,}["\']', "token"),
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
found_secrets = []
|
|
110
|
+
exclude_dirs = {".git", "htmlcov", "venv", ".venv", "__pycache__", "tests", "scripts"}
|
|
111
|
+
|
|
112
|
+
# Also exclude common false positive files
|
|
113
|
+
exclude_files = {"config.py", "__init__.py"}
|
|
114
|
+
|
|
115
|
+
for py_file in Path(".").rglob("*.py"):
|
|
116
|
+
if any(ex in str(py_file) for ex in exclude_dirs):
|
|
117
|
+
continue
|
|
118
|
+
if py_file.name in exclude_files:
|
|
119
|
+
continue
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
content = py_file.read_text()
|
|
123
|
+
lines = content.split("\n")
|
|
124
|
+
|
|
125
|
+
for i, line in enumerate(lines, 1):
|
|
126
|
+
# Skip comments
|
|
127
|
+
if line.strip().startswith("#"):
|
|
128
|
+
continue
|
|
129
|
+
# Skip obvious false positives
|
|
130
|
+
if "Field(" in line or "Optional[" in line or "description=" in line:
|
|
131
|
+
continue
|
|
132
|
+
if "getenv" in line or "environ" in line:
|
|
133
|
+
continue
|
|
134
|
+
if "example" in line.lower() or "placeholder" in line.lower():
|
|
135
|
+
continue
|
|
136
|
+
# Skip common placeholder patterns
|
|
137
|
+
if "your-" in line.lower() or "-here" in line.lower() or "xxx" in line.lower():
|
|
138
|
+
continue
|
|
139
|
+
|
|
140
|
+
for pattern, name in patterns:
|
|
141
|
+
if re.search(pattern, line, re.IGNORECASE):
|
|
142
|
+
found_secrets.append((py_file, i, name, line.strip()[:50]))
|
|
143
|
+
except Exception:
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
passed = len(found_secrets) == 0
|
|
147
|
+
print_result(passed, f"No hardcoded secrets found" if passed else f"Found {len(found_secrets)} potential secrets")
|
|
148
|
+
|
|
149
|
+
if found_secrets:
|
|
150
|
+
for file, line_num, name, value in found_secrets[:5]:
|
|
151
|
+
print_warning(f" {file}:{line_num}: {name}")
|
|
152
|
+
|
|
153
|
+
return passed
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def check_shell_commands() -> bool:
|
|
157
|
+
"""Check for unsafe shell command patterns."""
|
|
158
|
+
print_header("Shell Command Security Check")
|
|
159
|
+
|
|
160
|
+
# Look for actual shell=True in subprocess calls (not comments about it)
|
|
161
|
+
code, stdout, stderr = run_command([
|
|
162
|
+
"grep", "-rn", "--include=*.py",
|
|
163
|
+
r"subprocess.*shell=True\|create_subprocess_shell",
|
|
164
|
+
"."
|
|
165
|
+
])
|
|
166
|
+
|
|
167
|
+
# Filter out comments and safe patterns
|
|
168
|
+
issues = []
|
|
169
|
+
for line in stdout.split("\n"):
|
|
170
|
+
if not line:
|
|
171
|
+
continue
|
|
172
|
+
# Skip tests, htmlcov, scripts, and virtual environments
|
|
173
|
+
if "tests/" in line or "htmlcov/" in line or "scripts/" in line or ".venv/" in line or "venv/" in line:
|
|
174
|
+
continue
|
|
175
|
+
# Skip comments (lines where # appears before the pattern)
|
|
176
|
+
file_content = line.split(":", 2)
|
|
177
|
+
if len(file_content) >= 3:
|
|
178
|
+
code_part = file_content[2]
|
|
179
|
+
# If it's a comment about shell=True (contains "Security:" or starts with #)
|
|
180
|
+
if "Security:" in code_part or code_part.strip().startswith("#"):
|
|
181
|
+
continue
|
|
182
|
+
# If it's actual shell=True usage
|
|
183
|
+
if "shell=True" in code_part and "subprocess" in code_part:
|
|
184
|
+
issues.append(line)
|
|
185
|
+
# create_subprocess_shell is inherently shell execution
|
|
186
|
+
elif "create_subprocess_shell" in code_part:
|
|
187
|
+
# These are expected in pentest tools
|
|
188
|
+
pass # Don't flag as issue, this is intentional
|
|
189
|
+
|
|
190
|
+
passed = len(issues) == 0
|
|
191
|
+
print_result(passed, f"No unsafe shell=True patterns" if passed else f"Found {len(issues)} shell=True usages")
|
|
192
|
+
|
|
193
|
+
if issues:
|
|
194
|
+
for issue in issues[:5]:
|
|
195
|
+
print_warning(f" {issue[:80]}...")
|
|
196
|
+
|
|
197
|
+
return passed
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def check_request_timeouts() -> bool:
|
|
201
|
+
"""Check for HTTP requests without timeout."""
|
|
202
|
+
print_header("HTTP Request Timeout Check")
|
|
203
|
+
|
|
204
|
+
# Find all Python files with requests calls
|
|
205
|
+
issues = []
|
|
206
|
+
exclude_dirs = {".git", "htmlcov", "venv", ".venv", "__pycache__", "tests"}
|
|
207
|
+
|
|
208
|
+
for py_file in Path(".").rglob("*.py"):
|
|
209
|
+
if any(ex in str(py_file) for ex in exclude_dirs):
|
|
210
|
+
continue
|
|
211
|
+
|
|
212
|
+
try:
|
|
213
|
+
content = py_file.read_text()
|
|
214
|
+
lines = content.split("\n")
|
|
215
|
+
|
|
216
|
+
for i, line in enumerate(lines, 1):
|
|
217
|
+
# Look for requests.get/post/put/delete calls
|
|
218
|
+
if re.search(r'requests\.(get|post|put|delete|patch)\s*\(', line):
|
|
219
|
+
# Check if timeout is specified (in same line or next few lines for multiline)
|
|
220
|
+
context = "\n".join(lines[max(0, i-1):min(len(lines), i+5)])
|
|
221
|
+
if "timeout" not in context:
|
|
222
|
+
issues.append(f"{py_file}:{i}: {line.strip()[:60]}")
|
|
223
|
+
except Exception:
|
|
224
|
+
continue
|
|
225
|
+
|
|
226
|
+
passed = len(issues) == 0
|
|
227
|
+
print_result(passed, f"All HTTP requests have timeouts" if passed else f"Found {len(issues)} requests without timeout")
|
|
228
|
+
|
|
229
|
+
if issues:
|
|
230
|
+
for issue in issues[:5]:
|
|
231
|
+
print_warning(f" {issue}")
|
|
232
|
+
|
|
233
|
+
return passed
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def check_dependencies() -> Tuple[bool, int]:
|
|
237
|
+
"""Check for outdated dependencies in project virtual environment only."""
|
|
238
|
+
print_header("Dependency Security Check")
|
|
239
|
+
|
|
240
|
+
# Determine which Python to use (prefer virtual environment)
|
|
241
|
+
venv_python = Path(".venv/bin/python")
|
|
242
|
+
if venv_python.exists():
|
|
243
|
+
python_cmd = str(venv_python)
|
|
244
|
+
pip_cmd = [python_cmd, "-m", "pip"]
|
|
245
|
+
print_result(True, "Using virtual environment (.venv)")
|
|
246
|
+
else:
|
|
247
|
+
python_cmd = "python3"
|
|
248
|
+
pip_cmd = ["pip3"]
|
|
249
|
+
print_warning("No virtual environment found, using system Python")
|
|
250
|
+
|
|
251
|
+
# Check for outdated packages in the environment
|
|
252
|
+
code, stdout, stderr = run_command(pip_cmd + ["list", "--outdated"])
|
|
253
|
+
|
|
254
|
+
outdated = len([l for l in stdout.split("\n") if l.strip()]) - 2 # Header lines
|
|
255
|
+
if outdated < 0:
|
|
256
|
+
outdated = 0
|
|
257
|
+
|
|
258
|
+
# More lenient threshold for virtual environments (they start fresh)
|
|
259
|
+
threshold = 20 if venv_python.exists() else 10
|
|
260
|
+
print_result(outdated < threshold, f"Outdated packages: {outdated}")
|
|
261
|
+
|
|
262
|
+
# Check for vulnerable packages using safety within the environment
|
|
263
|
+
code, stdout, stderr = run_command([
|
|
264
|
+
python_cmd, "-m", "safety", "check", "--short-report"
|
|
265
|
+
], timeout=60)
|
|
266
|
+
|
|
267
|
+
if code == 0:
|
|
268
|
+
print_result(True, "No known vulnerabilities in dependencies")
|
|
269
|
+
return True, outdated
|
|
270
|
+
elif "No known security" in stdout:
|
|
271
|
+
print_result(True, "No known vulnerabilities in dependencies")
|
|
272
|
+
return True, outdated
|
|
273
|
+
elif "Command not found" in stderr or "No module named" in stderr:
|
|
274
|
+
# Safety not installed in this environment - check if requirements are pinned
|
|
275
|
+
print_warning("Safety not installed, checking for pinned dependencies")
|
|
276
|
+
pyproject = Path("pyproject.toml")
|
|
277
|
+
if pyproject.exists():
|
|
278
|
+
content = pyproject.read_text()
|
|
279
|
+
# Check if dependencies have version constraints
|
|
280
|
+
has_pins = ">=" in content or "==" in content or "~=" in content
|
|
281
|
+
print_result(has_pins, "Dependencies have version constraints in pyproject.toml")
|
|
282
|
+
return has_pins, outdated
|
|
283
|
+
return False, outdated
|
|
284
|
+
else:
|
|
285
|
+
# Count vulnerabilities - exclude system paths from count
|
|
286
|
+
vuln_lines = [l for l in stdout.split("\n") if "Vulnerability found" in l]
|
|
287
|
+
# Filter out system Python vulnerabilities (only count project deps)
|
|
288
|
+
project_vulns = [l for l in vuln_lines
|
|
289
|
+
if "/Library/Developer/" not in l
|
|
290
|
+
and "/usr/lib/" not in l
|
|
291
|
+
and "site-packages" not in l or ".venv" in l]
|
|
292
|
+
vuln_count = len(project_vulns)
|
|
293
|
+
|
|
294
|
+
if vuln_count == 0 and len(vuln_lines) > 0:
|
|
295
|
+
print_result(True, f"No project vulnerabilities (system: {len(vuln_lines)} ignored)")
|
|
296
|
+
else:
|
|
297
|
+
print_result(vuln_count == 0, f"Vulnerable packages: {vuln_count}")
|
|
298
|
+
return vuln_count == 0, outdated
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def check_domain_validation() -> bool:
|
|
302
|
+
"""Check that domain validation is in place."""
|
|
303
|
+
print_header("Domain Validation Check")
|
|
304
|
+
|
|
305
|
+
orchestrator_path = Path("orchestrator.py")
|
|
306
|
+
if not orchestrator_path.exists():
|
|
307
|
+
print_warning("orchestrator.py not found")
|
|
308
|
+
return False
|
|
309
|
+
|
|
310
|
+
content = orchestrator_path.read_text()
|
|
311
|
+
|
|
312
|
+
has_validation = "validate_domain" in content
|
|
313
|
+
has_sanitize = "sanitize_for_shell" in content or "safe_domain" in content
|
|
314
|
+
|
|
315
|
+
print_result(has_validation, "Domain validation function exists")
|
|
316
|
+
print_result(has_sanitize, "Shell sanitization in place")
|
|
317
|
+
|
|
318
|
+
return has_validation and has_sanitize
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def main():
|
|
322
|
+
parser = argparse.ArgumentParser(description="AIPT v2 Security Audit")
|
|
323
|
+
parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
|
|
324
|
+
parser.add_argument("--fix", action="store_true", help="Attempt to fix issues (not implemented)")
|
|
325
|
+
args = parser.parse_args()
|
|
326
|
+
|
|
327
|
+
print(f"\n{BOLD}{'='*60}{RESET}")
|
|
328
|
+
print(f"{BOLD} AIPT v2 SECURITY AUDIT{RESET}")
|
|
329
|
+
print(f"{BOLD}{'='*60}{RESET}")
|
|
330
|
+
|
|
331
|
+
results = []
|
|
332
|
+
|
|
333
|
+
# Run all checks
|
|
334
|
+
bandit_passed, high, medium, low = check_bandit(args.verbose)
|
|
335
|
+
results.append(("Bandit SAST", bandit_passed))
|
|
336
|
+
|
|
337
|
+
results.append(("Hardcoded Secrets", check_secrets()))
|
|
338
|
+
results.append(("Shell Commands", check_shell_commands()))
|
|
339
|
+
results.append(("Request Timeouts", check_request_timeouts()))
|
|
340
|
+
|
|
341
|
+
dep_passed, outdated = check_dependencies()
|
|
342
|
+
results.append(("Dependencies", dep_passed))
|
|
343
|
+
|
|
344
|
+
results.append(("Domain Validation", check_domain_validation()))
|
|
345
|
+
|
|
346
|
+
# Summary
|
|
347
|
+
print_header("AUDIT SUMMARY")
|
|
348
|
+
|
|
349
|
+
passed = sum(1 for _, p in results if p)
|
|
350
|
+
total = len(results)
|
|
351
|
+
|
|
352
|
+
for name, result in results:
|
|
353
|
+
print_result(result, name)
|
|
354
|
+
|
|
355
|
+
print(f"\n{BOLD}Score: {passed}/{total} checks passed{RESET}")
|
|
356
|
+
|
|
357
|
+
if passed == total:
|
|
358
|
+
print(f"\n{GREEN}{BOLD}All security checks passed!{RESET}")
|
|
359
|
+
return 0
|
|
360
|
+
else:
|
|
361
|
+
print(f"\n{YELLOW}{BOLD}Some security checks failed. Review the issues above.{RESET}")
|
|
362
|
+
return 1
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
if __name__ == "__main__":
|
|
366
|
+
sys.exit(main())
|