#!/usr/bin/env python3 """ šŸ” ROA2WEB Security Validation Tool Validates that all security measures are properly implemented and functioning. """ import os import sys import subprocess from pathlib import Path class SecurityValidator: """Validates security implementation""" def __init__(self, repo_path: str = "."): self.repo_path = Path(repo_path) self.errors = [] self.warnings = [] self.checks_passed = 0 self.total_checks = 0 def check(self, condition: bool, success_msg: str, error_msg: str, is_warning: bool = False): """Check a condition and track results""" self.total_checks += 1 if condition: print(f"āœ… {success_msg}") self.checks_passed += 1 else: if is_warning: print(f"āš ļø {error_msg}") self.warnings.append(error_msg) else: print(f"āŒ {error_msg}") self.errors.append(error_msg) def validate_files_exist(self): """Validate that all security files exist""" print("\nšŸ” Checking Security Files...") required_files = [ "security/secrets_scanner.py", "security/git_cleanup.py", "security/install_hooks.sh", "security/setup_security.sh", "security/git_hooks/pre-commit", "security/git_hooks/commit-msg", "security/README.md", "security/SECURITY_PROCEDURES.md" ] for file_path in required_files: full_path = self.repo_path / file_path self.check( full_path.exists(), f"Security file exists: {file_path}", f"Missing security file: {file_path}" ) def validate_gitignore(self): """Validate .gitignore security patterns""" print("\nšŸ›”ļø Checking .gitignore Security...") gitignore_path = self.repo_path / ".gitignore" if not gitignore_path.exists(): self.check(False, "", ".gitignore file missing") return with open(gitignore_path, 'r') as f: gitignore_content = f.read() critical_patterns = [ "*.env.*", "!.env.example", "*.pem", "*.key", "*secret*", "*credential*", "*password*", ".serena/cache/", ".serena/memories/" ] for pattern in critical_patterns: self.check( pattern in gitignore_content, f"Security pattern in .gitignore: {pattern}", f"Missing .gitignore pattern: {pattern}", is_warning=True ) def validate_git_hooks(self): """Validate git hooks installation""" print("\nšŸŖ Checking Git Hooks...") hooks_dir = self.repo_path / ".git" / "hooks" self.check( hooks_dir.exists(), "Git hooks directory exists", "Git hooks directory missing" ) required_hooks = ["pre-commit", "commit-msg"] for hook in required_hooks: hook_path = hooks_dir / hook self.check( hook_path.exists(), f"Git hook installed: {hook}", f"Git hook missing: {hook}" ) if hook_path.exists(): self.check( os.access(hook_path, os.X_OK), f"Git hook executable: {hook}", f"Git hook not executable: {hook}" ) def validate_scripts_executable(self): """Validate that scripts are executable""" print("\nšŸ”§ Checking Script Permissions...") executable_scripts = [ "security/secrets_scanner.py", "security/git_cleanup.py", "security/install_hooks.sh", "security/setup_security.sh" ] for script in executable_scripts: script_path = self.repo_path / script if script_path.exists(): self.check( os.access(script_path, os.X_OK), f"Script executable: {script}", f"Script not executable: {script}" ) def validate_scanner_functionality(self): """Validate scanner can run""" print("\nšŸ” Testing Security Scanner...") try: # Import and test scanner sys.path.append(str(self.repo_path)) import security.secrets_scanner as scanner s = scanner.SecretsScanner(self.repo_path) self.check( len(s.CRITICAL_PATTERNS) > 0, f"Scanner patterns loaded: {len(s.CRITICAL_PATTERNS)} patterns", "Scanner patterns not loaded" ) self.check( len(s.SUSPICIOUS_FILES) > 0, f"Scanner file patterns loaded: {len(s.SUSPICIOUS_FILES)} patterns", "Scanner file patterns not loaded" ) except Exception as e: self.check( False, "Scanner functionality test passed", f"Scanner functionality test failed: {e}" ) def validate_git_repository(self): """Validate git repository status""" print("\nšŸ“¦ Checking Git Repository...") try: # Check if in git repo result = subprocess.run( ['git', 'status'], cwd=self.repo_path, capture_output=True, check=True ) self.check( True, "Git repository status OK", "Git repository issues detected" ) except subprocess.CalledProcessError: self.check( False, "Git repository status OK", "Not in a valid git repository" ) def check_environment_files(self): """Check for environment files that should be protected""" print("\nšŸ” Checking Environment Files...") # Find .env files env_files = [] for root, dirs, files in os.walk(self.repo_path): for file in files: if file.endswith('.env') and not file.endswith('.env.example'): rel_path = os.path.relpath(os.path.join(root, file), self.repo_path) env_files.append(rel_path) for env_file in env_files: # Check if file is in git tracking try: result = subprocess.run( ['git', 'ls-files', env_file], cwd=self.repo_path, capture_output=True, text=True ) if result.stdout.strip(): self.check( False, "", f"Environment file tracked in git: {env_file}", is_warning=True ) else: self.check( True, f"Environment file properly ignored: {env_file}", "" ) except subprocess.CalledProcessError: pass def run_validation(self): """Run complete security validation""" print("šŸ”’ ROA2WEB Security Validation") print("=" * 50) self.validate_files_exist() self.validate_gitignore() self.validate_git_hooks() self.validate_scripts_executable() self.validate_scanner_functionality() self.validate_git_repository() self.check_environment_files() # Print summary print("\n" + "=" * 50) print("šŸ“Š VALIDATION SUMMARY") print("=" * 50) print(f"āœ… Checks passed: {self.checks_passed}/{self.total_checks}") if self.errors: print(f"āŒ Errors: {len(self.errors)}") for error in self.errors: print(f" • {error}") if self.warnings: print(f"āš ļø Warnings: {len(self.warnings)}") for warning in self.warnings: print(f" • {warning}") # Overall status if not self.errors: if not self.warnings: print("\nšŸŽ‰ VALIDATION PASSED - Security implementation complete!") return True else: print("\nāœ… VALIDATION PASSED - Minor warnings noted") return True else: print("\nāŒ VALIDATION FAILED - Critical issues found") return False def main(): validator = SecurityValidator() success = validator.run_validation() sys.exit(0 if success else 1) if __name__ == "__main__": main()