Files
HiveGrid-Wireframes/hivegrid-cloud/docs/improvement-plan/06-plugin-system/VERIFICATION.md
T

29 KiB
Raw Blame History

Phase 06 — Verifikation (Plugin-System)

Verifier: general-purpose-Subagent Datum: 2026-05-16 Scope: README.md, wasm-sandbox.md, hooks-lifecycle.md, audit-docs.md Modus: READ-ONLY

2026-05-27 code status correction: This file primarily verifies the original plan quality, not the whole current shipped implementation. A focused code re-check against PluginSettingsFrame, PluginSettingsService, the tenant plugin settings route, and related tests shows that the plugin settings iframe/CSP/postMessage path is now implemented for non-secret per-user plugin settings. The frame runs as a srcDoc iframe with sandbox="allow-scripts" only, embeds a CSP with default-src 'none', connect-src 'none', form-action 'none', inline script/style allowances, data-only image/font sources, and no allow-same-origin or allow-forms. Message handling uses a local versioned postMessage parser (plugin.settings.*, protocol version 1) with request/key/value bounds and secret-like key rejection; it is not Zod. Persistence is routed server-side through the tenant settings route with app.plugins.use permission checks, CSRF validation, JSON/body-size checks, rate limiting, and per-user PluginUserConfig writes. Server bundle loading resolves realpaths under the plugin bundle root and enforces a settings HTML size limit before returning trusted HTML.

TL;DR

Status: plan-quality PASS; current runtime remains staging/foundation-only until external security signoff, production rollout proof, and the deferred runtime hardening items are complete.

Phase 06 ist die ambitionierteste und sicherheitskritischste Phase im gesamten Plan, und die drei Detail-MDs reflektieren das angemessen. Template-Konformität ist 100% (alle 15 Sections, korrekte Reihenfolge in allen drei Files). Dependencies sind sauber konsistent zwischen den Files und nach außen (Phase 03 Outbox/Worker-Queues/SharedStore, Phase 05 Feature-Flags). Implementation-Steps sind durchgehend atomar und commitbar. Sandbox-Escape-Tests sind systematisch enumeriert (7 Vektoren in wasm-sandbox.md), Capability-Enforcement-Tests vollständig, Resource-Limit-Tests (CPU/Memory/Wall-Clock) als kritisch markiert. Externe Security-Review ist als hard-gate vor Production-Rollout in audit-docs.md etabliert. Konsistenz mit User-Wünschen ist gegeben: vollwertiges Runtime-Plugin-System, WASM-basiert (Wasmtime mit Research-Spike), kein Marketplace-Code in Phase 06, Self-Hosted-Plugin-Install via ZIP-Upload.

Kleinere Anmerkungen (keine Blocker): leichte Inkonsistenzen bei Env-Variable-Naming zwischen README Done-Definition und wasm-sandbox.md (CPU-Limit-Variable), und einige nicht-funktionale Aspekte (Plugin-Update-Notification an Tenants, Plugin-Sandbox-Telemetry-Tagging in Sentry) könnten stärker hervorgehoben sein. Beides ist mit kleinen Edits adressierbar oder akzeptabel als Implementation-Detail.


1. Template-Konformität

Alle drei Detail-MDs folgen DETAIL-MD-TEMPLATE.md strikt:

File Frontmatter Problem Depends on Blocks Approach Implementation Steps Test Requirements Acceptance Criteria Rollback Security Subagent Rec. Staging Deviations Progress Log Lessons Learned
wasm-sandbox.md OK OK OK OK OK OK (17 Steps) OK OK OK OK (sehr ausführlich) OK OK OK OK OK
hooks-lifecycle.md OK OK OK OK OK OK (17 Steps) OK OK OK OK OK OK OK OK OK
audit-docs.md OK OK OK OK OK OK (18 Steps) OK OK OK OK OK OK OK OK OK

Reihenfolge der Sections ist in allen Files identisch und template-konform. Frontmatter-Felder (Status, Task-ID, Phase, Estimated Effort, Owner) sind durchgehend gesetzt; Owner ist überall unassigned, was korrekt für pending Tasks ist.

Befund: PASS — keine Template-Abweichungen.


2. Dependencies-Konsistenz

