Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 4 additions & 43 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,48 +1,9 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
ENV/
env.bak/
venv.bak/
*.egg-info/
.pytest_cache/
.venv/
.env
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
*.egg-info/
160 changes: 160 additions & 0 deletions src/vpn_config_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import subprocess
import re
from typing import Dict, Optional, List
import logging
import platform

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class VPNConfigParser:
"""
A utility class for parsing VPN configuration details from system commands.

Supports multiple platforms and VPN types with modular parsing strategies.
"""

@staticmethod
def run_command(command: List[str]) -> str:
"""
Execute a system command and return its output.

Args:
command (List[str]): Command to execute as a list of strings

Returns:
str: Command output

Raises:
subprocess.CalledProcessError: If command execution fails
"""
try:
result = subprocess.run(
command,
capture_output=True,
text=True,
check=True
)
return result.stdout.strip()
except subprocess.CalledProcessError as e:
logger.error(f"Command execution failed: {e}")
raise

@classmethod
def parse_macos_vpn_config(cls) -> Dict[str, Optional[str]]:
"""
Parse VPN configuration on macOS using system commands.

Returns:
Dict[str, Optional[str]]: VPN configuration details
"""
try:
# Retrieve network service configurations
network_services_output = cls.run_command(
["networksetup", "-listnetworkserviceorder"]
)

# Search for VPN-related services
vpn_services = re.findall(
r'\(\d+\)\s+(.*\(VPN\))',
network_services_output
)

# Detailed configuration parsing
config = {
"platform": "darwin",
"vpn_services": vpn_services,
"active_vpn": None,
"vpn_type": None
}

# Check active VPN connections
try:
scutil_output = cls.run_command(
["scutil", "--nc", "status"]
)
active_vpns = [
line.split(":")[0].strip()
for line in scutil_output.split("\n")
if "Connected" in line
]
config["active_vpn"] = active_vpns[0] if active_vpns else None
except Exception as e:
logger.warning(f"Could not retrieve active VPN: {e}")

return config

except Exception as e:
logger.error(f"macOS VPN config parsing failed: {e}")
return {
"platform": "darwin",
"vpn_services": [],
"active_vpn": None,
"vpn_type": None
}

@classmethod
def parse_linux_vpn_config(cls) -> Dict[str, Optional[str]]:
"""
Parse VPN configuration on Linux systems.

Returns:
Dict[str, Optional[str]]: VPN configuration details
"""
try:
# Retrieve network interfaces
ip_output = cls.run_command(["ip", "tuntap", "show"])

# Look for VPN-related interfaces
vpn_interfaces = re.findall(r'(tun\d+)', ip_output)

config = {
"platform": "linux",
"vpn_interfaces": vpn_interfaces,
"active_vpn": None,
"vpn_type": None
}

# Check for active connections via routing table
try:
route_output = cls.run_command(["ip", "route", "show", "table", "all"])
vpn_routes = [
line for line in route_output.split("\n")
if "tun" in line
]
config["active_vpn"] = vpn_routes[0] if vpn_routes else None
except Exception as e:
logger.warning(f"Could not retrieve active VPN routes: {e}")

return config

except Exception as e:
logger.error(f"Linux VPN config parsing failed: {e}")
return {
"platform": "linux",
"vpn_interfaces": [],
"active_vpn": None,
"vpn_type": None
}

@classmethod
def detect_vpn_config(cls) -> Dict[str, Optional[str]]:
"""
Detect VPN configuration based on the current platform.

Returns:
Dict[str, Optional[str]]: Parsed VPN configuration
"""
os_name = platform.system().lower()

if os_name == "darwin":
return cls.parse_macos_vpn_config()
elif os_name == "linux":
return cls.parse_linux_vpn_config()
else:
logger.warning(f"Unsupported platform: {os_name}")
return {
"platform": os_name,
"active_vpn": None,
"vpn_type": None
}
54 changes: 54 additions & 0 deletions tests/test_vpn_config_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import pytest
import platform
from unittest.mock import patch
from src.vpn_config_parser import VPNConfigParser

def test_run_command_success():
"""Test successful command execution."""
with patch('subprocess.run') as mock_run:
mock_run.return_value.stdout = "test output\n"
mock_run.return_value.returncode = 0

result = VPNConfigParser.run_command(["echo", "test"])
assert result == "test output"

def test_run_command_failure():
"""Test command execution failure."""
with patch('subprocess.run') as mock_run:
mock_run.side_effect = Exception("Command failed")

with pytest.raises(Exception):
VPNConfigParser.run_command(["invalid_command"])

@pytest.mark.skipif(platform.system() != "Darwin", reason="macOS specific test")
def test_macos_vpn_config_parsing():
"""Test VPN configuration parsing on macOS."""
with patch.object(VPNConfigParser, 'run_command') as mock_run:
mock_run.side_effect = [
"(1) VPN (Service)\n(2) Wi-Fi (Built-in)",
"Connected: VPN Service"
]

config = VPNConfigParser.parse_macos_vpn_config()
assert "VPN (Service)" in config["vpn_services"]
assert config["active_vpn"] is not None

@pytest.mark.skipif(platform.system() != "Linux", reason="Linux specific test")
def test_linux_vpn_config_parsing():
"""Test VPN configuration parsing on Linux."""
with patch.object(VPNConfigParser, 'run_command') as mock_run:
mock_run.side_effect = [
"tun0: tun\ntun1: tun",
"default via 192.168.1.1 dev tun0"
]

config = VPNConfigParser.parse_linux_vpn_config()
assert len(config["vpn_interfaces"]) > 0
assert config["active_vpn"] is not None

def test_detect_vpn_config():
"""Test VPN configuration detection across platforms."""
config = VPNConfigParser.detect_vpn_config()
assert "platform" in config
assert "active_vpn" in config
assert "vpn_type" in config