# 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__*`), `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? | |---|---|---| | **S**poofing (Plugin-Identität) | Ed25519-Signing + Publisher-Key-Registry + revoked-Key-Check | OK | | **T**ampering (Plugin-Binary/Manifest) | Signature-Verify über Binary + canonical Manifest-Subset; Tamper-Tests | OK | | **R**epudiation (Tenant bestreitet Capability-Consent) | Consent-Action im existierenden Audit-Log mit Actor+Timestamp (hooks-lifecycle Security-Note Zeile 404) | OK | | **I**nformation Disclosure (Cross-Tenant-Leak) | Tenant-Scoping in DB-Bindings, Storage-Prefix, separate Wasmtime-Stores, Output-Sanitization | OK | | **D**enial of Service | Resource-Limits, Rate-Limits per Tenant, Auto-Disable, Global-Auto-Disable, Pool-Sizing | OK | | **E**levation 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 ` 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 ` (subcommand-style). Beide sind funktional äquivalent. **Empfehlung:** `tc plugin ` 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 ` 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.**