Commit Graph
3 Commits
Author SHA1 Message Date
Deploymentcenter BotandClaude Opus 5 073d52ab7d feat(2.9.2): Nur geprueft in Hardware entstandene Schluessel als zweiter Faktor
Befund aus dem Betrieb: Ein Passkey aus 1Password liess sich als
Hardwarekey registrieren. Fuer das Deploymentcenter hinnehmbar, fuer ein
Protokoll, das ausdruecklich Hardware fordert, nicht.

Zwei Dinge, die wie eine Pruefung aussahen und keine waren - beide seit
2.9.0 gesetzt: authenticatorAttachment "cross-platform" steuert, was der
Browser anbietet, und ein Passwortverwalter meldet sich ebenso. Und
response.authenticatorAttachment setzt der Client, also genau die Instanz,
gegen die sich nichts beweisen laesst.

Was traegt, ist Basic Attestation. Neu geprueft wird die Kette bis zu einer
hinterlegten Herstellerwurzel; die drei FIDO-Wurzeln von Yubico liegen bei.
Ein x5c allein genuegt ausdruecklich nicht: Der Fall "eigenes Zertifikat,
Name Yubico, AAGUID eines echten YubiKey 5, gueltige Unterschrift" wurde
nachgebaut und wird abgewiesen - an der Kette, nicht an der Signatur.

Drei Stufen statt eines Schalters, weil alle bisher registrierten
Schluessel als nicht nachgewiesen gelten - auch echte Yubikeys. Bei
attestation "none" ersetzt der Browser die Geraetekennung durch Nullbytes,
bevor der Server sie sieht; es gab also nie eine Herkunft. Ein einzelner
Schalter haette beim Umlegen den eigenen Zugang gesperrt. Der Wechsel nach
"strict" wird zusaetzlich abgelehnt, solange das eigene Konto keinen
nachgewiesenen Schluessel hat.

- Attestation.php: packed und fido-u2f, Kettenpruefung gegen eigene
  Wurzeln statt gegen den Speicher des Betriebssystems, Gueltigkeit,
  Gegenprobe der AAGUID aus der Zertifikatserweiterung
- HardwarePolicy.php: off / register / strict, optionale Modellliste,
  Wurzeln aus der Konfiguration erweiterbar
- Migration 021: attested und drei Anzeigespalten, Vorgabe 0
- WebUI: Herkunft je Schluessel, Regel vor der Beruehrung angesagt
- testctl clear_credentials, damit ein Testlauf den naechsten nicht
  aussperrt
- Testreihe "hardware", 5 Faelle, ohne Krypto-Bibliothek

184 von 184 Testfaellen gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 10:44:19 +02:00
Deploymentcenter BotandClaude Opus 5 f0ed67ad29 feat(2.9.0): Hardwarekey an der WebUI-Anmeldung und die Roadmap als Tafel
Zwei angeforderte Funktionen, dazu zwei Befunde, die ihnen im Weg lagen.

