12 KiB
Offene Punkte — bei jedem Start prüfen und abhaken
-
„Projekt hinzufügen" verliert
archiveHome— Ursache gefunden, behoben. Der Verdacht (Add-Projekt-Pfad) war falsch. Ursache war ein verlustbehafteter Config-Round-Trip:read_project_config_indeserialisiert inProjectConfig, serde verwirft dabei still jeden unbekannten Key, undwrite_project_config_inschreibt die Datei komplett neu. Geschrieben hat die Pool-Zuweisung (ProjectList.vue:113→assign_pool_in), ausgeführt von einem älteren Build ohne das Feldarchive_home— daher der Halbzustand mit erhaltener Berechtigung in.claude/settings.json(anderer Schreibpfad).Fix: `#[serde(flatten)] rest` in `ProjectConfig` reicht unbekannte Keys unverändert durch; Regressionstest `pool_zuweisen_erhaelt_fremde_keys`. Einschränkung: Die Versionsdifferenz ist aus Zeitstempeln erschlossen, das alte Binary war schon überschrieben. Der Defekt selbst ist am Code belegt. Ursprünglicher Befund 2026-07-19 (claude-ai-control): - `archive/` existiert seit 21:03 samt Testdokument, war also gesetzt. - `.claude/settings.json` hat den Eintrag noch: `additionalDirectories: ~/claude-projects/claude-ai-control/archive` (geschrieben von `add_archive_permission`, archive.rs:56). - `ai-control.json` hat **kein** `archiveHome` mehr, mtime 21:18. - Halbzustand Recht-ohne-Config kann nicht vom Abwählen-Button kommen: `clear_project_archive_home` (archive.rs:96-104) nimmt beides zurück. - Folge: `panel-wiring.ts:45` blendet Wiki- und Suche-Tab aus. Zu prüfen war: Schreibpfade auf `PROJECT_FILE` in `domain/project.rs` (`write_project_config_in`, project.rs:81) — welcher davon eine frische Config ohne `archive_home` schreibt. -
Command-History braucht eine Identität statt Indizes. Aufgefallen beim Fix von
commands_delete(Panik bei Doppelklick, Löschen der falschen Kachel). Der Fix schickt jetzt den Befehlstext mit und gleicht serverseitig ab — das verhindert Schaden, ist aber nur ein Notnagel: Zwei identische Befehle im selben Record sind nicht unterscheidbar, und die Panel-Indizes hinken der Datei weiter um bis zu ein Watcher-Intervall (200 ms) hinterher. Sauber wäre eine ID je Eintrag beim Schreiben. -
csp: nullintauri.conf.jsonersetzen. Aufgefallen beim XSS-Fix: Es gibt keine zweite Schranke. Das Panel liegt im selben Webview wie das Terminal, dessenterm_writedie PTY über das Fensterlabel adressiert — eingeschleustes Skript könnte in die laufende Shell schreiben. Das Markdown-Rendering ist jetzt entschärft (src/markdown.ts), aber eine echte CSP wäre die Schranke dahinter. Braucht eine Runde Prüfen, was inline benötigt wird (Themes, xterm). Schon eindefault-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_startprüft das Fenster-Label nicht (terminal.rs:96-211). Jedes Fenster des Terminal-Prozesses kann sich selbst in die Terminals-Map eintragen (Schlüssel istwindow.label(), Zeile 207-210) und danach perterm_writein 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 aufterm-*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 inPANEL_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 überwrite_panel→archive_panel→search_archive. Zu entscheiden: Allowlist (Projektordner + Archiv-Home, nachcanonicalize) oderpathaus der prompt-freien Freigabe nehmen. Zusätzlich fehlt ein Größenlimit (path=/dev/zerohängt den Prozess). -
archive_panel(dir)vergibt dauerhaft Schreibrechte (archive.rs:33-91).dirist nicht nur Ablageort: Überset_project_archive_home→add_archive_permissionlandetadditionalDirectories += dirundallow += "Edit(dir/**)"in der.claude/settings.jsondes Projekts. Der einzige Guard (archive.rs:41) lehnt nur Vorfahren des Home ab —~/.claudepassiert, womit die nächste Session eigene Hooks schreiben darf.create_dir_alllegt das Ziel vorher an. Vorschlag:diraus 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.jsonder Projekte, Claudes.claude.jsonin 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_settingverliert die übrigen App-Settings (settings.rs:71). Dieselbe Fehlerklasse wie die.claude.jsoninpool.rs, nur eine Datei weiter — beim Simplify-Review gefunden, bewusst nicht mitgefixt.read_app_settings(settings.rs:11-14) wirft mit zweimal.ok()?fehlende Datei, Lesefehler und kaputtes JSON in denselbenNone;write_app_settingmacht darausjson!({})und schreibt die Datei mit einem Key neu. Konkret: halb geschriebene~/.config/ai-control/settings.json, dann einmal Schriftgröße verstellen —claudeCommand,poolSyncDir,syncOnSessionEndundspellcheckLangsind endgültig weg. Fix wie inpool.rs: nur eine fehlende Datei rechtfertigt ein frisches Objekt. -
struct Poolverliert unbekannte Keys beim Umbenennen (pool.rs:20). Dritte Stelle derselben Klasse.rename_pool_in(pool.rs:402-408) liest typisiert inPool(nurname+credentialType) und schreibt die ganzepool.jsonüberwrite_pool_jsonneu — jeder künftige Key stirbt beim Umbenennen.ProjectConfigundTerminalConfighaben dafür inzwischen#[serde(flatten)] rest.Grundsätzlicher: `flatten` bildet pro Struct nach, was read-modify-write auf `serde_json::Value` von Natur aus kann (so macht es `add_archive_permission`, archive.rs:56-90). Statt es an jedem Struct einzeln zu wiederholen, wäre ein gemeinsamer Helfer die Lösung, die die Klasse wirklich schließt. -
Archiv-Dateinamen:
create_newstattexists()(archive.rs,free_path). Die Kollisionsprüfung schaut vorab nach (exists()), die Garantie liegt aber nicht im Schreibvorgang — klassisches TOCTOU. Als Einbenutzer-App folgenlos, aber nicht mehr beim Maschinen-Sync: Zwei Rechner können in derselben Minute mit derselben Titelzeile ins synchronisierte Archiv schreiben, und dann greift der Vorabblick zu spät.OpenOptions::new().create_new(true)in der Schleife gibt dieselbe Struktur, holt die Kollisionsfreiheit aber vom Dateisystem. Mitzudenken: Was der Sync-Mechanismus selbst bei zwei gleichen Namen macht (Konfliktkopien?) — der Fix deckt nur die lokale Seite. -
Projekt-Artefakte im Projektordner bündeln (
.ai-control/?).ai-control.jsonund das Projekt-Icon liegen heute lose im Projektordner. Idee: beides in ein Unterverzeichnis (z. B..ai-control/) zusammenziehen, damit der Projektordner sauber bleibt und alles an einem Ort steht. Offen: Migrationspfad für bestehende Projekte, Rückwärtskompatibilität beim Lesen (altes Layout weiter erkennen). -
Migrations-Assistent: Projekt sauber löschen. Löschen soll alle Ressourcen des Projekts aus dem Ordner entfernen (Config, Icon, ggf. Archiv-Rechte in
.claude/settings.json, Registry-Eintrag inprojects.json) — nicht nur den Registry-Eintrag. Vorher anzeigen, was entfernt wird. -
Archiv-Ordner-Handling sauber aufsetzen. Beim Setzen eines Archiv-Homes explizit abfragen statt still anlegen: Zielordner bestätigen, bestehenden Inhalt erkennen, klarmachen was passiert. Wechsel eines bereits gesetzten Archiv-Homes braucht eine Migrationsmöglichkeit (Dokumente mitnehmen oder liegen lassen) — als kritisch einstufen: hier hängen echte Nutzerdaten dran, also nichts implizit verschieben oder überschreiben.
-
poolgehört nicht in die projektbezogeneai-control.json. Der Pool ist rechnerspezifisch: gleicher Claude-Account, aber pro Maschine ein anderer Pool. Versioniert im Projekt trägt die Datei die Zuordnung über Rechnergrenzen falsch weiter (aktuell steht dort die UUIDc39d96c9-…, gepusht mit5081efc).Zu klären: Trennung projektbezogen (archiveHome, Theme, Icon) vs. maschinenbezogen (pool). Optionen — projekt-lokale, nicht versionierte Datei, oder Pool pro Rechner zentral in der `projects.json`-Registry unter `~/.config/ai-control`. Nachdenken, noch keine Entscheidung. -
Settings-Dialog (⚙) neu gestalten — dringend, Plan liegt vor (2026-07-20). Ist-Zustand: sieben Felder untereinander, drei Zeilen-Layouts, rohe Pfade, zwei gemischte Interaktionsmodelle (Ordner/Archiv schreiben sofort, Titel/Theme/Icon/Todo erst beim Speichern). Abgestimmter Plan: (1) drei Gruppen mit Eyebrows — Darstellung (Titel/Theme/Dock-Icon), Ordner (Projekt/Arbeitsordner/Archiv), Session (Todoliste); (2) einheitliche Zeile Label|Wert|Aktionen, Pfade ~-kontrahiert mit Hover-Pop; (3) Sofort-Schreiber optisch als eigene Gruppe, Speichern bezieht sich erkennbar nur auf Darstellung + Session, „gilt ab nächstem Start"-Hinweis an die betroffene Gruppe; (4) festes Dialog-Grid, Theme-Variablen des Hauptfensters. Nur ProjectList.vue (Template + Styles), keine Backend-Änderung.