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

319 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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? |
|---|---|---|
| **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 <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.**