== Zweite Stufe der Anmeldung (#666) ==
Nach korrektem Passwort verlangt das WebUI einen registrierten FIDO2-Schluessel.
Die Pflicht haengt je Konto daran, OB ein Schluessel registriert ist - nicht an
einem Schalter. Ein Schalter, den man vor der Registrierung umlegt, sperrt den
Betreiber aus seiner eigenen Verwaltung aus.

Ohne Fremdbibliothek gebaut (das Projekt hat bewusst kein Composer):
src/Core/Webauthn/ mit CBOR-Leser, COSE-nach-DER-Wandlung und Pruefung ueber
openssl_verify (ES256, RS256). Auth::login() heisst jetzt Auth::startLogin()
und liefert drei Zustaende statt eines bool - aus "wahr heisst drin" ist "wahr
heisst vielleicht drin" geworden, und ein uebersehener Aufrufer soll an einem
fehlenden Methodennamen scheitern statt an einer truthy Zeichenkette. Die
Setup-API der Zielsysteme geht ueber verifyCredentials() daran vorbei und
bleibt unberuehrt.

Notfalltoken als Ersatzweg: nur als Hash in der Datenbank, Klartext einmalig
angezeigt, Einmalgebrauch. Laeuft als gewoehnliches Formular ohne JavaScript -
ein Ersatzweg mit denselben Voraussetzungen wie der Hauptweg waere keiner.
Fehlversuche auf der zweiten Stufe laufen in dieselbe Konto- und IP-Sperre wie
das Passwort; zuvor zaehlte nur die erste Stufe.

Benutzerverwaltung kann Konten umbenennen. Beim Loeschen eines Kontos und beim
Zuruecksetzen der Testinstanz werden seine Schluessel mitgenommen: credential_id
ist geraeteweit eindeutig, eine verwaiste Zeile haette denselben Yubikey
dauerhaft unbrauchbar gemacht.

== Roadmap-Tafel (#664) ==
Neuer Unterreiter Roadmap -> Tafel. Der Server liefert die fertige Gliederung
(RoadmapBoard, ?action=board), die Seite rendert nur - sonst schneidet das
WebUI anders als die API und ein spaeterer Export ein drittes Mal anders.

Phasen stehen nicht im Code: gepflegt in der neuen Tabelle roadmap_phases,
sonst an den groessten Luecken der Rangfolge geschnitten und sichtbar als
geraten gekennzeichnet. Links die gekappte Spalte des Erledigten, rechts
"Wird ueberlegt". Nachpruefung ist ein eigener Zustand und wird nicht als
erledigt gezeichnet.

Klick oeffnet ein Seitenpanel: Volltext, Verlauf, Kommentieren mit
action_taken, Aendern von Bereich, Rang, Zielfassung, Status und Ausloeser -
ohne Seitenwechsel. Das war der eigentliche Gewinn: Wer etwas bemerkte, musste
sich bisher die Nummer merken und im Bugtracker danach suchen. Tiefe Verweise
#roadmap/<projekt>/item/<id>. Ziehen zwischen Phasen ueber ?action=rank_between.

Fremdtext wird ausschliesslich ueber textContent gesetzt, nie ueber innerHTML.

== Befund: roadmap_rank war projektweit und bereichslokal zugleich (#177) ==
Entschieden: projektweit. move() sucht den Nachbarn ohne Bereichsfilter,
reorder() schreibt keinen ganzen Bereich mehr auf 10/20/30 um, sondern
vertauscht nur die Raenge, die die Eintraege ohnehin halten. rank_between
rechnet die Mitte zwischen zwei Nachbarn und nummeriert nur dann einmal neu -
unter Erhalt der Reihenfolge -, wenn dort kein Platz mehr ist; die Antwort
sagt es dann.

== Befund: CSRF an sitzungsauthentifizierten API-Schreibzugriffen ==
ApiAuth akzeptiert die WebUI-Sitzung, die Endpunkte prueften dabei kein
CSRF-Token. Der Kommentar in Http::beginJson - "ausschliesslich per
Token-Header, niemals per Cookie" - traf auf die API nie zu. Schreibende
Anfragen im Sitzungsweg verlangen jetzt csrf_token bzw. X-CSRF-Token.
Token-Clients sind nicht betroffen und wurden gegengeprueft.

== Geprueft ==
tests/e2e/suite_tafel.py neu (16 Faelle), Suite gesamt 172 von 172 gegen die
Testinstanz. Die WebAuthn-Zeremonien zusaetzlich gegen einen Software-
Authenticator mit echtem P-256 und echtem CBOR: Registrieren, Anmelden,
verfaelschte Signatur, veraltete Herausforderung und Zaehler-Rueckfall als
Klonverdacht werden je mit 403 abgewiesen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 18:16:51 +02:00
Deploymentcenter BotandClaude Opus 5 1d5ac92051 feat(2.7.0): Roadmap im DC, Testinstanz mit Testtor, Supervisor und MCP
Sammelcommit fuer den ueber mehrere Sitzungen gewachsenen, bisher nicht
eingecheckten Stand. Getrennte Commits waeren ehrlicher gewesen, sind es
nachtraeglich aber nicht mehr: BugRepo, changelog.json und die Doku tragen
Aenderungen aus allen Straengen gleichzeitig, und keine Zwischenstufe waere
lauffaehig.

ROADMAP IM DEPLOYMENTCENTER (2.7.0)

Planungsstand, Reihenfolge und Zustaendigkeit lagen in handgepflegten
Markdown-Dateien neben einem Bugtracker, der genau diese drei Dinge bereits
fuehrt. Zwei Pfleger fuer dasselbe Feld - Drift war eine Frage der Zeit.

Ab jetzt: Zustand im DC, Begruendung in der Datei. Kein Feld hat zwei Pfleger.

  - Migration 016: epic, roadmap_rank, target_build, defer_trigger,
    doc_anchor, Status "deferred", Tabelle roadmap_epics.
    KEINE Roadmap-Tabelle - ein Eintrag ist ein bugtracker_item.
  - Zugehoerigkeit ist ein Feld: Ein Item steht auf der Roadmap, wenn es ein
    epic hat. Ein zweites Kennzeichen daneben koennte auseinanderlaufen.
  - Kennung ist die Item-ID. Handvergebene Kuerzel ("A12") erzeugen die
    schlimmste Fehlerart: Beide Seiten stimmig, Verschiedenes gemeint, weil
    die Kennung nach einer Loeschung neu vergeben wurde.
  - Zurueckstellen verlangt defer_trigger. Ohne benannten Anlass ist ein
    Vorhaben nicht zurueckgestellt, sondern vergessen. Die Regel sitzt im
    Repository, damit sie auf allen drei Schreibwegen gleich greift.
  - Modules/Bugtracker/Roadmap.php, /api/bugtracker/v1/roadmap.php,
    WebUI-Modul mit Filtern, cli/roadmap.php --write/--check,
    dc_roadmap und dc_roadmap_set im MCP-Adapter.
  - cli/roadmap.php --write ERZEUGT den Zustandsteil der Markdown-Datei;
    --check prueft nur die Prosa. Was erzeugt wird, kann nicht driften.
  - docs/ROADMAP_MIGRATION.md: Uebernahme fuer die anderen Projekte.

TESTINSTANZ UND TESTTOR

  - dc-dev.mhdf.de: zweite Installation, eigene DB, eigene Schluessel,
    Basic-Auth davor, Rocket.Chat aus.
  - 124 Testfaelle in tests/e2e gegen API, Rechte, Migrationen, Sperre.
  - scripts/deploy.py bringt vor jedem Upload erst den anstehenden Stand auf
    die Testinstanz und laesst dann den Testlauf laufen. Erst bei
    Rueckgabewert 0 geht etwas auf die Produktion (--ohne-test als Notausgang).
    Andersherum prueft der Lauf aelteren Code als den, der gleich hochgeht.
  - tests/ ist aus dem Produktiv-Deploy ausgeschlossen; testctl.php kann
    nicht versehentlich auf dc.mhdf.de landen.
  - Ein Lauf mit mehr uebersprungenen als geprueften Faellen endet mit
    Rueckgabewert 2 statt 0. Nichts ist kaputt, geprueft wurde es aber auch
    nicht - der Unterschied darf nicht am Rueckgabewert verschwinden.

SUPERVISOR, PROJEKTLANDSCHAFT, NACHPRUEFUNG (2.6.0/2.6.1)

  - Migration 013: projektuebergreifend melden (dev_agent, evidence,
    triage_external, affects).
  - Migration 014: supervisor_findings, Katalog, fuenf serverseitige
    Detektoren im Tick.
  - Migration 015: Nachpruefung als Arbeitsschlange statt Benachrichtigung.
  - DEV-Lizenzschluessel zur Selbstbedienung, dc-testkit fuer die Test-VM.
  - MCP-Adapter: 15 Werkzeuge, ohne Fremdabhaengigkeiten, mit Abgrenzung
    von Fremddaten gegen Prompt-Injektion.

BEHOBEN

  - observe und status im Supervisor legten den Zustand des Befundes als
    "status" in die Antwort. Http::ok() merged ['status' => 'success']
    darunter, also gewann der Befundzustand - eine angenommene Meldung kam
    als status="open" an. Heisst jetzt finding_status. War Item #59.

Migrationen 013-016 sind auf dem Produktivserver noch NICHT ausgefuehrt.
Ohne sie bricht das WebUI, weil die Roadmap-Ansicht bei jedem Seitenaufruf
gegen bugtracker_items.epic abfragt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 21:59:20 +02:00