Security
- Enforce guest ownership and server assignment across Proxmox endpoints
(VM/LXC actions, firewall, backups, tasks, bulk operations, deploy,
LXC/ISO creation, golden template) and WebSocket channels
- Keep the panel's PVE root ticket server-side for consoles; single-use
WebSocket tickets instead of the JWT in URLs; re-check access on open
WebSocket bridges
- Validate identifiers that reach Proxmox API paths and node shell
commands; reject backup archives / foreign volumes in create requests
- Close privilege escalation in user management, TOTP replay, reset-link
poisoning, X-Forwarded-For spoofing, stored XSS (group icons, SVG logos)
- SSH host key pinning (TOFU), encrypted 2FA seeds and notification
secrets, password changes end other sessions, redact secrets in logs
- Quotas count in-flight creations; 2FA failures count toward lockout
Fixes
- Consoles on non-default API ports, large App Store/script files,
interrupted tasks after restart, impersonation lost on token refresh,
HTTPExceptions swallowed into 500s, WebSocket client reconnects
Performance and process
- Fewer DB writes and queries on hot paths, lazy-loaded locales and
heavy pages, off-loop blocking work, indexes (migrations 44-46)
- Deploy waits for backend tests and frontend build; frontend checks
workflow; API bound to loopback without the SSL profile
The mock_proxmox fixture patched ProxmoxClient with return_value= (constructor
style), but since commit d66928e clients are built via the
ProxmoxClient.from_server classmethod. from_server() therefore returned an
unconfigured child mock, so endpoints serialized a MagicMock and responded 500
— failing 5 tests in test_api_vms.py on every CI run (Backend Tests workflow).
Wire .from_server.return_value (and .return_value) on all patched ProxmoxClient
references to the configured instance so every construction path resolves to
the same mock. Full suite now green (60 passed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Roles are stored in the new resource:action format and any legacy keys are
converted once at startup by the RBAC v2 DB migration (migrate_rbac_v2), so
the permission engine no longer needs to resolve legacy codes on every check.
- get_user_permissions: store role permission codes as-is (no dual add).
- has_permission: match new-format codes only; drop resolve_permission and the
dotted-code fallbacks.
- Remove now-unused resolve_permission import in middleware.
- LEGACY_PERMISSION_MAP / convert_* are retained as one-shot DB-migration
utilities (upgrade path), not a runtime bridge.
- Tests: adapt fixtures to new-format role permissions; assert legacy keys are
no longer honored at request time.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- AttributeError: OSTemplate has no 'template_vmid' field; the actual column is 'vmid'.
- Use OSTemplate.get_vmid_for_node() to support cross-node replicated templates,
falling back to source_node/vmid otherwise.
- VMInstance.memory is stored in bytes, but Proxmox config expects MB.