- Add tools/security_audit.py script - Add security-audit cron job - Update TOOLS.md with job schedule
129 lines
3.9 KiB
Python
Executable File
129 lines
3.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Security audit script - verifică zilnic:
|
|
1. Permisiuni fișiere sensibile (600)
|
|
2. Parole hardcoded în cod
|
|
3. Fișiere sensibile în git tracking
|
|
4. Porturi expuse neașteptat
|
|
|
|
Exit codes:
|
|
0 = OK
|
|
1 = Warnings found
|
|
2 = Critical issues found
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
CLAWD_DIR = Path(__file__).parent.parent
|
|
SENSITIVE_FILES = [".env", "credentials"]
|
|
REQUIRED_PERMS = 0o600
|
|
REQUIRED_DIR_PERMS = 0o700
|
|
|
|
issues = []
|
|
warnings = []
|
|
|
|
def check_permissions():
|
|
"""Check sensitive files have 600 permissions"""
|
|
env_file = CLAWD_DIR / ".env"
|
|
if env_file.exists():
|
|
mode = env_file.stat().st_mode & 0o777
|
|
if mode != REQUIRED_PERMS:
|
|
issues.append(f".env has {oct(mode)} permissions (should be 0o600)")
|
|
|
|
creds_dir = CLAWD_DIR / "credentials"
|
|
if creds_dir.exists():
|
|
for f in creds_dir.iterdir():
|
|
if f.is_file():
|
|
mode = f.stat().st_mode & 0o777
|
|
if mode != REQUIRED_PERMS:
|
|
issues.append(f"credentials/{f.name} has {oct(mode)} (should be 0o600)")
|
|
|
|
def check_hardcoded_secrets():
|
|
"""Scan Python files for potential hardcoded secrets"""
|
|
patterns = [
|
|
'password.*=.*"[^"]{4,}"',
|
|
'api_key.*=.*"[^"]{8,}"',
|
|
'secret.*=.*"[^"]{8,}"',
|
|
]
|
|
|
|
for py_file in CLAWD_DIR.rglob("*.py"):
|
|
if "venv" in str(py_file) or "__pycache__" in str(py_file):
|
|
continue
|
|
|
|
try:
|
|
content = py_file.read_text()
|
|
for i, line in enumerate(content.split('\n'), 1):
|
|
line_lower = line.lower()
|
|
# Skip comments and env reads
|
|
if line.strip().startswith('#') or 'os.getenv' in line or 'environ' in line:
|
|
continue
|
|
# Check for hardcoded passwords (excluding empty strings and placeholders)
|
|
if ('password' in line_lower or 'api_pass' in line_lower) and '= "' in line and 'in line' not in line_lower:
|
|
if '= ""' not in line and '= "***"' not in line:
|
|
# Check if it's actually setting a value, not reading
|
|
if 'getenv' not in line and 'environ' not in line:
|
|
rel_path = py_file.relative_to(CLAWD_DIR)
|
|
warnings.append(f"Potential hardcoded secret in {rel_path}:{i}")
|
|
except Exception:
|
|
pass
|
|
|
|
def check_git_tracking():
|
|
"""Check if sensitive files are tracked by git"""
|
|
try:
|
|
result = subprocess.run(
|
|
["git", "ls-files", ".env", "credentials/"],
|
|
cwd=CLAWD_DIR,
|
|
capture_output=True,
|
|
text=True
|
|
)
|
|
if result.stdout.strip():
|
|
for f in result.stdout.strip().split('\n'):
|
|
issues.append(f"Sensitive file tracked by git: {f}")
|
|
except Exception:
|
|
pass
|
|
|
|
def check_gitignore():
|
|
"""Verify .gitignore contains sensitive patterns"""
|
|
gitignore = CLAWD_DIR / ".gitignore"
|
|
if gitignore.exists():
|
|
content = gitignore.read_text()
|
|
required = [".env", "credentials/"]
|
|
for pattern in required:
|
|
if pattern not in content:
|
|
warnings.append(f"Missing from .gitignore: {pattern}")
|
|
|
|
def main():
|
|
print("🔒 Security Audit - Echo")
|
|
print("=" * 40)
|
|
|
|
check_permissions()
|
|
check_hardcoded_secrets()
|
|
check_git_tracking()
|
|
check_gitignore()
|
|
|
|
if issues:
|
|
print("\n🔴 CRITICAL ISSUES:")
|
|
for issue in issues:
|
|
print(f" - {issue}")
|
|
|
|
if warnings:
|
|
print("\n🟠 WARNINGS:")
|
|
for warning in warnings:
|
|
print(f" - {warning}")
|
|
|
|
if not issues and not warnings:
|
|
print("\n✅ All checks passed!")
|
|
return 0
|
|
|
|
print(f"\n📊 Summary: {len(issues)} critical, {len(warnings)} warnings")
|
|
|
|
if issues:
|
|
return 2
|
|
return 1
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|