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>
Vier Befunde aus einer externen Durchsicht der 2.4-Integration.
1. Die .htpasswd war eine Klartext-Kundenliste
Das htpasswd-Format hasht nur die Passwortspalte. Benutzername UND Passwort
waren der Lizenzschluessel - der Schluessel stand also im Klartext direkt
neben seinem eigenen bcrypt-Hash, und der Hash war Dekoration. Geschuetzt
hat das Ganze nur die FilesMatch-Regel in derselben Datei.
Der Benutzername wird jetzt abgeleitet: lic_<sha256(schluessel), 16 Hex>.
Die Datei enthaelt damit nur noch eine Einwegableitung und einen Hash ueber
einen hochentropen Schluessel.
Server und SDK muessen dabei zeichengenau uebereinstimmen; ein Test prueft
die C#-Ableitung gegen die PHP-Formel.
2. Ein Formatwechsel blieb unbemerkt liegen
Beim Umbau auf 1. faellt auf: reconcile() sah keinen Anlass zur
Neuerzeugung, die Dateien behielten das alte Format, waehrend die Clients
bereits das neue schickten. Die erzeugten Dateien tragen deshalb jetzt eine
Formatkennung; weicht sie ab, wird neu erzeugt.
3. Doku beschrieb Nginx, der Schutz ist Apache-only
.htaccess wird von Nginx ignoriert - dort waeren die Verzeichnisse offen und
die .htpasswd oeffentlich abrufbar. Die Statusanzeige pruefte nur, ob die
Dateien existieren, und haette in dem Fall "GESCHUETZT" gemeldet.
Neu: ein echter Selbsttest ruft die eigene Paket-Adresse OHNE Zugangsdaten
ab und erwartet 401. Er laeuft beim manuellen Erzeugen und nach jeder
automatischen Neuerzeugung; das Ergebnis steht in der Oberflaeche, ein
Fehlschlag im Log. Er findet nebenbei auch abgeschaltetes AllowOverride und
Tippfehler in der erzeugten Datei. Doku korrigiert, Nginx-Vorlage ergaenzt.
4. Erstinstallation schrieb an einen Ort, an dem Linux-Anwendungen nicht lesen
setup.json-Ziele waren immer installationsrelativ. Eine Anwendung, die sich
unter Linux richtig verhaelt, liest aus $XDG_CONFIG_HOME - /opt/<app> ist
fuer den Dienstbenutzer meist nicht schreibbar. Der Installer legte die
Datei also dorthin, wo nie jemand nachsieht.
Ziele haben jetzt ein "location": install (Vorgabe), config, data, home,
plus ${VAR}- und %VAR%-Ersetzung in "file". Unbekannte Variablen bleiben
stehen statt leer zu werden - ein Platzhalter faellt auf, ein falscher Pfad
nicht. Der Installer gibt den aufgeloesten Pfad aus, weil bei config das
Konto entscheidet, unter dem er laeuft.
Ausserdem
- Doku zeigte "status": "ok" fuer update/delete; Http::ok() erzeugt
"status": "success".
- UPGRADE §16.1 deckte Neuprodukte nicht ab: Fuer ein Produkt ohne Release
existiert /releases/<slug>/ nicht und wird uebersprungen. Das Verzeichnis
entsteht erst mit dem ersten Upload, der naechste Tick schuetzt es. Der erste
ausgelieferte Build muss die Zugangsdaten also schon mitbringen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>