319 lines
29 KiB
Markdown
319 lines
29 KiB
Markdown
# 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.**
|