Sicherheitsrunde dokumentiert: vier offene Entscheidungen, Kleinkram, bewusste Nicht-Befunde
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,79 @@
|
|||||||
Shell schreiben. Das Markdown-Rendering ist jetzt entschärft
|
Shell schreiben. Das Markdown-Rendering ist jetzt entschärft
|
||||||
(`src/markdown.ts`), aber eine echte CSP wäre die Schranke dahinter.
|
(`src/markdown.ts`), aber eine echte CSP wäre die Schranke dahinter.
|
||||||
Braucht eine Runde Prüfen, was inline benötigt wird (Themes, xterm).
|
Braucht eine Runde Prüfen, was inline benötigt wird (Themes, xterm).
|
||||||
|
Schon ein `default-src 'self'` würde auswärtige Requests und die
|
||||||
|
Fremdnavigation unabhängig vom Sanitizer abschneiden.
|
||||||
|
|
||||||
|
## Aus der Sicherheitsrunde 2026-07-19 (vier Angriffsflächen geprüft)
|
||||||
|
|
||||||
|
Behoben und gepusht (6d44f77): Befehlsinjektion im SessionStart-Hook über den
|
||||||
|
Projektpfad, ungeprüfter Pool-Name als `CLAUDE_CONFIG_DIR`, `check_name` gegen
|
||||||
|
Steuerzeichen (`.desktop`-Injektion), Bild-Beacon im Panel. Dazu `cargo audit`
|
||||||
|
nachgeholt: `plist` auf 1.10.0, `quick-xml` auf 0.41.0 im Laufzeitpfad.
|
||||||
|
|
||||||
|
Offen sind die vier Punkte, die eine Designentscheidung brauchen:
|
||||||
|
|
||||||
|
- [ ] **`term_start` prüft das Fenster-Label nicht (`terminal.rs:96-211`).**
|
||||||
|
Jedes Fenster des Terminal-Prozesses kann sich selbst in die Terminals-Map
|
||||||
|
eintragen (Schlüssel ist `window.label()`, Zeile 207-210) und danach per
|
||||||
|
`term_write` in eine frisch gestartete Shell schreiben. Damit ist auch das
|
||||||
|
abgelöste Panel-Fenster gefährlich, nicht nur das angedockte — und jedes
|
||||||
|
Skript im Webview wird zu Codeausführung, auch ohne XSS.
|
||||||
|
Zu entscheiden: Soll das Panel-Fenster je ein eigenes Terminal starten
|
||||||
|
dürfen? Wenn nein, ist die Prüfung auf `term-*` ein enger Fix.
|
||||||
|
|
||||||
|
Strukturell dahinter: Die Fenster-ACL scheint für App-Commands nicht zu
|
||||||
|
greifen (`gen/schemas/acl-manifests.json` enthält kein App-Manifest) —
|
||||||
|
**vor dem Bauen darauf verifizieren**, die beiden Prüfagenten waren sich
|
||||||
|
in diesem Punkt uneins.
|
||||||
|
|
||||||
|
- [ ] **`write_panel(path)` liest jede Datei, ohne Rückfrage (`mcp.rs:256-259`).**
|
||||||
|
Keine Prüfung gegen Projektordner oder Archiv-Home. Das Tool steht in
|
||||||
|
`PANEL_PERMISSIONS` (`pool.rs:185-188`), läuft also prompt-frei — und
|
||||||
|
umgeht damit das Read-Permission-Modell von Claude Code (`~/.ssh/id_rsa`,
|
||||||
|
`.credentials.json`, fremde `.env`). Rückkanal zum Modell besteht über
|
||||||
|
`write_panel` → `archive_panel` → `search_archive`.
|
||||||
|
Zu entscheiden: Allowlist (Projektordner + Archiv-Home, nach
|
||||||
|
`canonicalize`) oder `path` aus der prompt-freien Freigabe nehmen.
|
||||||
|
Zusätzlich fehlt ein Größenlimit (`path=/dev/zero` hängt den Prozess).
|
||||||
|
|
||||||
|
- [ ] **`archive_panel(dir)` vergibt dauerhaft Schreibrechte (`archive.rs:33-91`).**
|
||||||
|
`dir` ist nicht nur Ablageort: Über `set_project_archive_home` →
|
||||||
|
`add_archive_permission` landet `additionalDirectories += dir` **und**
|
||||||
|
`allow += "Edit(dir/**)"` in der `.claude/settings.json` des Projekts.
|
||||||
|
Der einzige Guard (archive.rs:41) lehnt nur Vorfahren des Home ab —
|
||||||
|
`~/.claude` passiert, womit die nächste Session eigene **Hooks** schreiben
|
||||||
|
darf. `create_dir_all` legt das Ziel vorher an.
|
||||||
|
Vorschlag: `dir` aus dem MCP-Tool entfernen, Archiv-Home nur über die UI.
|
||||||
|
|
||||||
|
- [ ] **Nicht-atomare Schreibvorgänge auf fremde Dateien.**
|
||||||
|
Überall `fs::write` (truncate + write), nirgends temp + `rename`.
|
||||||
|
Betrifft u. a. die `.claude/settings.json` der Projekte, Claudes
|
||||||
|
`.claude.json` in den Pools (`pool.rs:289`), die Registry und die
|
||||||
|
App-Settings. Die Leseseite haben wir gehärtet, die Schreibseite nicht:
|
||||||
|
Absturz oder volle Platte hinterlässt abgeschnittenes JSON in Dateien,
|
||||||
|
die der App nicht gehören.
|
||||||
|
|
||||||
|
Kleinere Punkte aus derselben Runde, bewusst zurückgestellt:
|
||||||
|
`panel_file` und Geschwister (`paths.rs:55-84`) ohne `check_name` (zweite
|
||||||
|
Linie); `write_secret_file` (`unix.rs:48-55`) schreibt erst mit umask-Rechten
|
||||||
|
und setzt danach 0600, `create_new` mit Modus löst das samt Symlink-Frage;
|
||||||
|
API-Key steht unter macOS kurz in der Prozess-Kommandozeile
|
||||||
|
(`macos.rs:101-103`, `ps`-sichtbar, Linux/Windows nicht betroffen); die PTY-Umgebung
|
||||||
|
wird nicht bereinigt (`terminal.rs:134-144`, kein `env_clear` — ein
|
||||||
|
`ANTHROPIC_API_KEY` aus dem Shell-Profil sticht den Pool-`apiKeyHelper`);
|
||||||
|
Bidi-/Zero-Width-Zeichen in den Befehlskacheln (`commands-view.ts:123`), was
|
||||||
|
Anzeige und Zwischenablage auseinanderlaufen lässt.
|
||||||
|
|
||||||
|
Ausdrücklich **kein** Handlungsbedarf (Stand 2026-07-19): `sync_all_desktops`
|
||||||
|
räumt `aicontrol-*.desktop` auf — der Präfix gehört uns. Falls es je stört,
|
||||||
|
wäre `art.lintus.`-Präfix die saubere Variante.
|
||||||
|
|
||||||
|
Nicht als Sicherheitsgrenze missverstehen: Pools trennen Konfiguration, nicht
|
||||||
|
Zugriff. Die Keychain-Einträge sind bewusst über `/usr/bin/security` angelegt,
|
||||||
|
damit der `apiKeyHelper` promptfrei liest — womit jeder Prozess des Nutzers
|
||||||
|
jeden Pool-Key lesen kann. Beim Design so gewollt, aber nichts, worauf man
|
||||||
|
Isolationsversprechen bauen sollte.
|
||||||
|
|
||||||
- [ ] **`write_app_setting` verliert die übrigen App-Settings (`settings.rs:71`).**
|
- [ ] **`write_app_setting` verliert die übrigen App-Settings (`settings.rs:71`).**
|
||||||
Dieselbe Fehlerklasse wie die `.claude.json` in `pool.rs`, nur eine Datei
|
Dieselbe Fehlerklasse wie die `.claude.json` in `pool.rs`, nur eine Datei
|
||||||
|
|||||||
Reference in New Issue
Block a user