feat: add daily security audit job (07:00 București)
- Add tools/security_audit.py script - Add security-audit cron job - Update TOOLS.md with job schedule
This commit is contained in:
1
TOOLS.md
1
TOOLS.md
@@ -234,6 +234,7 @@ create_event(
|
||||
| Oră (UTC) | Oră (București) | Job | Canal | Ce face |
|
||||
|-----------|-----------------|-----|-------|---------|
|
||||
| 00:00 | 02:00 | content-discovery | - | Caută video+articole pe teme recente → memory/kb/ |
|
||||
| 05:00 | 07:00 | security-audit | #echo-work (doar alerte) | Audit securitate zilnic |
|
||||
| 01:00 | 03:00 | night-execute-late | #echo-work | Continuă execuția task-uri (run 2) |
|
||||
| 03:00 | 05:00 | archive-tasks | #echo-work | Arhivează task-uri vechi |
|
||||
| 06:00,17:00 | 08:00,19:00 | insights-extract | - | Extrage insights din memory/kb/ + actualizează tehnici-pauza.md |
|
||||
|
||||
128
tools/security_audit.py
Executable file
128
tools/security_audit.py
Executable file
@@ -0,0 +1,128 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user