#!/usr/bin/env python3 """ Phase 1 validation script. Validates that all Phase 1 deliverables are in place and code structure is correct. Does not require dependencies to be installed. """ import ast import os import sys from pathlib import Path def validate_file_exists(file_path, description): """Check if a file exists.""" if Path(file_path).exists(): print(f"✓ {description}: {file_path}") return True else: print(f"✗ {description} missing: {file_path}") return False def validate_directory_exists(dir_path, description): """Check if a directory exists.""" if Path(dir_path).is_dir(): print(f"✓ {description}: {dir_path}") return True else: print(f"✗ {description} missing: {dir_path}") return False def validate_python_syntax(file_path): """Validate Python file syntax.""" try: with open(file_path, 'r') as f: ast.parse(f.read()) return True except SyntaxError as e: print(f" ✗ Syntax error in {file_path}: {e}") return False def main(): """Run all validation checks.""" print("=" * 70) print("SneakyScanner Phase 1 Validation") print("=" * 70) all_passed = True # Check project structure print("\n1. Project Structure:") print("-" * 70) structure_checks = [ ("web/", "Web application directory"), ("web/api/", "API blueprints directory"), ("web/templates/", "Jinja2 templates directory"), ("web/static/", "Static files directory"), ("web/utils/", "Utility modules directory"), ("migrations/", "Alembic migrations directory"), ("migrations/versions/", "Migration versions directory"), ] for path, desc in structure_checks: if not validate_directory_exists(path, desc): all_passed = False # Check core files print("\n2. Core Files:") print("-" * 70) core_files = [ ("requirements-web.txt", "Web dependencies"), ("alembic.ini", "Alembic configuration"), ("init_db.py", "Database initialization script"), ("docker-compose-web.yml", "Docker Compose for web app"), ] for path, desc in core_files: if not validate_file_exists(path, desc): all_passed = False # Check Python modules print("\n3. Python Modules:") print("-" * 70) python_modules = [ ("web/__init__.py", "Web package init"), ("web/models.py", "SQLAlchemy models"), ("web/app.py", "Flask application factory"), ("web/utils/__init__.py", "Utils package init"), ("web/utils/settings.py", "Settings manager"), ("web/api/__init__.py", "API package init"), ("web/api/scans.py", "Scans API blueprint"), ("web/api/schedules.py", "Schedules API blueprint"), ("web/api/alerts.py", "Alerts API blueprint"), ("web/api/settings.py", "Settings API blueprint"), ("migrations/env.py", "Alembic environment"), ("migrations/script.py.mako", "Migration template"), ("migrations/versions/001_initial_schema.py", "Initial migration"), ] for path, desc in python_modules: exists = validate_file_exists(path, desc) if exists: # Skip syntax check for .mako templates (they're not pure Python) if not path.endswith('.mako'): if not validate_python_syntax(path): all_passed = False else: print(f" (Skipped syntax check for template file)") else: all_passed = False # Check models print("\n4. Database Models (from models.py):") print("-" * 70) try: # Read models.py and look for class definitions with open('web/models.py', 'r') as f: content = f.read() tree = ast.parse(content) models = [] for node in ast.walk(tree): if isinstance(node, ast.ClassDef) and node.name != 'Base': models.append(node.name) expected_models = [ 'Scan', 'ScanSite', 'ScanIP', 'ScanPort', 'ScanService', 'ScanCertificate', 'ScanTLSVersion', 'Schedule', 'Alert', 'AlertRule', 'Setting' ] for model in expected_models: if model in models: print(f"✓ Model defined: {model}") else: print(f"✗ Model missing: {model}") all_passed = False except Exception as e: print(f"✗ Failed to parse models.py: {e}") all_passed = False # Check API endpoints print("\n5. API Blueprints:") print("-" * 70) blueprints = { 'web/api/scans.py': ['list_scans', 'get_scan', 'trigger_scan', 'delete_scan'], 'web/api/schedules.py': ['list_schedules', 'get_schedule', 'create_schedule'], 'web/api/alerts.py': ['list_alerts', 'list_alert_rules'], 'web/api/settings.py': ['get_settings', 'update_settings'], } for blueprint_file, expected_funcs in blueprints.items(): try: with open(blueprint_file, 'r') as f: content = f.read() tree = ast.parse(content) functions = [node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)] print(f"\n {blueprint_file}:") for func in expected_funcs: if func in functions: print(f" ✓ Endpoint: {func}") else: print(f" ✗ Missing endpoint: {func}") all_passed = False except Exception as e: print(f" ✗ Failed to parse {blueprint_file}: {e}") all_passed = False # Summary print("\n" + "=" * 70) if all_passed: print("✓ All Phase 1 validation checks passed!") print("\nNext steps:") print("1. Install dependencies: pip install -r requirements-web.txt") print("2. Initialize database: python3 init_db.py --password YOUR_PASSWORD") print("3. Run Flask app: python3 -m web.app") print("4. Test API: curl http://localhost:5000/api/settings/health") return 0 else: print("✗ Some validation checks failed. Please review errors above.") return 1 if __name__ == '__main__': sys.exit(main())