Intern (Phase-06-Sequenz)

  • wasm-sandbox.md deklariert keine Depends-on innerhalb Phase 06 (korrekt, da Foundation). Blocks: hooks-lifecycle.md, audit-docs.md, 11-backlog/marketplace.md. Konsistent.
  • hooks-lifecycle.md Depends-on: wasm-sandbox.md. Blocks: audit-docs.md, 11-backlog/marketplace.md. Konsistent.
  • audit-docs.md Depends-on: wasm-sandbox.md UND hooks-lifecycle.md. Blocks: Production-Rollout der gesamten Phase 06, Marketplace. Konsistent.

Die strikt sequenzielle Reihenfolge (kein Parallelisierungs-Versuch) ist in README.md explizit begründet (kein halber Security-Review-Pass). Plausibel.

Extern (Phase-Cross-Refs)

Cross-Phase-Deps werden in allen drei Files konsistent referenziert:

Dep wasm-sandbox.md hooks-lifecycle.md audit-docs.md 00-execution-order
03/shared-store-package.md OK (transitiv) (transitiv) OK
03/worker-queues.md OK (transitiv) (transitiv) OK
03/outbox-pattern.md OK OK (explizit) (transitiv) OK
05/feature-flags.md OK OK (explizit) (transitiv) OK
02/sentry-wiring-api-worker.md OK (transitiv) (transitiv) (impliziert)

Konsistent mit 00-execution-order.md: dort steht „Phase 06 Depends on: Phase 03, 05". Reihenfolge wasm-sandbox → hooks-lifecycle → audit-docs ist in 00-execution-order.md ebenfalls so dokumentiert (Tabellen-Eintrag „Reihenfolge 7").

