diff --git a/TOOLS.md b/TOOLS.md index 1bde312..f621c5b 100644 --- a/TOOLS.md +++ b/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 | diff --git a/tools/security_audit.py b/tools/security_audit.py new file mode 100755 index 0000000..b8df034 --- /dev/null +++ b/tools/security_audit.py @@ -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())