Files
nexarch/docs/API-21-PRUEFPROTOKOLL.md
patrickandClaude Sonnet 5 1889f973bc
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (pull_request) Canceled after 0s
Archive-Pflichttest-Gate / pflichttest-gate (pull_request) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (pull_request) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (pull_request) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (pull_request) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (pull_request) Canceled after 0s
CI / Web (dms-browser) (pull_request) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Web (pdf-viewer) (pull_request) Canceled after 0s
CI / Web (shl) (pull_request) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
Core-Schnittstellen-Vertragstests / contract-tests (pull_request) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (pull_request) Canceled after 0s
Supply-Chain-Scan (npm) / npm-audit (pull_request) Canceled after 0s
Core-Pflichttest-Gate / pflichttest-gate (pull_request) Canceled after 0s
RBAC-Rollenhierarchie-Pflichtgate / rbac-policy-invariant (pull_request) Canceled after 0s
API-21: Pruefprotokoll
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 11:21:28 +02:00

5.4 KiB
Raw Permalink Blame History

API-21 – Architekturtest gegen Fehlertext-Leck in HTTP-Antworten – Prüfprotokoll

Basis: integration/gesamtprojekt (Head 2c09253) + Merge von feature/qa-15-phase2c-sicherheitsbehebung (API-20/AUD-08/IAM-23) als Vorbedingung. Geprüft auf Entwicklungshost 204, Wegwerf-Cluster 17/coredev, seriell über die gemeinsame Sperre. archivdms unberührt.

Umsetzung

  • internal/archtest (Core) und archive/internal/archtest (Archive) — je ein eigenständiges Paket, da beide Module getrennte go.mod-Dateien haben und keine gemeinsame Bibliothek existiert (dasselbe Muster wie bei internal/apierror/internal/httpharden).
  • Scan(root, exempt) durchläuft den Quellbaum per go/parser/go/ast (keine Typprüfung, Namenskonvention w für http.ResponseWriter — dieselbe Konvention, die im ganzen Code bereits durchgängig verwendet wird, u.a. in apierror.Write selbst).
  • Regel 1 (err-in-response): erkennt w.Write(...), fmt.Fprintf(w, ...), fmt.Fprint(w, ...), fmt.Fprintln(w, ...) und json.NewEncoder(w).Encode(...), wenn ein Argument rekursiv einen X.Error()-Aufruf enthält.
  • Regel 2 (raw-listen-and-serve): erkennt http.ListenAndServe(...) (Paketfunktion, nicht s.ListenAndServe() auf einem httpharden-erzeugten *http.Server).
  • Ausnahmen: ein Kommentar // archtest:allow <Begründung> direkt über der Fundzeile (oder als Trailing-Kommentar auf derselben Zeile) unterdrückt den Fund. Aktuell 0 Einträge in Core und Archive — keine Ausnahme nötig.
  • Modulgrenzen: Cores Scan schließt archive/, dms/, mail/ aus (eigene Module, DMS hat mit INT-01/INT-15 bereits ein eigenes Muster). Innerhalb jedes Moduls sind internal/apierror und internal/httpharden ausgenommen — dort ist das Schreiben des Fehlertexts bzw. der rohe ListenAndServe-Aufruf die Implementierung der Regel selbst.

Echter Fund, behoben

internal/statuspage/statuspage.go:268 (writeJSONResult) schrieb err.Error() direkt in eine 500-JSON-Antwort — vor API-20/API-21 unentdeckt, weil dieser Fund nicht in der ursprünglichen 116-Stellen-Liste von F-14 war (vermutlich weil erst nach dieser Erhebung entstanden oder übersehen). Zur Laufzeit war das Leck bereits durch apierror.Normalize (API-20, am Rand von httpharden.ListenAndServe) abgefangen — der Statuspage-Dienst läuft nachweislich darüber (cmd/statuspage-devserver/main.go:53). Trotzdem verlangt AC1 den sanktionierten Aufrufpfad auch am Ursprung. Behoben: apierror.Write mit allgemeiner Meldung, der Originalfehler geht ins Log (log.Printf("statuspage: %v", err)). Kein bestehender Test deckte den Fehlerzweig von OverviewHandler/HistoryHandler ab, daher keine Testanpassung nötig.

Prüfungen

Prüfung 1 — absichtlich eingefügter Verstoß bringt den Test zum Scheitern, danach entfernt:

  • Zunächst am echten Fund demonstriert: vor der Behebung von statuspage.go schlug TestNoErrorTextLeaksIntoHTTPResponses mit exakter Fundstelle (internal/statuspage/statuspage.go:268) fehl, danach bestanden. Das ist der schärfste Nachweis — ein realer, bis dahin unentdeckter Verstoß.
  • Zusätzlich als dauerhafter, automatisierter Beleg: TestScanDetectsInjectedViolations (Core und Archive je eigene Kopie) schreibt eine Testfixtur mit 3 err-in-response-Verstößen (Write, Fprintf, Encode), 1 raw-listen-and-serve-Verstoß und 1 durch archtest:allow begründet ausgenommenem Verstoß in ein temporäres Verzeichnis und prüft die exakte Fundzahl inklusive korrekt wirkender Ausnahme. Läuft bei jedem go test ./... mit — kein manueller Schritt, keine Rückbau-Notwendigkeit.

Prüfung 2 — bestehender Code besteht den Test ohne Änderung außer dokumentierten Ausnahmen:

  • Nach Behebung des einen echten Funds: go build ./..., go vet ./... und go test -count=1 ./... in Core und Archive vollständig grün, 0 Ausnahmen nötig.

Prüfung 3 — Ende-zu-Ende-Smoke bleibt grün:

  • scripts/e2e-core-dms.sh: TestCoreE2E_TokenJWKSAndPolicy 10/10 Teilfälle bestanden (50,7 s), inklusive der RBAC-10-Modulaktivierungs- und Mandantenbindungsfälle.

QA und Entscheidungen

  • Entscheidung — Erkennungsmethode ohne Typprüfung: Ein voller go/analysis-Typchecker wäre präziser, aber deutlich aufwändiger und würde golang.org/x/tools/go/analysis als neue Abhängigkeit einführen. Die Namenskonvention (w für http.ResponseWriter) ist im gesamten Code durchgängig eingehalten (Stichprobe über alle Handler in Core/Archive/DMS ergab keine Abweichung), das Risiko falscher Negative ist damit gering und über Codereview auffangbar.
  • Entscheidung — Scope nur Core und Archive: Wie im Ticket festgelegt (DMS hat eigenes Muster aus INT-01/INT-15, Mail war von API-20 nicht betroffen).
  • Entscheidung — kein Rückbau-Schritt für die Nachweise: TestScanDetectsInjectedViolations bleibt dauerhaft im Baum (bessere, wiederholbare Absicherung als ein einmaliger manueller Verstoß); die Testfixtur lebt nur in t.TempDir(), berührt also nie den echten Quellbaum.
  • 0 blockierende Iterationen — der einzige Fund war klein und eindeutig behebbar, keine Rückfrage nötig.

Offen

  • Keine Typprüfung → theoretische falsche Negative bei unüblicher Variablenbenennung (siehe oben, als Risiko akzeptiert und dokumentiert statt durch einen Typchecker geschlossen).
  • Mail-Modul hat weiterhin keinen eigenen Architekturtest (außerhalb des Tickets, siehe API-20-Protokoll: der eine Mail-Fund war eine nicht eingebundene Testfixtur ohne Leck).