Blocks → Marketplace (Backlog #24)

  • README.md Zeile 21: „Phase 11 / marketplace.md (Backlog): … Ohne Sandbox + Lifecycle + Audit kein veröffentlichbares Plugin und damit kein Marketplace."
  • Alle drei Detail-MDs deklarieren 11-backlog/marketplace.md als geblockt.
  • Konsistent.

Befund: PASS.


3. Implementation-Steps-Qualität

Die User-spezifizierten 11 Detail-Aspekte werden alle adressiert:

Aspekt Adressiert File:Section Qualitäts-Bewertung
WASM-Engine-Wahl (Wasmtime vs Wasmer) — Research-Spike empfohlen? Ja wasm-sandbox.md Step 1 + Approach-Tabelle Zeile 48 Stark. Explizit als Research-Spike (Step 1) mit zwei parallelen Explore-Subagents (Approach-Vergleich + Security-CVE-Review). Default-Choice Wasmtime mit Begründung (Bytecode Alliance, Fastly/CF/MS-Adoption, dokumentierte Resource-Limit-API). Research-Output-File RESEARCH-runtime-choice.md ist temporär — sauber.
Plugin-SDK mit Host-Bindings (HTTP/DB/Logging/Audit/Storage) — vollständig konzipiert? Ja wasm-sandbox.md Approach §SDK-Package + Step 12 Stark. 8 Host-Bindings vollständig spezifiziert (http, db.read, db.write, storage.read/write, log, audit, secret, tenant) mit jeweiliger Capability + Tenant-Scoping-Strategie. AssemblyScript-First (TypeScript-Devs), Rust-Crate optional/Skip-bar — pragmatisch.
Ed25519-Manifest-Signing + Publisher-Key-Registry — kryptografisch korrekt? Ja wasm-sandbox.md Approach §Ed25519 + Step 5+6 Korrekt. Ed25519 mit @noble/ed25519 (audited, no native deps — gute Wahl). Canonical-JSON-Subset für Signing (sorted keys, no whitespace) explizit gefordert — verhindert Signing-Round-Trip-Issues. Tamper-Tests in Test-Requirements (Binary + Manifest). Publisher-Key-Registry initial JSON-File mit revokedAt-Feld — minimal aber funktional. Anmerkung: Key-Rotation-Process wird in Security-Notes erwähnt, aber kein eigener Implementation-Step. Könnte als Operator-CLI-Subcommand (tc plugin keys rotate) ergänzt werden (Step 13 in audit-docs.md hat add/revoke/list, aber kein rotate). Nicht kritisch — Operator kann revoke+add machen.
Capability-System mit Tenant-Consent — UX-Flow durchdacht? Ja wasm-sandbox.md Approach §Capability + hooks-lifecycle.md Lifecycle-State-Machine + UI-Tabelle Stark. Klare Trennung: Capability-Declare im Manifest (Step 4 wasm-sandbox) → Validator → Tenant-Consent-Dialog (hooks-lifecycle Step 6-8) → Runtime-Enforcement im Main-Thread (nicht Worker-Thread, korrekt). Scope-System mit allowedHosts, keyPattern etc. ist granular genug. Capability-Erweiterung zur Runtime explizit ausgeschlossen — gut.
Resource-Limits (CPU/Memory/Wall-Clock) — wie enforced? Ja wasm-sandbox.md Approach-Tabelle Zeilen 156-165 + Step 7 Stark. Defense-in-Depth: Fuel (CPU) + Memory.max + Wall-Clock-Timer + Body-Size + DB-Row-Limit + Rate-Limit-pro-Tenant. Mechanismus pro Limit konkret benannt (Wasmtime-API). Tests fordern explizit Fuel-Trip, Memory-Trip, Wall-Clock-Trip. Anmerkung: Defaults erscheinen vernünftig (100 Mio Fuel ~= 100ms, 64 MiB Mem, 5s Wall-Clock), sollten aber im Tuning-Doku iterierbar dokumentiert sein — was docs/operations/plugin-runtime.md (Step 16) explizit fordert.
Worker-Thread-Isolation der WASM-Instances — realistisch? Teilweise umgesetzt wasm-sandbox.md Approach §Worker-Thread-Isolation + aktueller Code-Abgleich 2026-05-27 Aktueller Stand: Plugin-Aufrufe laufen in einem frischen, killable Worker Thread pro Invocation und werden danach terminiert. Ein wiederverwendbarer Pool mit PLUGIN_WORKER_POOL_SIZE existiert noch nicht; Pooling bleibt Upgrade-Pfad.
15-20 Host-Hook-Points im Code identifiziert? Ja hooks-lifecycle.md Problem-Section Zeilen 22-32 + Approach-Tabelle Zeilen 66-87 Stark. 18 Hook-Points enumeriert (innerhalb des 15-20-Zielbereichs), für jeden Input-Felder definiert, Output-Verwendung dokumentiert. Konkrete file-Pfade im Problem-Block genannt (apps/web/app/routes/onboarding.tsx, users.db.server.ts, WorkflowEngineService.ts etc.) — das sind real existierende Pfade (verifiziert anhand git status der laufenden Boilerplate). Step 13 explizit „pro Hook ein Commit" = 18 atomare Commits — sauber.
Plugin-Lifecycle (Install/Enable/Disable/Update/Uninstall) — vollständig? Ja hooks-lifecycle.md Approach §State-Machine + Step 6 Vollständig. State-Machine (pending → awaiting-consent → installed-disabled → enabled ↔ disabled → uninstalling → uninstalled, plus auto-disabled-Pfad) mit ASCII-Diagramm. Service-Methoden install/grantConsent/enable/disable/update/uninstall/autoDisable. Update läuft side-by-side mit superseded-Markierung der alten Version — wichtig für Rollback.
Plugin-Migrations-Mechanismus für eigene DB-Tabellen — sauber? Ja hooks-lifecycle.md Approach §DB-Schema-Migrations + Step 5 Sauber. Tabellen-Prefix-Pflicht (plugin_<sanitized-id>_*), tenantId NOT NULL Pflicht-Spalte (validated via Schema-Inspection), Down-Migrations Pflicht, Apply via prismaWrite (Host-Operation, nicht Sandbox), SQL-Allowlist (CREATE TABLE/INDEX, ALTER TABLE ADD COLUMN, DROP TABLE/INDEX/COLUMN — keine DROP DATABASE, GRANT, REVOKE, COPY) in Security-Notes Zeile 405. Stark gegen SQL-Injection. Alternative (separate Postgres-Schemas) wurde explizit gegen abgewogen mit Begründung — gute Architektur-Hygiene.
iframe-Sandboxed UI für Plugin-Settings — secure? Ja, aktueller Code implementiert den Kernpfad hooks-lifecycle.md Step 9 + aktueller Code-Abgleich 2026-05-27 Implementiert für non-secret per-user settings. PluginSettingsFrame rendert ein srcDoc-iframe mit sandbox="allow-scripts" only (kein allow-same-origin, kein allow-forms, kein allow-top-navigation) und eingebetteter CSP: default-src 'none', connect-src 'none', form-action 'none', script-src 'unsafe-inline', style-src 'unsafe-inline', data-only image/font, base-uri 'none'. Die Message-Validation erfolgt über den lokalen versionierten Parser parsePluginSettingsFrameMessage für plugin.settings.ready/get/set (Protocol v1), inklusive Bounds für requestId/key/value, JSON-like Value-Shape und Rejects für reserved/secret-like keys. Persistenz läuft nicht im iframe, sondern serverseitig über app.$tenant/settings/plugins.$id: app.plugins.use Permission, CSRF, JSON/body-size-Checks, rate limiting und setPluginUserSetting auf per-user PluginUserConfig. PluginSettingsService prüft ui.settings.entry als safe relative HTML path, löst Bundle-Root und Entry per realpath auf, verhindert Bundle-Escape und erzwingt ein HTML-Size-Limit. Tests decken Frame-CSP/Sandbox/PostMessage, Parser-Rejections, Route-Permission/CSRF/Rate-Limit/Persistenz und den E2E-iframe-Kontrakt ab.
Crash-Handling + Auto-Disable — implementiert? Teilweise umgesetzt hooks-lifecycle.md Approach §Crash-Handling + aktueller Code-Abgleich 2026-05-27 Aktueller Stand: Tenant-scope Crash-/Failure-Tracking und Auto-disable sind vorhanden. Globaler Multi-Tenant-Crash-Counter plus Operator-Alert bleiben Follow-up vor Production-Signoff.

Befund: PASS — Implementation-Steps sind durchweg hochqualitativ. Komplexeste Phase, aber Detail-Tiefe entspricht der Komplexität.


4. Test-Requirements

Sandbox-Escape-Tests (Anforderung: mindestens 7 Vektoren)

wasm-sandbox.md Zeilen 317-324 listet 7 Escape-Tests:

  1. WASM mit wasi:filesystem-Import → Instantiation-Fail
  2. WASM mit raw memory.grow über Limit → Trap
  3. WASM versucht Host-Function-Pointer zu konstruieren → Component-Model verhindert
  4. Plugin-Binary mit modifizierten Imports nach Signing → Verify-Fail
  5. Plugin sendet file:// oder localhost-URL → Reject (Allowlist)
  6. Plugin versucht Wildcard-Key (*) via secret.get → Reject
  7. Plugin versucht Memory-Region eines anderen gleichzeitig laufenden Plugins zu lesen → unmöglich (separate Wasmtime-Stores)

Anforderung erfüllt (exakt 7). Zusätzlich sind die Tests als „kritisch" markiert und explizit als Blocker für Task-Completion gesetzt (Zeile 332: „Bei auch nur einem fehlgeschlagenen Escape-Test: Task blockiert, BLOCKERS.md füllen.").

