Endpoint-Scope-Audit (#186, Teil von Epic Public-Readiness II)
Systematischer Durchgang aller 24 Router (~270 Endpoints): jeder daten-/ aktionsführende Endpoint braucht eine Auth-Dep und einen Node/VM/Tenant- Scope-Check (Muster aus #108). „Authenticated but unscoped" = Finding.
Methode: 6 parallele Read-only-Audit-Agents, je Router eine Endpoint-Tabelle (auth_dep · scope_check · data_scope · Verdict). Verdicts: OK / MISSING-AUTH / MISSING-SCOPE / IDOR-RISK / INTENTIONAL-PUBLIC.
Ergebnis
16 Findings gefunden und behoben. Saubere Router (keine Findings): deploy_router, net_router, monitor_router, rdp_router, notification_router, auto_update_router, auth_router, faq_router, temperature_router, docgen_router, command_router, tunnel_router, me_router, task_router, log_router.
Findings + Fixes
| # | Sev | Endpoint | Problem | Fix |
|---|---|---|---|---|
| 1 | HIGH | schedule_router GET /jobs |
listete Jobs aller Tenants | list_user_accessible_nodes-Filter (Scope-WHERE) |
| 2 | HIGH | schedule_router POST /jobs |
Job auf fremder Node anlegbar | check_node_scope/check_vm_scope auf body |
| 3 | HIGH | schedule_router GET /jobs/{id} |
IDOR — jeder liest jeden Job | _scope_job nach Load |
| 4 | HIGH | schedule_router PATCH /jobs/{id} |
IDOR + Move auf fremde Node | Scope auf alte und neue Node/VM |
| 5 | HIGH | schedule_router DELETE /jobs/{id} |
IDOR — fremden Job löschen | _scope_job nach Load |
| 6 | HIGH | schedule_router POST /jobs/{id}/run |
IDOR — fremden Job ausführen | _scope_job nach Load |
| 7 | HIGH | update_router POST /bulk-job |
Operator triggert Updates auf fremden Tenant-VMs | check_vm_scope je entry vor launch_bulk_job |
| 8 | MED | update_router GET /bulk-job/{id} |
IDOR — fremde Job-Logs lesen | _assert_job_in_scope (alle Job-Nodes im Scope) |
| 9 | MED | update_router POST /bulk-job/{id}/cancel-item |
IDOR — fremdes Item abbrechen | _assert_job_in_scope |
| 10 | MED | docker_router GET /compose-update/status |
IDOR via geratenem cmd_id (cross-VM Live-Log) |
Run an node/vmid binden + prüfen |
| 11 | MED | security_router GET /scan-history |
Port-Snapshots aller Nodes cross-tenant | Scope-WHERE / check_node_scope |
| 12 | MED | admin_router GET /cluster/overview |
cross-tenant Node/VM-Counts für jeden User | _scoped_nodes_for_bulk |
| 13 | MED | audit_router GET /stats |
cross-tenant Command-Aggregate für jeden User | Scope auf accessible nodes, Events nur Superadmin |
| 14 | MED | template_router POST/PUT/DELETE |
jeder authed User (inkl. viewer) schreibt globale Templates | require_role("operator") + UUID-Guard |
| 15 | LOW | security_router GET /scan-state |
Scan-Fortschritt aller Nodes | Scope-Filter / check_node_scope |
| 16 | LOW | backup_router POST /sync-history |
viewer triggert Agent-Arbeit + DB-Writes | require_role("operator") |
Nicht-Findings (bewusst so)
require_superadmin-Endpoints (Provisioning) laden per ID ohne Ownership-Check — Superadmin überspringt Scope per Definition, kein IDOR.command_routerScripts +template_routerTemplates sind globale Bibliotheken (keine Tenant-Spalte) — Schreibrechte jetzt operator-gated, kein Tenant-IDOR.auth_routerlogin/refresh/logout,upgrade_router /pve/latest-major,rdp_router /_internal_resolve(GUAC_SECRET, fail-closed) — INTENTIONAL-PUBLIC.audit_router GET /events(AuditLog, globale Auth-Events) — admin-gated, nicht node-scoped; bewusst belassen.
Aktivierung
Fixes sind reine Backend-Auth-Verschärfungen. Wirksam erst nach Backend-Rebuild — dieser ist mit dem #185-Deploy gekoppelt (Migration 0024 + Agent-Re-Enrollment), also gemeinsam ausrollen.