diff --git a/.gitignore b/.gitignore index 284e270..eece8ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,48 +1,9 @@ -# Python __pycache__/ *.py[cod] *$py.class -*.so -.Python -env/ +.pytest_cache/ +.env +.venv venv/ -ENV/ -env.bak/ -venv.bak/ -*.egg-info/ -dist/ -build/ - -# Security Analysis Results (user-generated) -*.json -*_results.txt -*_report.txt -*_audit.json -daily_check.json -weekly_audit.json -security_check.json - -# macOS -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db - -# IDE -.vscode/ -.idea/ -*.swp -*.swo -*~ - -# Temporary files -*.tmp -*.temp -*.log - -# User configuration -config.local.py -.env \ No newline at end of file +.log +*.log \ No newline at end of file diff --git a/src/vpn_config_detector.py b/src/vpn_config_detector.py new file mode 100644 index 0000000..28381e9 --- /dev/null +++ b/src/vpn_config_detector.py @@ -0,0 +1,163 @@ +import logging +import subprocess +import platform +from typing import Dict, Optional, List, Any + +class VPNConfigurationError(Exception): + """Custom exception for VPN configuration detection errors.""" + pass + +class VPNConfigDetector: + """ + A comprehensive VPN configuration detection and analysis class. + + Handles error detection, logging, and retrieval of VPN connection details. + """ + + def __init__(self, log_level: int = logging.INFO): + """ + Initialize VPN configuration detector with logging. + + Args: + log_level (int): Logging level, defaults to INFO + """ + logging.basicConfig( + level=log_level, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + self.logger = logging.getLogger(__name__) + + def detect_vpn_connections(self) -> List[Dict[str, Any]]: + """ + Detect active VPN connections across different interfaces. + + Returns: + List of dictionaries containing VPN connection details + + Raises: + VPNConfigurationError: If detection fails + """ + try: + # Detect OS and use appropriate method + os_name = platform.system().lower() + + if os_name == 'darwin': # macOS + return self._detect_vpn_macos() + elif os_name == 'linux': + return self._detect_vpn_linux() + else: + self.logger.warning(f"Unsupported OS: {os_name}") + return [] + + except Exception as e: + self.logger.error(f"Unexpected error during VPN detection: {e}") + raise VPNConfigurationError(f"Unexpected VPN detection error: {e}") + + def _detect_vpn_macos(self) -> List[Dict[str, Any]]: + """ + Detect VPN connections on macOS. + + Returns: + List of VPN connection details + """ + try: + result = subprocess.run( + ['scutil', '--nc', 'list'], + capture_output=True, + text=True, + timeout=5 + ) + + # Basic parsing of scutil output + vpn_connections = [] + for line in result.stdout.split('\n'): + if 'VPN' in line or 'Connected' in line: + vpn_connections.append({ + 'interface': 'utun', + 'status': 'active', + 'protocol': 'Unknown' + }) + + if not vpn_connections: + self.logger.info("No VPN connections detected on macOS") + + return vpn_connections + + except subprocess.TimeoutExpired: + self.logger.error("macOS VPN detection timed out") + return [] + except Exception as e: + self.logger.error(f"macOS VPN detection error: {e}") + return [] + + def _detect_vpn_linux(self) -> List[Dict[str, Any]]: + """ + Detect VPN connections on Linux. + + Returns: + List of VPN connection details + """ + try: + # Check for typical VPN interface names + vpn_interfaces = ['tun', 'tap', 'ppp'] + vpn_connections = [] + + result = subprocess.run( + ['ip', 'link', 'show'], + capture_output=True, + text=True, + timeout=5 + ) + + for line in result.stdout.split('\n'): + for interface in vpn_interfaces: + if interface in line.lower(): + vpn_connections.append({ + 'interface': line.split(':')[1].strip(), + 'status': 'active', + 'protocol': 'OpenVPN' if 'tun' in interface else 'Unknown' + }) + + if not vpn_connections: + self.logger.info("No VPN connections detected on Linux") + + return vpn_connections + + except subprocess.TimeoutExpired: + self.logger.error("Linux VPN detection timed out") + return [] + except Exception as e: + self.logger.error(f"Linux VPN detection error: {e}") + return [] + + def validate_vpn_configuration(self, config: Dict[str, Any]) -> bool: + """ + Validate VPN configuration for potential security issues. + + Args: + config (Dict[str, Any]): VPN configuration to validate + + Returns: + bool: Whether configuration passes basic security checks + """ + try: + if not config: + self.logger.warning("Empty VPN configuration") + return False + + # Add specific validation checks + if config.get('protocol') not in ['OpenVPN', 'WireGuard', 'IPSec', 'Unknown']: + self.logger.warning(f"Unsupported VPN protocol: {config.get('protocol')}") + return False + + return True + + except Exception as e: + self.logger.error(f"Configuration validation error: {e}") + return False + +# Configure default logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) \ No newline at end of file diff --git a/tests/test_vpn_config_detector.py b/tests/test_vpn_config_detector.py new file mode 100644 index 0000000..d0b885d --- /dev/null +++ b/tests/test_vpn_config_detector.py @@ -0,0 +1,43 @@ +import pytest +import logging +from src.vpn_config_detector import VPNConfigDetector, VPNConfigurationError + +def test_vpn_config_detector_initialization(): + """Test VPN configuration detector initialization.""" + detector = VPNConfigDetector() + assert detector is not None + assert detector.logger is not None + +def test_detect_vpn_connections_empty(): + """Test VPN connection detection with no connections.""" + detector = VPNConfigDetector(log_level=logging.DEBUG) + connections = detector.detect_vpn_connections() + assert isinstance(connections, list) + +def test_validate_vpn_configuration(): + """Test VPN configuration validation.""" + detector = VPNConfigDetector() + + # Test valid configuration + valid_config = { + 'protocol': 'OpenVPN', + 'interface': 'tun0' + } + assert detector.validate_vpn_configuration(valid_config) is True + + # Test invalid configuration + invalid_config = { + 'protocol': 'UnknownProtocol' + } + assert detector.validate_vpn_configuration(invalid_config) is False + assert detector.validate_vpn_configuration({}) is False + +def test_error_handling(): + """Test error handling in VPN configuration detection.""" + detector = VPNConfigDetector(log_level=logging.DEBUG) + + # Test with an edge case configuration + assert detector.validate_vpn_configuration({ + 'protocol': 'Unknown', + 'interface': 'testing' + }) is True \ No newline at end of file