Resource-Limit-Tests (CPU/Memory/Disk-Bombs)

wasm-sandbox.md Zeilen 307-310:

  • Endlosschleife → Fuel-Trip in <200ms
  • Memory-Bomb (256 MiB bei 64 MiB Limit) → Trap + Pool-Replace
  • Wall-Clock-Bomb (synchroner Sleep > 5s) → Worker-Terminate + Retry (max 3×) → Fail

Anmerkung: „Disk-Bombs" sind nicht explizit getestet — aber Plugin-Sandbox hat per Default keinen Filesystem-Zugriff (kein wasi:filesystem-Import gewährt, Test #1). Damit ist Disk-Bomb-Vektor strukturell ausgeschlossen. ZIP-Bomb (Plugin-Upload-Pfad) ist in hooks-lifecycle.md Security-Notes Zeile 398 separat gemitigated (max 50 MiB komprimiert, max 200 MiB unkomprimiert). Implizit erfüllt.

Capability-Enforcement-Tests

wasm-sandbox.md Zeilen 311-316: 5 Tests (ohne Capability-Grant, Host-Allowlist-Bypass, db.write ohne Grant, db.write ohne Prefix, Tenant-Bypass via SQL-Injection). Plus hooks-lifecycle.md Test-Requirements mit Per-User-Enablement-Test, Auto-Disable-Test, Migration-Tests (positiv + negativ). Vollständig.

Coverage-Targets

  • wasm-sandbox.md: ≥90% für plugin-runtime (security-kritisch), ≥85% für plugin-manifest, ≥80% für plugin-sdk
  • hooks-lifecycle.md: ≥85% für plugin-hooks und plugin-modules, ≥90% für migrations.ts und crash-tracker.ts
  • audit-docs.md: ≥85% für plugin-cli, ≥90% für audit-recorder.ts und sanitize.ts

