Some checks are pending
Tests & Quality Checks / Test on Python 3.11 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.11-1 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12-1 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.10 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.11-2 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12-2 (push) Waiting to run
Tests & Quality Checks / Build Artifacts (push) Blocked by required conditions
Tests & Quality Checks / Build Artifacts-1 (push) Blocked by required conditions
10 KiB
10 KiB
Architecture Guide
Related Docs
High-Level Design
┌──────────────────────────────────────────────────────────┐
│ Application Layer (UI) │
│ ┌────────────────────────────────────────────────────────┤
│ │ QMainWindow (main_window.py) │
│ │ ├─ QWebEngineView (web app container) │
│ │ └─ DragInterceptor (drag handling) │
│ └────────────────────────────────────────────────────────┘
│ ↓ Events (dragEnterEvent, etc.)
├──────────────────────────────────────────────────────────┤
│ Business Logic Layer (Core) │
│ ┌────────────────────────────────────────────────────────┤
│ │ PathValidator (security & validation) │
│ │ DragInterceptor (drag/drop conversion) │
│ │ Config (configuration management) │
│ └────────────────────────────────────────────────────────┘
│ ↓ Platform APIs
├──────────────────────────────────────────────────────────┤
│ Platform Layer (OS Integration) │
│ ┌────────────────────────────────────────────────────────┤
│ │ QUrl (Windows: CF_HDROP, macOS: NSFilenamesPboardType)│
│ │ QMimeData (cross-platform mime data) │
│ │ QDrag (native drag-drop implementation) │
│ └────────────────────────────────────────────────────────┘
Module Organization
src/webdrop_bridge/core/
Purpose: Core business logic, independent of UI
Key Components:
validator.py: Path validation against whitelist with security checksdrag_interceptor.py: Drag event handling and native drag operationsconfig_manager.py: Configuration loading from files and cachingurl_converter.py: Azure Blob Storage URL → local path conversionupdater.py: Update checking via Forgejo API
Dependencies: None (only stdlib + pathlib)
src/webdrop_bridge/ui/
Purpose: User interface components using Qt/PySide6
Key Components:
main_window.py: Main application window with web engine integrationrestricted_web_view.py: Hardened QWebEngineView with security policiessettings_dialog.py: Settings UI for configurationupdate_manager_ui.py: Update checking and notification UIbridge_script_intercept.js: JavaScript drag interception and WebChannel bridge for Qt communicationdownload_interceptor.js: Download handling for web content
Dependencies: PySide6, core/
src/webdrop_bridge/utils/
Purpose: Shared utilities and helpers
Key Components:
logging.py: Logging configuration (console + file with rotation)
Dependencies: stdlib only
Data Flow
Drag-and-Drop Operation
User in Web App (browser)
↓
[dragstart event] → bridge_script_intercept.js detects drag
├─ Checks if content is convertible (file path or Azure URL)
├─ Calls window.bridge.start_file_drag(url)
└─ preventDefault() → Blocks normal browser drag
↓
JavaScript → QWebChannel Bridge
↓
_DragBridge.start_file_drag(path_text) [main_window.py]
├─ Defers execution via QTimer (drag manager safety)
└─ Calls DragInterceptor.handle_drag()
↓
DragInterceptor.handle_drag() [core/drag_interceptor.py]
├─ Check if Azure URL: Use URLConverter → local path
├─ Else: Treat as direct file path
└─ Validate with PathValidator
↓
PathValidator.validate(path)
├─ Resolve to absolute path
├─ Check file exists (if configured)
├─ Check is regular file (not directory)
└─ Check path within allowed_roots (whitelist)
↓
If valid:
→ Create QUrl.fromLocalFile(path)
→ Create QMimeData with file URL
→ QDrag.exec(Qt.CopyAction) → Native file drag
→ Emit drag_started signal
↓
If invalid:
→ Emit drag_failed signal with error
→ Log validation error
↓
OS receives native file drag
↓
Target application (InDesign/Word) receives file handle
Key Components in Data Flow:
- bridge_script_intercept.js: Opens a WebChannel to Qt's _DragBridge
- _DragBridge: Exposes
start_file_drag()slot to JavaScript - DragInterceptor: Handles validation and native drag creation
- URLConverter: Maps Azure Blob Storage URLs to local paths via config
- PathValidator: Security-critical validation against whitelist
Security Model
Path Validation Strategy
- Whitelist-based: Only allow configured root directories
- Absolute path resolution: Prevent directory traversal attacks
- Symlink handling: Resolve to real path before checking
- File existence check: Don't drag non-existent files
- Type verification: Only allow regular files
# Example allowed roots
ALLOWED_ROOTS = [
Path("Z:/"), # Network share
Path("C:/Users/Public"), # Windows public folder
]
# Example validation
path = Path(r"Z:\projects\file.psd")
→ Resolve: Z:\projects\file.psd
→ Check: starts with Z:\? YES
→ Check: file exists? YES
→ Check: is regular file? YES
→ Result: ALLOW
Web Engine Security
- LocalContentCanAccessFileUrls: Enabled (required for drag)
- LocalContentCanAccessRemoteUrls: Disabled (prevent phishing)
- Certificate validation: Enforced for HTTPS
Performance Considerations
Drag Operation Timing
Event Time (ms) Critical Path
─────────────────────────────────────
dragenter <1 ✓ Must be fast
validation <10 ✓ Keep fast
QUrl creation <1
QDrag.exec() <50 ✓ Start quickly
─────────────────────────
TOTAL <50 ✓ Imperceptible
Memory Management
- Web engine: ~100-150 MB baseline
- Drag interceptor: Minimal overhead
- Logging: Rotated to prevent growth
- File cache: None (on-demand validation)
Testing Strategy
Unit Testing
validator.py
├─ test_is_allowed()
├─ test_is_valid_file()
└─ test_symlink_handling()
config.py
├─ test_load_from_env()
├─ test_default_values()
└─ test_validation()
drag_interceptor.py
├─ test_dragEnterEvent()
├─ test_invalid_path_rejected()
└─ test_file_drag_created()
Integration Testing
end_to_end.py
├─ test_app_startup()
├─ test_webapp_loads()
├─ test_complete_drag_workflow()
└─ test_invalid_path_handling()
Extension Points
Adding New Handlers
# Future: Support for additional file types or sources
class HandlerRegistry:
def register(self, source_type: str, handler: Handler):
self.handlers[source_type] = handler
def handle(self, data: QMimeData) -> Optional[QMimeData]:
for handler in self.handlers.values():
if handler.can_handle(data):
return handler.handle(data)
return None
Custom Validators
# Future: Pluggable validation rules
class ValidatorPlugin(ABC):
@abstractmethod
def validate(self, path: Path) -> bool:
pass
class FileSizeValidator(ValidatorPlugin):
def __init__(self, max_size: int):
self.max_size = max_size
def validate(self, path: Path) -> bool:
return path.stat().st_size <= self.max_size
Deployment Architecture
Single-File Executable
WebDropBridge.exe (built with PyInstaller)
├─ Python runtime
├─ PySide6 libraries
├─ Qt libraries
├─ Embedded web app (index.html)
└─ Bundled resources (icons, etc.)
Size: ~100-120 MB
Startup: <1 second
Portable Deployment
- Single executable: No installation required
- No registry entries: Clean removal
- Relative paths: Can run from any directory
- Configuration:
.envfile per instance
Platform Differences
Windows
- Drag Format: CF_HDROP (file paths)
- File URLs:
file:///C:/path/to/file - Paths: Backslash
\(converted to forward/) - Permissions: Admin privileges not required
macOS
- Drag Format: NSFilenamesPboardType (same as Windows, different implementation)
- File URLs:
file:///Users/username/path/to/file - Paths: Forward slash
/(native) - Permissions: May require accessibility permissions
Update Manager
The UpdateManager class checks for new releases using the Forgejo API. It caches results and only signals updates for newer versions. See src/webdrop_bridge/core/updater.py for implementation.
Release Flow
- Checks for new releases on startup or user request
- Parses release notes and assets
- Notifies UI if update is available
Integration Test Strategy
Integration tests verify workflows across modules. The update workflow is covered in tests/integration/test_update_flow.py.
For more details, see DEVELOPMENT_PLAN.md