087f4392a946c7e95a99c7f6eb5fcf1bd76cb352
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0e93345ab8 |
fix(2.8.1): Sechs Befunde, eine Bauart - die Voreinstellung, die einsprang
Drei Agenten haben von aussen gemeldet, und fast jeder Befund hat dieselbe Form: Irgendwo sprang ein Vorgabewert ein, obwohl die richtige Antwort bekannt war - meist das Projekt, an das der Aufrufer ohnehin gebunden ist. #351 manage?action=next antwortete durchgaengig mit HTTP 500. In BugRepo::buildFilter() stand :project_scope zweimal in derselben Anweisung; mit ATTR_EMULATE_PREPARES=false bindet PDO jeden Namen genau einmal - zwei Marker, ein Wert, SQLSTATE[HY093]. Getroffen war nur "next", weil nur dort der Rueckkanal-Filter gesetzt wird. Damit gab es keine exklusive Uebernahme. Es ist der VIERTE Fall dieser Bauart, deshalb prueft scripts/pruefe_platzhalter.py das jetzt mechanisch vor jedem Ausliefern. #382 Meldungen ohne project_slug landeten im Scheinprojekt "default" - #387 ohne dc_projects-Zeile, also project_id NULL. Die Antwort sah wie ein #397 Erfolg aus, und danach sperrte dieselbe Token-Bindung den Zugriff (403 project_forbidden). Der Rueckfall aufs Heimatprojekt war an !isWorkbench gebunden; ein Werkbank-Token ohne Slug bekam gar keinen Vorgabewert. Jetzt: genannter Slug, sonst Heimatprojekt, sonst 400. Ein unbekanntes Projekt wird abgewiesen statt still angelegt. #364 Ohne project_slug lieferten list, stats und die Roadmap ALLE #176 Projekte statt des eigenen, weil die Vorgabe auf "all" stand und ein Werkbank-Token beim Lesen absichtlich nicht unter die Schranke faellt. Gemessen: 393 Items, davon 28 eigene. Ausgerechnet der erste Aufruf jeder Sitzung. Die Roadmap antwortet ausserdem schmal - 55 Felder je Eintrag waren 628.392 Zeichen und fuellten das Kontextfenster. #404 Watchdog-Monitore bekamen nie ein Projekt: upsertHeartbeat() wurde keines uebergeben, obwohl authorizeSource() die Token-Bindung liest. Befunde erbten dadurch "default" - dc_findings(project_slug=...) war fuer eine laufende, meldende Instanz leer. #131 Wo der Leseweg richtig verengte, sagte er es nicht: HTTP 200 mit dem umgeschriebenen Filter sieht aus wie eine Auskunft ueber das angefragte Projekt. Ein Agent, der vor dem Melden dc_search fragt, legt so das Duplikat an. Jetzt scoped_to und scope_note im Umschlag - nur bei tatsaechlicher Verengung. #354 dc-testkit 1.1.0 wurde zweimal mit verschiedenem Inhalt ausgeliefert (1898 und 1930 Zeilen), 1.0.0 davor ebenso. Der Absatz ueber DC_TESTKIT_VERSION stand die ganze Zeit im Skript und hat nicht getragen. Jetzt 1.1.1 plus ein Riegel in deploy.py, der abbricht, wenn sich der Inhalt aendert und die Nummer nicht. Warum die Testsuite all das nicht sah: Ihr Master-Token ist an kein Projekt gebunden und nimmt deshalb ueberall den anderen Zweig. Es gab sogar einen Testfall fuer action=next - er lief gruen, waehrend der Aufruf in der Praxis ausnahmslos scheiterte. Die neuen Faelle benutzen ausdruecklich projektgebundene Token; testctl kann dafuer jetzt owner_type und scopes waehlen. Ausserdem: Die Agentenvorlage nennt fuer die SDK-Fassung dc_link_project statt einer abgeschriebenen Zahl - "SDK 2.5.1" stand dort als Beispiel und wurde als Wert uebernommen. Nicht enthalten: die 20 Altlasten in "default". Sie sind ueber die API nicht erreichbar; sql/wartung/2026-08-31_default-projekt-aufraeumen.sql liegt dafuer bereit (alle 20 haben einen titelgleichen Zwilling im richtigen Projekt). 156 e2e-Faelle gruen, davon 9 neue. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
51aa526f35 |
fix(deploy): interne Anleitungen bleiben aus dem Webroot
docs/TESTUMGEBUNG.md und docs/TESTINSTANZ.md gingen bei jedem Deployment mit hoch. Erreichbar waren sie dort nicht - aber nur, weil die Rewrite-Regel ^docs/(.+)$ auf public/docs/ umleitet und dort nichts Gleichnamiges liegt. Das ist das Zufallsergebnis einer Regel, die fuer etwas anderes da ist: Faellt mod_rewrite aus oder aendert jemand ihre Reihenfolge, liegen interne Adresse, Benutzername und der Weg zur sudo-Freischaltung offen. Beide Dateien sagen in ihrem eigenen Kopf, dass sie da nicht hingehoeren. Pfadgenau statt ueber IGNORE_PATTERNS: Ein Eintrag "docs" haette auch public/docs/ ausgeschlossen. docs/ selbst bleibt drin - cli/roadmap.php liest DC_ROOT/docs/ROADMAP.md, und die Integrationsanleitungen sind aus dem WebUI verlinkt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3b56839a1e |
fix(2.7.1): Auskuenfte, die stimmen - Fassung, Health, doctor, Ansicht
Der erste Durchlauf der Testinstanz hat neun Bugs zutage gefoerdert, und fast
alle waren dieselbe Sorte Fehler: eine Auskunft, die nicht stimmte.
Am folgenreichsten die Fassung. config/config.php fuehrte app.version als feste
Zeichenkette und stand seit 2.0.0 darauf, waehrend 2.7.0 lief - unbemerkt, weil
die Datei gitignored ist und in keinem Diff auftaucht. Zwei gemeldete Bugs sind
daran fehldiagnostiziert worden ("der Server ist zu alt"). src/bootstrap.php
leitet die Fassung jetzt aus "current" in public/docs/changelog.json ab.
Im Einzelnen:
- #346 Fassung aus dem Changelog statt aus der Konfiguration
- #60 /api/health ueberlebt eine leere Datenbank: eigener Check "schema",
healthy=false mit Begruendung statt HTTP 500
- #54 dc-testkit doctor prueft Mindestfassung (semantisch) und Rechte, statt
sie nur anzuzeigen - dafuer neu: GET /api/tokens/v1/whoami
- #53 dc-testkit.sh unter /testkit/ erreichbar (Route fehlte, der
dokumentierte Bezugsweg hat nie funktioniert) und mit LF ausgeliefert
(deploy.py/deploy_dev.py entfernen CR bei .sh/.bash beim Upload)
- #288 Filter und Formulare werfen nicht mehr aufs Dashboard zurueck: die
offene Ansicht wandert bei GET als "view" in den Query-String, und die
Filter loesen ueber requestSubmit() aus - form.submit() erzeugt kein
submit-Ereignis, weshalb der zentrale Zuhoerer dort nie lief
- #347 /api/bugtracker/v1/roadmap auch ohne .php erreichbar
- #55 Fassungshinweis in TESTUMGEBUNG.md 4.6 (serverseitig war das mit 2.6.0
bereits behoben)
Nicht geprueft: php -l. Auf dem Arbeitsplatz ist kein PHP installiert; die
Abnahme laeuft ueber die Testinstanz.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
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>
|
||
|
|
9d0261306c |
chore(cleanup): Altlasten entfernt, Webroot-Sperre und Supervisor-Route ergaenzt
Fruehjahrsputz. Alles hier ist unabhaengig von der laufenden Supervisor- Arbeit im Arbeitsverzeichnis - die bleibt unangetastet. Entfernt: - Die zwoelf Symbole unter public/assets/ waren bitgleiche Dubletten der Dateien in public/assets/icons/. Referenziert wurde ausschliesslich der Pfad mit icons/. getMonitorIconUrl() setzt flach gespeicherte Altwerte aus der Datenbank jetzt auf icons/ um, damit bestehende Monitore ihr Symbol behalten. - System.CommandLine (2.0.0-beta4) aus dem Packager. Kein using, kein Symbol, die Argumente werden von Hand geparst. Build bleibt fehlerfrei. Geschlossen: - client-php/ und mcp/ wurden weder von der .htaccess gesperrt noch vom Deployment ausgeschlossen. Die .mjs-Datei haette der Webserver im Klartext ausgeliefert. Beide gehoeren nicht ins Webroot: die PHP-Datei wird ins Zielprojekt kopiert, der MCP-Server laeuft lokal ueber stdio. Jetzt in beiden Ausschlusslisten - wie client-dotnet. - Fuer /api/supervisor/v1/ fehlte die Rewrite-Regel. Der Datei-Fallback haette /api/supervisor/v1/findings auf eine nicht vorhandene Datei geleitet, obwohl Code und OpenAPI diese Schreibweise vorsehen. Ergaenzt: - __pycache__/ und *.pyc in .gitignore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ceb977187e |
feat(releases): Zugangsschutz ueber Lizenzschluessel
/releases/ wurde bisher offen ausgeliefert, damit ausgelieferte Anwendungen ohne Zugangsdaten nach Updates suchen koennen. Das bedeutete aber auch, dass jeder im Internet die vollstaendigen Pakete herunterladen konnte - mitsamt allem, was versehentlich darin liegt. Genau so lag ein echter API-Schluessel in einer mitgelieferten appsettings.json oeffentlich abrufbar. Zugang haengt jetzt am Lizenzschluessel: Wer eine gueltige Lizenz fuer ein Produkt hat, kommt an dessen Updates. Die Anwendung kennt ihren Schluessel ohnehin und versorgt sich damit selbst - es muss nichts verteilt werden. Server - ReleaseGuard erzeugt je Produktverzeichnis .htaccess und .htpasswd. Bewusst getrennt: eine gemeinsame Datei wuerde bedeuten, dass eine Lizenz fuer Produkt A auch Produkt B oeffnet. license_licenses.product_id bindet jeden Schluessel ohnehin an genau ein Projekt. - Eingetragen werden aktive, nicht abgelaufene Lizenzen (Benutzername = Passwort = Schluessel; Basic Auth braucht zwei Felder, es gibt aber nur ein Geheimnis) sowie alle Installationskonten - bei einer Erstinstallation gibt es noch keinen Schluessel, mit dem sich das Paket holen liesse. - Deren Hash wird unveraendert aus dc_users uebernommen: password_hash() erzeugt bcrypt im Format $2y$, genau das versteht Apache. Ein Klartextpasswort wird nirgends gebraucht. Argon2-Hashes werden erkannt und uebersprungen statt eine unbrauchbare Datei zu erzeugen. - Lizenzschluessel werden mit Kosten 8 gehasht statt 12: 29 Zeichen maschineller Zufall sind kein Menschenpasswort, Apache prueft aber bei *jeder* Anfrage neu. - Geschrieben wird ueber eine temporaere Datei mit rename() - ein Abbruch wuerde sonst eine halbe Zugangsdatei hinterlassen und in dem Moment die halbe Kundschaft aussperren. - Neu erzeugt bei jeder Lizenz- und Kontoaenderung. Abgelaufene Lizenzen loesen anders als ein Widerruf nichts aus; dafuer gleicht cli/tick.php nach und erzeugt spaetestens alle sechs Stunden neu. - Statusanzeige und Schaltflaeche im WebUI unter UpdateService. Client - ReleaseCredentials: Lizenzschluessel oder Installationskonto als Basic Auth. - UpdateClient und Agent senden sie fuer latest.json und package.tar.gz. - UpdateCheckResult.Unauthorized trennt "Lizenz traegt nicht mehr" von einem Netzwerkfehler. Ohne diese Unterscheidung sucht man an der falschen Stelle. - LaunchUpdateAgent reicht licenseKey als --license-key durch. - Der Installer benutzt die beim Anmelden eingegebenen Zugangsdaten auch fuer den Paketabruf; das Setup-Token taugt dafuer nicht, weil Apache prueft und nicht die Anwendung. Sonstiges - deploy.py klammert artifacts/ aus. Ohne das landeten die gebauten Installer-Binaries zusaetzlich unter /artifacts/ im Webroot. ACHTUNG Reihenfolge: Der Schutz sperrt jede Anwendung aus, die noch mit dem alten SDK gebaut ist. Erst ausliefern, dann scharfschalten - siehe UPGRADE.md §16. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e7fbc85db4 |
fix(security, core): Auth-Pflicht für Ingest-APIs, 500er-Ursachen beheben, Agenten-Workflow
Sicherheit
- install_db.php war ohne Authentifizierung erreichbar und setzte bei jedem
Aufruf das Admin-Passwort auf einen fest im Code stehenden Wert zurück.
Jetzt Auth-Pflicht; ein Konto wird nur bei leerer Benutzertabelle angelegt.
- Stored XSS im Bugtracker-Detail-Modal: Titel, Beschreibung, Fehlermeldung,
Stacktrace und Kommentare gingen ungefiltert durch innerHTML.
- report.php, projects.php und das Veröffentlichen von Releases verlangen jetzt
zwingend ein Token. Publish war zuvor völlig ungeschützt.
- CSRF-Token in allen Formularen, Session-Regenerierung nach Login,
Drosselung fehlgeschlagener Anmeldeversuche.
- Zugangsdaten aus der Versionskontrolle entfernt (Serverdaten.txt,
config.php, .htpasswd, deploy_config.json). Historie enthält sie weiterhin,
Rotation erforderlich (siehe docs/UPGRADE.md).
- Token-Validierung nur noch über SHA-256-Hash; expires_at wird ausgewertet.
Behobene 500er
- Audit::log() war in index.php weder eingebunden noch importiert. Jeder
Klick auf "Aktivierung freigeben" endete in einem Fatal Error.
- Derselbe benannte PDO-Platzhalter mehrfach je Statement (:id in
revokeToken/deleteToken, :q siebenfach in der Volltextsuche). Bei
EMULATE_PREPARES=false ist das nicht zulässig und warf HY093.
- Migration 005 nutzte dynamisches SQL, dessen Semikolons in String-Literalen
vom alten explode(';')-Installer als Statement-Ende gelesen wurden. Sie
schlug still fehl, wodurch push_id/target_agent/tags dauerhaft fehlten.
- Monitor-Umbenennung ohne Transaktion, verschachtelte Transaktionen im
RateLimiter.
Funktionale Korrekturen
- Der Watchdog-Evaluator fehlte vollständig: Monitor-Zustände änderten sich nur
beim Eintreffen eines Heartbeats, ein ausgefallenes System blieb dauerhaft
"up". Erster Lauf auf dem Produktivsystem: 7 von 10 Monitoren waren
tatsächlich seit über einem Tag nicht erreichbar.
- Das Feld "os" fehlte im Monitor-Dialog, wurde aber gespeichert und löschte
damit bei jedem Speichern das Betriebssystem.
- Der Resolve-Dialog existierte im HTML nicht; der Button war funktionslos.
- Versionsvergleich erfolgte lexikografisch, wodurch 1.9.0 als neuer galt
als 1.10.0.
- Schreiboperationen meldeten Erfolg auch für nicht existierende IDs.
- Post/Redirect/Get gegen doppelte Einträge beim Neuladen.
Neue Struktur
- src/bootstrap.php mit PSR-4-Autoloader ersetzt die require-Ketten.
- Core: Config, Http, Csrf, ApiAuth, Logger, Migrator, ErrorReporter.
- Migrator mit zeichenweisem SQL-Parser, dc_migrations und Baseline-Verfahren,
damit bestehende Installationen keine Beispieldaten zurückbekommen.
Agenten-Workflow
- Claim/Lease: Items werden exklusiv übernommen, damit nicht zwei Agenten am
selben Problem arbeiten. action=next holt und reserviert in einem Zug.
- Idempotenz über client_ref, Deduplizierung auch für Feature Requests,
Erkennung von Regressionen, automatische Eskalation des Schweregrads.
- Strukturierter Code-Kontext (repo_url, commit_sha, file_path, line_no).
- Delta-Abfragen über updated_since, Pagination, Bulk-Update.
- Beim Veröffentlichen eines Releases schließen sich Items mit passendem
resolved_in_build selbst.
- Ausgehende Webhooks mit HMAC-Signatur, /api/health, /api/openapi.json.
- Unbehandelte Fehler meldet die Plattform in ihren eigenen Bugtracker.
WebUI
- Serverseitige Filterung mit Pagination statt Rendern aller Datensätze.
- Migrations-Schranke, Evaluator-Warnung, Übersicht aktiver Agenten.
Zeitstempel liegen in der Datenbank durchgängig in UTC und werden für die
Anzeige in die App-Zeitzone umgerechnet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
d71f90cdfc | feat: integrate Bugtracker module, UpdateService enhancements & Token hierarchy | ||
|
|
3a38fd4837 | Initial commit: Modular Deploymentcenter platform |