fix(setup): Dateirechte bei Geheimnissen, plattformabhaengige Zielnamen

Zwei Beobachtungen aus der laufenden Integration.

1. Der Installer schrieb Geheimnisse weltlesbar
   SetupWriter benutzte File.WriteAllText ohne Rechteanpassung - unter Linux
   also die Standardmaske und damit ueblicherweise 644. In genau dieser Datei
   stehen Lizenzschluessel und Anwendungstoken; jeder Benutzer des Systems
   konnte sie lesen. Der Lizenz-Cache in StateStore wird aus demselben Grund
   seit jeher auf 600 gesetzt - der Installer zog nicht nach.
   Enthaelt ein Ziel mindestens einen geheimen Wert, wird die Datei jetzt auf
   den eigenen Benutzer beschraenkt. Als geheim gilt type=secret UND
   source=provision: ein so geholtes Token traegt oft den Typ "string", ist
   aber genauso schutzbeduerftig. Unter Windows bleibt es beim Profil-ACL.

2. Zielnamen koennen plattformabhaengig unterschiedlich sein
   %APPDATA%\MeineAnwendung gegen $XDG_CONFIG_HOME/meineanwendung - eine
   setup.json kannte nur eine Schreibweise. Die kleingeschriebene Form allein
   traegt, weil NTFS die Schreibweise ignoriert, aber nur solange das
   Dateisystem tatsaechlich unempfindlich ist; auf APFS mit Beachtung der
   Schreibweise oder bei groesseren Unterschieden entstuende ein zweites,
   leeres Verzeichnis neben dem, aus dem die Anwendung liest.
   Ziele haben deshalb optional fileWindows, fileLinux und fileMacOS; ohne
   Angabe gilt weiterhin file.

Die Dokumentation haelt ausserdem fest, dass der Installer bewusst Klartext
schreibt und die Anwendung selbst entscheidet, ob und wie sie ihn danach
schuetzt - und dass eine Entschluesselung, die Klartext durchreicht, deshalb
kein Altlast-Zweig mehr ist, sondern ein aktiv genutzter Pfad.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Deploymentcenter Bot
2026-08-13 21:37:01 +02:00
co-authored by Claude Opus 5
parent 7a3a5dad69
commit 56b1d2631a
6 changed files with 183 additions and 7 deletions
+48
View File
@@ -219,6 +219,54 @@ Platzhalter fällt auf, ein stillschweigend falscher Pfad nicht.
> deshalb den **aufgelösten Pfad** aus — prüfe ihn. Für Dienste ist ein
> absoluter Pfad oft die ehrlichere Angabe.
#### Wenn die Schreibweise plattformabhängig ist
Konfigurationsordner heißen nicht überall gleich: `%APPDATA%\MeineAnwendung`
ist unter Windows üblich, `$XDG_CONFIG_HOME/meineanwendung` unter Linux klein.
Meistens genügt es, die **kleingeschriebene Form** anzugeben. Sie trifft unter
Linux exakt und unter Windows ebenfalls, weil NTFS die Schreibweise ignoriert.
Andersherum funktioniert es nicht — unter Linux entstünde ein zweites, leeres
Verzeichnis neben dem, aus dem die Anwendung liest.
Dieser Kniff trägt aber nur, solange das Dateisystem wirklich unempfindlich
ist. Auf APFS mit Beachtung der Schreibweise, auf einem per `fsutil`
umgestellten NTFS-Ordner oder wenn sich die Namen um **mehr als die
Schreibweise** unterscheiden, braucht es eine eigene Angabe:
```json
{
"id": "user",
"file": "meineanwendung/Settings.json",
"fileWindows": "MeineAnwendung/Settings.json",
"location": "config"
}
```
`fileLinux` und `fileMacOS` gibt es ebenso. Ohne Angabe gilt `file`.
### Dateirechte
Enthält ein Ziel mindestens einen als geheim gekennzeichneten Wert, wird die
geschriebene Datei auf den eigenen Benutzer beschränkt (`600` unter Linux und
macOS). Ohne das schriebe .NET mit der Standardmaske, unter Linux üblicherweise
`644` — und in dieser Datei stehen Lizenzschlüssel und Anwendungstoken.
Als geheim gilt jedes Feld mit `type: "secret"` **und** jedes mit
`source: "provision"` — ein so geholtes Token trägt oft den Typ `string`, ist
aber genauso schutzbedürftig.
Unter Windows geschieht nichts: Dateien in `%APPDATA%` erben die
Zugriffsrechte des Profils.
> **Der Installer verschlüsselt nicht.** Er schreibt Klartext; ob und wie
> Geheimnisse anschließend geschützt werden, entscheidet die Anwendung — etwa
> über DPAPI beim ersten Speichern. Das setzt voraus, dass eure
> Entschlüsselung Klartext unverändert durchreicht, statt an ihm zu scheitern.
> Wer eine solche Weiche hat, sollte sie im Test als **aktiv genutzt**
> beschreiben und nicht als Altlast — sonst wird sie eines Tages als tot
> entfernt und sperrt jede frisch installierte Instanz aus.
`type` bestimmt auch, **wie** geschrieben wird: `int` und `bool` landen als
JSON-Zahl beziehungsweise -Wahrheitswert, nicht als Zeichenkette. Sonst
scheitert die Bindung in der Anwendung.