Add URL whitelist enforcement for Kiosk-mode and enhance configuration management

- Introduced `allowed_urls` in configuration to specify whitelisted domains/patterns.
- Implemented `RestrictedWebEngineView` to enforce URL restrictions in the web view.
- Updated `MainWindow` to utilize the new restricted web view and added navigation toolbar.
- Enhanced unit tests for configuration and restricted web view to cover new functionality.
This commit is contained in:
claudi 2026-01-28 11:33:37 +01:00
parent 6bef2f6119
commit 86034358b7
6 changed files with 529 additions and 33 deletions

View file

@ -0,0 +1,101 @@
"""Restricted web view with URL whitelist enforcement for Kiosk-mode."""
import fnmatch
from typing import List, Optional
from urllib.parse import urlparse
from PySide6.QtCore import QUrl
from PySide6.QtGui import QDesktopServices
from PySide6.QtWebEngineCore import QWebEngineNavigationRequest
from PySide6.QtWebEngineWidgets import QWebEngineView
class RestrictedWebEngineView(QWebEngineView):
"""Web view that enforces URL whitelist for Kiosk-mode security.
If allowed_urls is empty, no restrictions are applied.
If allowed_urls is not empty, only matching URLs are loaded in the view.
Non-matching URLs open in the system default browser.
"""
def __init__(self, allowed_urls: Optional[List[str]] = None):
"""Initialize the restricted web view.
Args:
allowed_urls: List of allowed URL patterns (empty = no restriction)
Patterns support wildcards: *.example.com, localhost, etc.
"""
super().__init__()
self.allowed_urls = allowed_urls or []
# Connect to navigation request handler
self.page().navigationRequested.connect(self._on_navigation_requested)
def _on_navigation_requested(
self, request: QWebEngineNavigationRequest
) -> None:
"""Handle navigation requests and enforce URL whitelist.
Args:
request: Navigation request to process
"""
url = request.url
# If no restrictions, allow all URLs
if not self.allowed_urls:
return
# Check if URL matches whitelist
if self._is_url_allowed(url):
# Allow the navigation (default behavior)
return
# URL not whitelisted - open in system browser
request.reject()
QDesktopServices.openUrl(url)
def _is_url_allowed(self, url: QUrl) -> bool:
"""Check if a URL matches the whitelist.
Supports:
- Exact domain matches: example.com
- Wildcard patterns: *.example.com
- Localhost variations: localhost, 127.0.0.1
- File URLs: file://...
Args:
url: QUrl to check
Returns:
True if URL is allowed, False otherwise
"""
url_str = url.toString()
host = url.host()
scheme = url.scheme()
# Allow file:// URLs (local webapp)
if scheme == "file":
return True
# If no whitelist, allow all URLs
if not self.allowed_urls:
return True
# Check against whitelist patterns
for pattern in self.allowed_urls:
# Check full URL pattern
if fnmatch.fnmatch(url_str, pattern):
return True
# Check host pattern (for http/https URLs)
if scheme in ("http", "https") and host:
if fnmatch.fnmatch(host, pattern):
return True
# Also check with www prefix variants
if fnmatch.fnmatch(f"www.{host}", pattern):
return True
if fnmatch.fnmatch(host.replace("www.", ""), pattern):
return True
return False