Differentiated Coverage (höhere Targets für security-kritischen Code) — gute Praxis.

Befund: PASS.


5. Security-Notes

wasm-sandbox.md Security-Notes ist ausführlich (~25 Zeilen, 9 thematische Blöcke):

  1. Sandbox-Boundary — explizit erwähnt was Defense-in-Depth-Lagen sind (typed Component-Model-API, Fuel + Epoch-Interrupt + Wall-Clock = 3 Lagen)
  2. Capability-System — Principle of Least Privilege explizit benannt, Default-Deny
  3. Tenant-Isolation — Tenant-ID vom Host injiziert (nie vom Plugin), DB-SQL-Rewriting für WHERE tenantId, Storage-Prefix, Audit-Tenant-Scoping
  4. Signing — Ed25519-Wahl begründet (vs RSA/ECDSA), @noble/ed25519 audited
  5. Resource-Limits — Worst-Case-Berechnung (POOL_SIZE × MEMORY_MB)
  6. Worker-Process-Isolation — seccomp + AppArmor empfohlen in Operations-Doku
  7. Plugin-Binary-Storage — read-only für worker-plugin-User außer Install
  8. Sensitive Data in Plugin-Output — Sanitization-Filter (wird in audit-docs.md detailliert)
  9. Sandbox-Escape-Detection — Sentry-Alert bei Trap, On-Call-Workflow
  10. No Eval, No Dynamic Code — WASM ist statisch, Plugin kann kein zweites Modul laden
  11. Security-Review als Blocker — verweist auf audit-docs.md (#23)

STRIDE-Mapping (von Verifier nachgeprüft)

STRIDE-Threat Mitigation in Phase 06 OK?
Spoofing (Plugin-Identität) Ed25519-Signing + Publisher-Key-Registry + revoked-Key-Check OK
Tampering (Plugin-Binary/Manifest) Signature-Verify über Binary + canonical Manifest-Subset; Tamper-Tests OK
Repudiation (Tenant bestreitet Capability-Consent) Consent-Action im existierenden Audit-Log mit Actor+Timestamp (hooks-lifecycle Security-Note Zeile 404) OK
Information Disclosure (Cross-Tenant-Leak) Tenant-Scoping in DB-Bindings, Storage-Prefix, separate Wasmtime-Stores, Output-Sanitization OK
Denial of Service Resource-Limits, Rate-Limits per Tenant, Auto-Disable, Global-Auto-Disable, Pool-Sizing OK
Elevation of Privilege Capability-System Default-Deny, kein Runtime-Capability-Request, Main-Thread macht Checks (nicht untrusted Worker) OK

STRIDE-Coverage vollständig.

Sandbox-Escape-Vektoren systematisch enumeriert?

Ja — 7 Escape-Tests (s.o.) plus explizite Diskussion in Security-Notes (Zeile 373) von 3 Escape-Pfaden (Host-Import-Bugs, Resource-Limit-Bypass, Component-Model-Bugs). Externe Security-Review (Task #23) ist als Pflicht-Gate vor Production-Rollout etabliert — anerkennt explizit dass interne Tests blinde Flecken haben (Zeile 379 audit-docs.md).

Befund: PASS — Security-Notes sind ausführlich, defense-in-depth-orientiert, STRIDE-konform.


6. Staging-Deployment-Steps

wasm-sandbox.md Staging (Zeilen 408-481)

13 nummerierte Steps: Branch pushen → Coolify-Deploy → docker-compose-Snippet für worker-plugin (mit Resource-Limits cpus/memory) → Test-Publisher-Keypair generieren → Public-Key in Staging-Registry → Hello-Plugin signieren + droppen → Smoke-Test via Test-Endpoint /api/internal/plugin-test/invoke (gated) → Resource-Limit-Smoke (endless-loop-plugin) → Capability-Enforcement-Smoke (unauthorized-http-plugin) → Health-Check → Sentry-Dashboard-Check → Logs-Check → Production-Gate (Test-Endpoint muss aus prod-Build entfernt werden).

Stark. docker-compose-Snippet ist konkret (mit env-Vars, Volumes, deploy-Resource-Limits, replicas).

hooks-lifecycle.md Staging (Zeilen 434-476)

18 Steps: Branch + Coolify + Migration-Apply → Worker-Handler-Registration-Check → Tenant-Admin-Login → Plugin-Install via UI → Capability-Consent-Dialog → Enable → Hook-Trigger via Test-Endpoint → Audit-Verify → Plugin-Settings-Test mit postMessage → Per-User-Toggle-Test → Crash-Test mit crash-plugin → Uninstall-Test mit DB-Verify (\dt plugin_*) → Update-Path-Test mit Version-Side-by-Side → Health-Check → Production-Gate.

Stark. Plugin-Install-Flow ist End-to-End auf Staging durchgespielt.

audit-docs.md Staging (Zeilen 411-451)

13 Steps inkl. CLI-Lokal-Install, Roundtrip-Smoke (scaffold → build → sign → lint → pack), Public-Key in Registry, Plugin-Install via UI, Audit-Trail-UI-Check, Output-Sanitization-Test (Credit-Card-Pattern → [REDACTED:credit-card]), Retention-Test (manuell loggedAt auf 91 Tage zurück, Cron-Job triggern), 3 Beispiel-Plugins durchlaufen (Slack/DATEV/Custom-Auth), Externe Security-Review-Phase (Test-Tenant-Setup-Skript, Reviewer-Zugang, Task auf blocked: waiting-for-external-review während Review), Production-Rollout-Gate mit Sign-off + 24h-Monitoring + Kill-Switch.

Sehr stark. Plugin-Test-Setup auf Staging ist klar; Beispiel-Plugin-Install-Flow ist End-to-End nachvollziehbar.

Befund: PASS.


7. Konsistenz mit User-Wünschen

User-Wunsch Reflektion im Plan Bewertung
„C: Vollwertiges Runtime-Plugin-System" README.md Zeile 5: „Vollwertiges Runtime-Plugin-System auf WASM-Basis" OK
„WASM-basiert (sauberer langfristig)" Wasmtime durchgängig (Approach, Steps, docker-compose, env-vars). Wasmer nur als Research-Spike-Alternative in Step 1. OK
„Marketplace ins Backlog" README.md Zeile 21 explizit: „Marketplace … explizit nicht Teil dieser Phase … liegt im Backlog (Phase 11)". Alle drei Detail-MDs deklarieren marketplace.md als geblockt durch Phase 06. Keine Marketplace-Code-Steps in Phase-06-Implementation. OK
„Self-Hosted-Plugin-Install zuerst" hooks-lifecycle.md Approach §Tenant-Admin-UI Zeile 160: Install-Dialog mit File-Upload .zip. Kein Marketplace-Pull, kein Discovery-Layer. CLI hat tc plugin publish --target <staging-url|production-url> als optional-Phase-11-Erweiterung markiert. OK

Befund: PASS — User-Intent vollständig reflektiert.


8. Acceptance-Criteria

Externe Security-Review als hard-gate vor Production-Rollout?

audit-docs.md Acceptance Criteria:

  • Zeile 353: „Externer Security-Review durchgeführt und Report archiviert (PFLICHT)"
  • Zeile 354: „Alle Critical- und High-Findings adressiert + von Reviewer-Firma re-tested (PFLICHT)"
  • Zeile 355: „Medium-Findings adressiert oder als Backlog-Tasks dokumentiert"
  • Zeile 356: „Production-Rollout-Sign-off-Document … mit Operator-Signature"

Plus Step 16 explizit: „Erst nach diesem File-Commit ist Production-Rollout erlaubt."

Plus README.md Done-Definition Zeile 102: „Externe Security-Review-Report von einer unabhängigen Security-Firma vorliegend, alle Critical/High-Findings adressiert".

Befund: PASS — Hard-Gate ist auf mehreren Ebenen verankert (Detail-MD + README + Approach).


9. Fehlende Aspekte (User-spezifisch)

User-Frage Adressiert? Detail
Plugin-Compatibility-Matrix gegen Host-SemVer? Ja audit-docs.md Step 11+12 (Matrix initial befüllen, version-check.ts mit isPluginCompatibleWithHost, Boot-Check + Install-Reject bei Inkompatibilität, incompatible_host_version-Status).
Plugin-Update-Pipeline mit Notification an Tenants? Teilweise. Plugin-Update-Flow (PluginLifecycleService.update) ist in hooks-lifecycle.md Step 6 enthalten (side-by-side, alte Version superseded). Aber: explizite Tenant-Notification bei Plugin-Update („dieser Plugin hat eine neue Version v1.2.0 — möchten Sie aktualisieren?") ist nicht als eigener Step. Update wird über die UI initiiert (/app/$tenant/plugins/$pluginId Detail-View) — implizit, aber keine Push-Notification-Pipeline. Für MVP akzeptabel (Plugin-Author-zu-Tenant-Channel ohne Marketplace ist eh out-of-scope). Empfehlung: als Folge-Backlog-Item notieren, nicht jetzt blockieren.
Plugin-Sandbox-Telemetry zu Sentry? Ja wasm-sandbox.md Step 15: packages/plugin-runtime/src/observability.ts mit OTel-Span + Sentry-Tags plugin.id, plugin.version, plugin.hook, plugin.tenant, plugin.capability. Integration in invoker.ts und allen host-bindings/*.ts. Plus Security-Notes Zeile 389: „Sandbox-Escape-Detection: Bei jedem Trap oder unexpected Worker-Crash → automatische Alert an Sentry mit severity=critical".

Befund: 2.5 von 3 vollständig adressiert. Update-Notification ist akzeptabel-thin, sollte als Lessons-Learned-Item dokumentiert werden falls Phase-06-Execution beginnt.


10. Kleinere Anmerkungen / Inkonsistenzen

10.1 Env-Variable-Naming: PLUGIN_MAX_CPU_MS vs PLUGIN_MAX_FUEL

README.md Zeile 99 listet PLUGIN_MAX_CPU_MS in Done-Definition. wasm-sandbox.md Zeile 158 nutzt PLUGIN_MAX_FUEL (Wasmtime-spezifisch) und Zeile 355 listet ebenfalls PLUGIN_MAX_FUEL.

Inkonsistenz. Fuel ist Wasmtime-spezifische Einheit (~ ein Fuel-Unit pro Bytecode-Instruktion), CPU-Ms ist Wall-Clock-ähnlich. Beide sind unterschiedliche Konzepte. README sollte PLUGIN_MAX_FUEL listen (oder PLUGIN_MAX_CPU_MS als zusätzliche Wall-Clock-CPU-Begrenzung, was aber PLUGIN_MAX_WALL_CLOCK_MS schon abdeckt).

Empfehlung: README.md Zeile 99 von PLUGIN_MAX_CPU_MS auf PLUGIN_MAX_FUEL ändern (oder zusätzlich noch beibehalten falls als separate Begrenzung gedacht). Klein, aber sollte vor Execution-Start korrigiert werden.

10.2 README.md vs wasm-sandbox.md: env-Variable-Set komplett?

README.md Done-Definition listet 6 env-vars (Zeile 99). wasm-sandbox.md Acceptance Criteria listet 10 env-vars (Zeile 355). hooks-lifecycle.md Staging fügt 5 weitere hinzu (Zeile 472-476). audit-docs.md Staging fügt 3 weitere hinzu (Zeile 458-463). Total ~18 env-vars über die Phase.

Empfehlung: README.md Done-Definition könnte komplettere Liste aufzählen, oder explizit auf Detail-MDs verweisen. Kein Blocker.

10.3 Plugin-Update-Notification-Channel

Wie oben in §9 — kein dedizierter Push-Notification-Step. Akzeptabel für MVP.

10.4 CLI-Naming-Konsistenz

wasm-sandbox.md Approach Zeile 142 nutzt tc plugin keygen. audit-docs.md Zeile 105 nutzt tc plugin scaffold etc. mit tc-plugin als Binary-Name (Zeile 208). Doku-Strings (tc plugin ...) vs Binary-Name (tc-plugin) — leichte Diskrepanz, Convention unter Operators ist meistens tc plugin <cmd> (subcommand-style). Beide sind funktional äquivalent. Empfehlung: tc plugin <subcmd> als finale Schreibweise in allen Docs vereinheitlichen.

10.5 Zeitplan-Plausibilität

  • wasm-sandbox.md: 4-5 Wochen (17 Steps, davon Research-Spike + 7 Host-Bindings + Tests)
  • hooks-lifecycle.md: 4-5 Wochen (17 Steps + 18 Hook-Wirings = 35 atomic operations)
  • audit-docs.md: 2-3 Wochen Code + 2-4 Wochen externe Security-Review = ~4-7 Wochen Wall-Clock

Total 10-13 Wochen wie in README.md angegeben. Plausibel — Phase ist sehr fokussiert und tiefgreifend, Estimates haben passenden Slack.


11. Stärken (besonders hervorzuheben)

  1. Defense-in-Depth bei Resource-Limits. 3 unabhängige Mechanismen (Fuel = Wasmtime-API, Epoch-Interrupt = OS-Signal, Wall-Clock = JS-Timer auf Worker-Thread) statt sich auf einen zu verlassen.
  2. Capability-Check im Main-Thread, nicht im Worker-Thread. Untrusted Worker-Thread kann keine Capability-Checks faken — Trust-Boundary klar.
  3. Tenant-Isolation auf 3 Ebenen. Sandbox-Stores (Memory), DB-SQL-Rewriting (Daten), Storage-Prefix (KV). Plus Per-User-Enablement on top.
  4. External Security Review als nicht-skipbarer Gate. Anerkennt explizit dass interne Tests blinde Flecken haben. Sign-off-Document mit Operator-Signature — organisationaler Kontrollpunkt.
  5. Crash-DoS-Mitigation. Plugin-Auto-Disable zählt nur internal Failures, nicht Output-Errors — schützt vor bösartiger User-Bombardierung.
  6. SQL-Migration-Allowlist. Kein $executeRawUnsafe ohne Statement-Inspection. Verbotene Statements (DROP DATABASE, GRANT, COPY) explizit enumeriert.
  7. Plugin-Settings-iframe mit echtem Browser-Sandboxing. Aktueller Code nutzt sandbox="allow-scripts" ohne allow-same-origin oder allow-forms, eine deny-by-default iframe-CSP, opaque-origin/source-gebundene postMessage-Validation und serverseitige Permission/CSRF/Persistenz. DOMPurify ist in diesem Pfad nicht der relevante Schutz; die HTML-Datei wird als vertrauenswürdiges Plugin-Bundle-Asset nach realpath/size checks geladen.
  8. Hook-Wiring atomisch per Commit. 18 Hook-Points = 18 einzelne, einzeln revert-bare Commits. Falls ein Hook später Performance-Probleme macht, ist Surgical-Revert möglich.
  9. Output-Sanitization mit Strictness-Modes. Tenant-Admin kann selbst entscheiden — Operator overridable. Plus Forensics-Hash ohne Body-Storage = DSGVO-konform.
  10. 3 unterschiedliche Referenz-Plugins. Slack (http+secret), DATEV (db.read+storage.write+migration), Custom-Auth (output-hook+iframe-settings). Decken die wichtigsten Pattern.

12. Empfehlungen (Priorität: niedrig)

  1. README.md Done-Definition (Zeile 99): PLUGIN_MAX_CPU_MS → PLUGIN_MAX_FUEL korrigieren. Trivial.
  2. CLI-Naming: tc plugin <cmd> als Standard in allen Docs (Binary kann tc-plugin heißen, aber Beispiele uniform). Trivial.
  3. Plugin-Update-Notification: als Lessons-Learned-Anchor in hooks-lifecycle.md notieren — Folge-Phase könnte einen Operator-Push-Channel ergänzen. Optional.
  4. Settings-iframe Hardening nach Bedarf weiterverfolgen: Der aktuelle Code blockiert Netzwerk/Formularzugriffe (connect-src/form-action 'none') und erlaubt inline script/style im sandboxed srcDoc-Dokument. Nonce- oder hash-basierte Skript-Policy wäre ein optionaler Folge-Hardening-Schritt, muss aber gegen das Plugin-Settings-HTML-Modell abgewogen werden. Optional.
  5. Key-Rotation-Subcommand (tc plugin keys rotate) als CLI-Erweiterung — derzeit nur add+revoke (was funktional reicht). Optional.

13. Fazit

Phase 06 ist execution-ready. Die drei Detail-MDs sind ungewöhnlich detailliert und reflektieren die Security-Stakes angemessen. Template-Konformität, Dependency-Konsistenz, Test-Coverage und Security-Review-Gate sind alle erfüllt. Die wenigen Anmerkungen sind kosmetisch und blockieren keine Implementation.

Empfehlung: Phase kann mit Task #21 (wasm-sandbox.md) gestartet werden, sobald Phase 03 (Outbox, Worker-Queues, SharedStore) und Phase 05 (Feature-Flags) completed sind. Die kleinen Konsistenz-Korrekturen aus §10.1 und §10.4 sollten vor Start gemacht werden (5 Minuten Aufwand).

Gesamt-Status: PASS.