9.0 KiB
EU-Utility Security Fixes Applied
Date: 2026-02-14
Auditor: Security Auditor Agent
Summary
This document details the security fixes applied during the security audit of EU-Utility.
Modules Fixed: 4
Security Improvements: 15+
Risk Level Reduced: MEDIUM-HIGH → LOW-MEDIUM
Fixes Applied
1. ✅ data_store.py - Path Traversal Protection
Action: Replaced vulnerable module with secure version
Changes:
- Backup created:
data_store_vulnerable.py - Active module now:
data_store_secure.py
Security Features Added:
- Path validation using
PathValidatorclass - Resolved path verification against base directory
- Plugin ID validation (type checking, empty checks)
- Key validation against dangerous patterns
- Data structure validation before save
- Backup path traversal protection
Code Example:
def _get_plugin_file(self, plugin_id: str) -> Path:
# Validate plugin_id
if not isinstance(plugin_id, str):
raise SecurityError("plugin_id must be a string")
# Sanitize and validate
safe_name = PathValidator.sanitize_filename(plugin_id, '_')
if '..' in safe_name or '/' in safe_name or '\\' in safe_name:
raise SecurityError(f"Invalid characters in plugin_id: {plugin_id}")
# Verify resolved path is within base directory
file_path = self.data_dir / f"{safe_name}.json"
resolved_path = file_path.resolve()
if not str(resolved_path).startswith(str(self._base_path)):
raise SecurityError(f"Path traversal detected: {plugin_id}")
return file_path
2. ✅ screenshot.py - Filename Sanitization & Path Validation
Action: Replaced vulnerable module with secure version
Changes:
- Backup created:
screenshot_vulnerable.py - Active module now:
screenshot_secure.py
Security Features Added:
- Filename sanitization using
PathValidator.sanitize_filename() - Path resolution validation against base save path
- Region coordinate validation (prevent DoS via huge regions)
- Window handle validation (type and value checking)
- Dimension sanity checks
Code Example:
def save_screenshot(self, image: Image.Image, filename: Optional[str] = None) -> Path:
# Sanitize filename
safe_filename = PathValidator.sanitize_filename(filename, '_')
filepath = self._save_path / safe_filename
# Security check: ensure resolved path is within save_path
try:
resolved_path = filepath.resolve()
if not str(resolved_path).startswith(str(self._base_save_path)):
raise SecurityError("Path traversal detected in filename")
except (OSError, ValueError) as e:
# Fallback to safe default
safe_filename = f"screenshot_{int(time.time())}.{self._format.lower()}"
filepath = self._save_path / safe_filename
3. ✅ clipboard.py - Input Validation & Size Limits
Action: Enhanced with security validation
Security Features Added:
- Maximum text length limit (10KB per entry)
- Maximum total storage limit (1MB)
- Null byte detection and rejection
- Text sanitization (control character removal)
- Source string length limiting
- Secure file permissions (0o600 - owner only)
- Temporary file atomic write pattern
Code Example:
def _validate_text(self, text: str) -> tuple[bool, str]:
if not isinstance(text, str):
return False, "Text must be a string"
if '\x00' in text:
return False, "Text contains null bytes"
if len(text) > self._max_text_length:
return False, f"Text exceeds maximum length"
# Auto-cleanup to make room
current_size = sum(len(entry.text) for entry in self._history)
if current_size + len(text) > self._max_total_storage:
while self._history and current_size + len(text) > self._max_total_storage:
removed = self._history.popleft()
current_size -= len(removed.text)
return True, ""
def _save_history(self):
# Write with restricted permissions
temp_path = self._history_file.with_suffix('.tmp')
with open(temp_path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2)
os.chmod(temp_path, 0o600) # Owner read/write only
temp_path.replace(self._history_file)
4. ✅ http_client.py - URL Validation & SSRF Protection
Action: Enhanced with security validation
Security Features Added:
- URL scheme validation (only http:// and https:// allowed)
- Path traversal pattern detection (
..,@,\, null bytes) - SSRF protection (blocks private, loopback, reserved, link-local IPs)
- Custom exception classes for security errors
- URL validation on both GET and POST requests
Code Example:
def _validate_url(self, url: str) -> str:
if not url:
raise URLSecurityError("URL cannot be empty")
from urllib.parse import urlparse
parsed = urlparse(url)
# Check scheme
allowed_schemes = {'http', 'https'}
if parsed.scheme not in allowed_schemes:
raise URLSecurityError(f"URL scheme '{parsed.scheme}' not allowed")
# Check for dangerous patterns
dangerous_patterns = ['..', '@', '\\', '\x00']
for pattern in dangerous_patterns:
if pattern in url:
raise URLSecurityError(f"URL contains dangerous pattern")
# SSRF protection - block private IPs
hostname = parsed.hostname
if hostname:
try:
ip = ipaddress.ip_address(hostname)
if ip.is_private or ip.is_loopback or ip.is_reserved:
raise URLSecurityError(f"URL resolves to restricted IP")
except ValueError:
pass # Not an IP, it's a hostname
return url
Files Modified
| File | Action | Status |
|---|---|---|
core/data_store.py |
Replaced with secure version | ✅ Fixed |
core/data_store_vulnerable.py |
Backup created | ✅ Archived |
core/screenshot.py |
Replaced with secure version | ✅ Fixed |
core/screenshot_vulnerable.py |
Backup created | ✅ Archived |
core/clipboard.py |
Enhanced with validation | ✅ Fixed |
core/http_client.py |
Added URL validation | ✅ Fixed |
Remaining Recommendations
Medium Priority (Not Yet Implemented)
-
Plugin Manager Security
- Implement plugin signature verification
- Add permission manifest system
- Consider sandboxed execution environment
-
Audit Logging
- Log all security violations
- Log plugin loading/unloading
- Log file operations outside data directories
Low Priority (Future Enhancements)
-
Data Encryption
- Encrypt sensitive plugin data at rest
- Encrypt clipboard history with user password
-
Rate Limiting
- Per-plugin API rate limits
- Screenshot capture rate limiting
Testing Security Fixes
Path Traversal Test
from core.data_store import get_data_store
data_store = get_data_store()
# Should raise SecurityError
try:
data_store.save("../../../etc/passwd", "key", "data")
except SecurityError:
print("✅ Path traversal blocked in data_store")
# Should raise SecurityError
try:
from core.screenshot import get_screenshot_service
service = get_screenshot_service()
service.save_screenshot(image, "../../../malware.exe")
except SecurityError:
print("✅ Path traversal blocked in screenshot")
URL Validation Test
from core.http_client import HTTPClient, URLSecurityError
client = HTTPClient()
# Should raise URLSecurityError
try:
client.get("file:///etc/passwd")
except URLSecurityError:
print("✅ File protocol blocked")
try:
client.get("http://127.0.0.1/admin")
except URLSecurityError:
print("✅ Localhost blocked (SSRF protection)")
try:
client.get("http://192.168.1.1/admin")
except URLSecurityError:
print("✅ Private IP blocked (SSRF protection)")
Clipboard Validation Test
from core.clipboard import get_clipboard_manager
clipboard = get_clipboard_manager()
# Should fail - too large
result = clipboard.copy("x" * (11 * 1024)) # 11KB
assert result == False, "Should reject oversized text"
print("✅ Size limit enforced")
# Should sanitize null bytes
result = clipboard.copy("hello\x00world")
assert result == False, "Should reject null bytes"
print("✅ Null byte protection working")
Security Checklist
- Path traversal protection (data_store)
- Path traversal protection (screenshot)
- Filename sanitization
- Input validation (clipboard)
- Size limits (clipboard)
- URL validation (http_client)
- SSRF protection (http_client)
- Secure file permissions
- Atomic file writes
- Data structure validation
- Plugin signature verification (future)
- Plugin sandboxing (future)
- Audit logging (future)
- Data encryption (future)
Contact
For questions about these security fixes, refer to:
SECURITY_AUDIT_REPORT.md- Full audit reportcore/security_utils.py- Security utility classes
Security fixes applied by Security Auditor Agent
EU-Utility Security Hardening 2026