Files
tessera-ctl/.planning/quick/261009-p0m-sicherheitsprotokoll-und-ci-sicherheitsp/261009-p0m-SUMMARY.md
T
schalli d62e6c2dbe
Tessera CI/CD / Lint & Type Check (push) Successful in 53s
Tessera CI/CD / Tests (push) Failing after 2m14s
Tessera CI/CD / Desktop-Pakete bauen (push) Has been skipped
Tessera CI/CD / Build & Publish Images (push) Has been skipped
Tessera CI/CD / Sicherheitspruefung (nur Bericht) (push) Has been skipped
docs(quick-261009-p0m): Sicherheitsprotokoll und CI-Pruefungen
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 21:07:46 +02:00

43 KiB

phase, plan, status, tasks_done, plan_head_before, plan_head_after, commits, actuals
phase plan status tasks_done plan_head_before plan_head_after commits actuals
quick-261009-p0m 01 complete
1
2
3
4
5
6
d15a470c5a b085889657 9
tasks commits
6 9

Quick 261009-p0m: Sicherheitsprotokoll + CI-Sicherheitspruefungen -- Summary

Task 1: Tracer -- Job security als reiner Bericht (erledigt)

Commit: 3d00be8 ci(quick-261009-p0m): Sicherheitspruefung als reiner Bericht in der Pipeline (nicht gepusht)

Dateien im Commit: .gitea/scripts/security-scan.sh (neu, ausfuehrbar), .gitleaks.toml (neu), .semgrepignore (neu), .gitignore, .dockerignore, .gitea/workflows/ci.yml (nur Anhaengen: Kopfzeile + Job security, keine andere Zeile geaendert), docs/ci-cd-setup.md (§4 fuenf Jobs, Unterabschnitt, §6-Eintrag). Nicht eingecheckt (Vorgabe des Orchestrators, alles unter .planning/ bleibt unversioniert): baseline/.gitignore, baseline/SUMMARY.txt (Block "nach Ausnahmelisten"), checks/task1-runner-full.txt, diese Datei.

Gates (alle gruen, Sentinel task1 ok gedruckt)

  • ci.yml per js-yaml geparst: Jobs quality,test,desktop,publish,security; needs: publish, continue-on-error: true, timeout-minutes: 30 (Auflage des Plan-Checkers, Gate erweitert), Bedingung main/Tags v*, kein secrets. im Job, kein anderer Job haengt an security, jeder run endet auf || true, fetch-depth: 0, upload-artifact@v3 mit continue-on-error.
  • --print-plan: main -> api:beta/web:beta, v9.9.9 -> api:live, Zweig live -> scan-image keine ....
  • .gitleaks.toml strukturell (tomllib): useDefault, jede Liste mit Beschreibung; nur das Fixture-Verzeichnis als Ordner, alles andere einzelne verankerte Datei mit targetRules; de.json zusaetzlich condition=AND + regexTarget=line + Regex auf die eine Zeile "authMethodPassword": "Benutzer/Passwort".
  • gitleaks ueber die volle Historie (1369 Commits inkl. neuem Commit): 0 Treffer (vorher 20).
  • Voller Lauf im echten Runner-Abbild gitea/runner-images:ubuntu-latest (Netz gitea, Socket-Mount, frisches Werkzeug-Volume, Wegwerf-Klon mit Kanarienvogel-Token): rc=0, gitleaks findings=1 (nur Kanarienvogel, github-pat), trivy-fs secrets=1 (nur Kanarienvogel), alle uebrigen Werkzeuge und beide Abbilder gezaehlt -> Annahme A1 bestaetigt (Job-Container scannt Abbilder des Host-Daemons). Semgrep per pipx im Container installiert und gelaufen.
  • Offline-Lauf (--network none, schreibgeschuetzter Klon): rc=0, 12 skipped reason=-Zeilen (>= 7 verlangt).
  • Host-Installation: gitleaks 8.30.1, trivy 0.75.0, osv-scanner 2.6.0 liegen in ~/.cache/tessera-security (Semgrep dort uebersprungen: kein pipx). pnpm 9.15.0 aus dem PATH.
  • Wegwerf-Klon, Volume und root-eigene Berichte entfernt; Token steht in keiner Datei ausserhalb des Klons (geprueft: ghp_ in der Nachweisdatei = 0 Treffer).

Zahlen nach Ausnahmelisten (Details in baseline/SUMMARY.txt)

gitleaks 0; pnpm audit --prod C5 H73 M68 L5; osv-scanner 56 (pnpm-lock + Cargo.lock; Grundlinie 43 nur pnpm-lock); Semgrep 34 Treffer (ERROR 2, WARNING 29, MEDIUM 3) / 4 Scanfehler (PartialParsing, 0 Zeitueberschreitungen), vorher 58/59; trivy-fs C5 H73 M70 L5, misconfig 2, secrets 0; trivy-image api C6 H116 M99 L7, web C2 H19 M22 L1.

Deviations from Plan

  1. [Orchestrator-Vorgabe] Nichts unter .planning/ eingecheckt. Der Plan nennt baseline/.gitignore, baseline/SUMMARY.txt und checks/task1-runner-full.txt als Commit-Dateien; sie liegen auf der Platte, bleiben aber unversioniert. Gates lesen sie von der Platte und sind gruen.
  2. [Rule 2 / Plan-Checker] timeout-minutes: 30 am Job ergaenzt (im Plan nicht vorgesehen), Gate und docs/ci-cd-setup.md (Unterabschnitt + §6) sind angepasst.
  3. Trivy-Ausnahme fuer die Testschluessel liegt als zur Laufzeit erzeugte trivy-secret.yaml (allow-rule auf den Fixture-Ordner) im Arbeitsordner des Skripts, nicht als Repository-Datei; wirkt nur auf den Secret-Scanner (trivy-fs secrets 7 -> 0, Kanarienvogel weiterhin gefunden).
  4. Der Skript-Aufruf all rechnet Semgrep-Zeitlimit pro Regel/Datei --timeout 20 (Plan: "per-file timeout"); Semgrep kennt nur dieses eine Limit.

Hinweise fuer Task 2 (Protokoll)

  • Zahlen "roh" aus baseline/SUMMARY.txt (oberer Teil) und "nach Ausnahmelisten" (unterer Block "nach Ausnahmelisten (2026-10-09, Task 1)"). Unterschied osv: 43 (nur pnpm-lock) vs. 56 (mit Cargo.lock). Semgrep roh 58 -> 34; die 4 Scanfehler sind alle PartialParsing (ci.yml, beide Dockerfiles), keine Zeitueberschreitungen mehr.
  • Abbild-Zaehlung des Skripts summiert OS- und Node-Pakete (api M99, web M22; Forschung nannte M98/M21 nur Node + je 1 Alpine).
  • Neue Dateien: .gitleaks.toml (6 Ausnahmegruppen), .semgrepignore; ci-cd-setup.md beschreibt Version-Anheben-Rezept (Version + URL + SHA256 gemeinsam).
  • Hostwerkzeuge liegen bereit (Precondition von Task 2 erfuellt: git log -- .gitea/scripts/security-scan.sh zeigt 3d00be8; gitleaks version -> 8.30.1).
  • Erster echter CI-Lauf steht aus (Orchestrator/Nutzer-Checkliste nach dem Push): Job security muss gelb/gruen laufen, SECURITY-SUMMARY-Zeilen erscheinen, upload-artifact@v3 ist best effort (Annahme A4 ungeprueft). Der Skript-Lauf ist bis auf den Artefakt-Schritt im Runner-Abbild nachgestellt.
  • Plattenstand nach Task 1: 80 % (36 GB frei), unter der 85-%-Schwelle.
  • Der Eintrag semgrep ... skipped reason=kein-pipx auf dem Host ist erwartet.

Task 2: Sicherheitsprotokoll, Verweise, Pflegeregel, CHANGELOG (erledigt)

Commit: 5b36b9a docs(quick-261009-p0m): Sicherheitsprotokoll mit Bestandsaufnahme und erster Vollpruefung (nicht gepusht)

Dateien im Commit: docs/sicherheitsprotokoll.md (neu, 227 Zeilen), docs/README.md (Absatz "Ebenfalls dabei"), docs/anleitung-betrieb.md (Satz im Kopf + Absatz in Kapitel 8 "Automatische Sicherheitspruefung"), docs/anleitung-entwicklung.md (Inhaltsverzeichnis 9 = Sicherheitspruefungen, 10 = Konventionen; neuer Abschnitt mit "So pflegen Sie dieses Protokoll", "Die Pruefung in der Pipeline", "Pruefungen von Hand wiederholen", "Ausnahmelisten"), CHANGELOG.md (ein Bullet "Sicherheit: ..." unter Neu; keine vierte Gruppe). Nicht eingecheckt (Vorgabe): checks/link-check.py (liegt auf der Platte), diese Datei.

Gates (Sentinel task2 ok gedruckt)

  • Alle sieben Ueberschriften exakt, alle 20 Schluesselwoerter, keine Du-Form, kein "Lizenz", kein "Mandant" im Protokoll (strenger als verlangt), " Sie " vorhanden.
  • gitleaks dir auf das Protokoll: keine Funde. Link-Pruefer: 8 Links geprueft, 0 kaputt (Anker #sicherheitsprüfungen aufgeloest).
  • CHANGELOG: nur Neu/Geaendert/Behoben, Bullet "- Sicherheit: ... Sicherheitsprotokoll ...".

Inhalt des Protokolls (fuer die Folgeaufgaben wichtig)

  • Abschnitte: Auf einen Blick, So lesen Sie, Wie die Pruefungen arbeiten (Unterabschnitte je Werkzeug), Bisherige Sicherheitspruefungen (Tabelle der acht Pruefungen, Haertung Adressschutz 09.10., Bedrohungsbetrachtungen 179 Plaene / 884 Kennungen frisch ausgezaehlt, Datentrennung als bestehender Schutz ohne Schaltzustand, Tabelle weiterer Auftraege, Fehlerregister, Tests, "Was es bisher nicht gab"), Erste vollstaendige Pruefung (Tabelle roh / nach Ausnahmelisten), Einordnung der Befunde (23 Zeilen, alle Abhaengigkeits-Zeilen noch "offen"), Verlauf (5 Eintraege, neueste oben).
  • Task 3 muss ergaenzen: Unterabschnitt "Aussenpruefung (ZAP)" unter "Wie die Pruefungen arbeiten", Verlauf-Eintrag fuer den ersten ZAP-Lauf (oben), "Auf einen Blick", Kapitel 9 der Betriebsanleitung. Das Protokoll nennt den Alpha-Hostnamen bewusst noch nirgends.
  • Tasks 4/5/6 muessen Stand-Spalte der Zeilen von "offen" auf "behoben (Datum)" setzen: Next.js, proxy-addr, handlebars (Paket entfernt), nodemailer, undici, multer, adm-zip 0.6.1, "Uebrige Bausteine", Entwicklungswerkzeuge im api-Abbild, npm im web-Abbild, rustls, AES-GCM-Tag; Zeilen xlsx, node-forge, glib bleiben offen; Abschnitt "Stand nach der Behebung" mit den Zahlen der Abschlussmessung; "Auf einen Blick" und Verlauf nachziehen. Die Zeile "Fehlende Gesundheitspruefung web" steht auf "offen".
  • Wahrheitsgehalt der Bestandsaufnahme (aus den Dateien/Git nachgeprueft): Phase 08 Fix in eebceb2 (CR-01, CR-02, WR-01..05, IN-01 behoben; IN-02/IN-03 weiter offen, im Code bestaetigt); Phase 16 vier Warnungen behoben, IN-01/IN-02 offen (im Code bestaetigt); Phase 18 vier behoben, zwei Hinweise nicht bearbeitet; dts IN-01/IN-02/IN-04 uebersprungen (Datenbankaenderung/Entwurf noetig); dkv alle 11 inkl. EXTRA behoben; ikt alle 15 behoben. Summe 89 Befunde: 76 behoben, 13 Hinweise offen. Fehlerregister: 39 Eintraege, 25 behoben, 1 zurueckgestellt, 13 offene gehoeren alle zur Datentrennung zwischen Organisationen und werden im Protokoll nur in einem Satz erwaehnt (D-07).
  • Triage-Fakten, die ich gegen die Rohdaten geprueft habe: adm-zip-Meldung ohne Korrektur ist "Mittel" (Symlinks beim Entpacken); node-forge-Meldung betrifft die Pruefung von Unterschriften; xlsx: Prototype-Pollution und ReDoS; trust proxy wird im Quelltext nirgends gesetzt.

Deviations (Task 2)

Keine fachliche Abweichung. Kleine Anmerkung: Der Plan nennt .planning/ nicht als Commit-Ort fuer link-check.py; er bleibt gemaess Orchestrator-Vorgabe unversioniert und das Gate liest ihn von der Platte. Die Anhaengsel-Zeile der Anlage des Commits nutzt wie verlangt die Opus-Zeile als Trailer.

Hinweise fuer Task 3

  • Hostwerkzeuge liegen bereit. Das Protokoll wartet auf den ZAP-Teil (siehe oben). link-check.py kann nach jeder Aenderung der Dokumente wiederholt werden (python3 -I .planning/quick/261009-p0m-sicherheitsprotokoll-und-ci-sicherheitsp/checks/link-check.py).
  • Beim Ergaenzen von Kapitel 9 der Betriebsanleitung den Anker-Stil beibehalten: Querverweise im Protokoll zeigen auf anleitung-entwicklung.md#sicherheitsprüfungen.
  • Wortregeln fuer das Protokoll beibehalten (Gate von Task 2 prueft sie nicht fuer Task 3): Sie-Form, kein "Lizenz", kein "Mandant", keine internen IPs.

Task 3: ZAP-Grundpruefung (erledigt, mit Abweichung beim Ziel)

Commit: 13571df ci(quick-261009-p0m): ZAP-Grundpruefung gegen alpha vor Freigaben, erster Lauf (nicht gepusht)

Dateien im Commit: .gitea/scripts/zap-baseline.sh (neu, ausfuehrbar), .gitea/scripts/zap-hooks.py (neu), docs/sicherheitsprotokoll.md, docs/anleitung-betrieb.md (Kapitel 9, Schritt 2 "Pruefung von aussen" vor "Zusammenfuehren und taggen"), docs/anleitung-entwicklung.md (Unterabschnitt "Pruefung von aussen (ZAP)"), CHANGELOG.md (Sicherheits-Bullet um die Aussenpruefung ergaenzt). Nicht eingecheckt (Vorgabe): checks/zap-testserver.py, checks/task3-zap-local.txt, checks/zap-alpha-2026-10-09.txt, baseline/SUMMARY.txt (ZAP-Zeile angehaengt), diese Datei. baseline/zap-hook-basic-auth.py geloescht.

Abweichung (Orchestrator-Entscheidung)

Die oeffentliche Adresse von alpha war vom Dev-Host nicht erreichbar (Zeitueberschreitung auf Port 443, 3 Versuche; allgemeines Internet ging). Auf Anweisung des Orchestrators wurde der erste Lauf direkt gegen die Anwendung auf alpha ohne Proxy ausgefuehrt (interne Adresse, Port 3000, /login = 200, Version per /api-proxy/health/version: v1.10.1-80-gd15a470, Kanal beta). Das Skript hat das Ziel als Parameter ZAP_TARGET (Standard weiter die oeffentliche Adresse). Im Protokoll steht nirgends die IP, nur "interne Adresse des Testservers"; das Protokoll sagt ausdruecklich, dass der Lauf ueber die oeffentliche Adresse noch folgt. Das Gate wurde angepasst (ZAP-SUMMARY-Zeile mit beliebigem Ziel statt festem Hostnamen; zusaetzliche Pruefungen: keine private IP in den Docs, keine Du-Form/Lizenz/Mandant, gitleaks auf dem Protokoll). Sentinel task3 ok gedruckt.

Lokaler Beweis (Passivitaet)

Testserver mit POST-Formular, ZAP ueber die Docker-Bruecke, ZAP_SPIDER_MINUTES=1: lauf-ohne-anmeldung POST=0 GET=8; lauf-mit-anmeldung POST=0 auth_ja=8 auth_nein=1 (die eine Anfrage ohne Kopf ist die Vorabpruefung des Skripts). Auch der Hakenweg fuer die Anmeldedatei funktioniert. Beim Test fiel auf und wurde behoben: bei vorzeitigem Abbruch (SIGPIPE) blieben die temporaeren Dateien mit dem Kopf liegen, deshalb wird die Umgebungsdatei jetzt sofort nach dem Container-Lauf entfernt und die Falle faengt auch HUP/PIPE ab (beide Reste von /tmp geloescht). Der Bericht enthaelt bei Nutzung der Anmeldedatei die gesendeten Kopfzeilen samt Zugangsdaten (in Skript-Kopf und Entwicklungsanleitung vermerkt; Berichte liegen nur in security-reports/, ignoriert).

Erster Lauf gegen alpha (2026-10-09 19:41, Ziel: Anwendung direkt, 28 URLs, 0 POST)

ZAP-SUMMARY: hoch 0, mittel 2, niedrig 6, info 3 (11 Meldungsarten).

  • Mittel: fehlende Content-Security-Policy; fehlender Schutz gegen Einrahmen (Clickjacking) -- beide auf /login, /reset-password.
  • Niedrig: Cross-Origin-Embedder-/Opener-/Resource-Policy fehlen; Permissions-Policy fehlt; X-Content-Type-Options fehlt; X-Powered-By (Next.js-Standard).
  • Info: Content-Type fehlt (Umleitungen 307 fuer nicht angemeldete Besucher), nicht speicherbar, speicherbar.
  • Einordnung im Protokoll: Kopfzeilen = offen ("Aufgabe des Proxys, beim oeffentlichen Lauf erneut pruefen; sonst in der Anwendung setzen"; next.config.ts setzt keine Kopfzeilen und kein poweredByHeader: false), HTTPS/HSTS = nicht beurteilt, offen (Proxy), Infos = bewusst akzeptiert. Es wurde kein Tessera-Code und keine Proxy-Konfiguration geaendert.

Hinweise fuer Task 4 und folgende

  • Protokoll: neue Tabelle "Pruefung von aussen gegen alpha" und fuenf neue Zeilen in "Einordnung der Befunde" (Kopfzeilen mittel/niedrig, X-Powered-By, HTTPS/Proxy, Infos); Verlaufseintrag steht oben. Task 6 soll "Auf einen Blick" (Bullet "Pruefung von aussen") und diese Zeilen nachziehen; ein spaeterer Lauf ueber die oeffentliche Adresse waere eine weitere Tabellenzeile + Verlaufseintrag.
  • Angebot an den Nutzer (nicht ausgefuehrt, kein Auftrag): poweredByHeader: false und Sicherheits-Kopfzeilen in apps/web/next.config.ts waeren eine kleine, sichere Haertung; vorher klaeren, ob der Proxy sie schon setzt.
  • link-check.py laeuft gruen (11 Links). Hostwerkzeuge und das ZAP-Abbild (3.8 GB, per Digest) liegen lokal; Plattenstand 80 %.

Task 4: Abhaengigkeiten innerhalb der Hauptversionen (erledigt)

Commit: cf982e9 fix(quick-261009-p0m): Abhaengigkeiten innerhalb der Hauptversionen aktualisiert, zwei unbenutzte entfernt (nicht gepusht)

Dateien im Commit: package.json (neu: pnpm.overrides, 15 Eintraege), pnpm-lock.yaml, apps/api/package.json, apps/web/package.json, apps/api/src/mail/mail.module.ts und apps/api/src/calendar/providers/exchange.provider.ts (nur falsch gewordene Kommentare), apps/desktop/src-tauri/Cargo.lock, docs/sicherheitsprotokoll.md, docs/anleitung-entwicklung.md (neuer Abschnitt ### Abhängigkeiten aktualisieren), CHANGELOG.md (dritter "Sicherheit:"-Bullet unter Behoben). Nicht eingecheckt (Vorgabe): checks/mail-smoke.sh, checks/task4-audit.txt, diese Datei.

Audit vorher / nachher (checks/task4-audit.txt)

critical high moderate low gesamt
prod vorher 5 73 68 5 151
prod nachher 0 9 3 0 12
alle vorher 7 85 74 5 171
alle nachher 2 9 5 0 16

Verbleibend prod (12): xlsx 2 hoch (keine Korrektur), node-forge 1 hoch (keine Korrektur), sharp 3 hoch (Korrektur erst 0.35 = andere 0.x-Minor), deepmerge-ts 1 hoch (Prisma-Werkzeugkette, Korrektur erst v8), nodemailer 2 hoch + 3 mittel (Korrektur erst in nodemailer 10 = neue Hauptversion). Verbleibend nur dev: tinypool 1.1.1 (2 kritisch) + vitest/@vitest/mocker 3.2.6 der API (2 mittel), Korrektur jeweils nur in neuer Hauptversion (vitest 4 / tinypool 2).

Was geaendert wurde

  • Entfernt: @nestjs-modules/mailer, ews-javascript-api (vorher per grep nachgewiesen: kein Import in apps/; Exchange bleibt httpntlm + rohes SOAP). 232 Paketnamen entfallen aus dem Lockfile.
  • Direkt angehoben: next ^15.5.27 (installiert 15.5.27); @nestjs/common/core/platform-express ^11.2.7 (multer 2.4.0); nodemailer ^9.1.1; undici exakt 7.30.0; adm-zip ^0.6.1; csv-parse ^7.0.3; vitest (web) ^4.1.11 (noetig, weil @vitest/mocker < 4.1.11 gemeldet).
  • Overrides (alle innerhalb derselben Hauptversion, 15 Stueck): baseline-browser-mapping, brace-expansion (1.x und 5.x), browserslist 4, fast-uri 3, ip-address 10, js-yaml 4, nanoid 3, nodemailer 9, postcss 8, proxy-addr 2, qs 6, source-map-js 1, underscore 1, undici 7 (auf 7.30.0, damit nur eine undici-Kopie im Baum bleibt). Selektorform name@>=N <N+1 (bzw. <2 fuer brace-expansion 1.x) wirkt; per Lockfile geprueft.
  • Prisma weiter 6.19.3, Next 15, NestJS 11, React 19.2.7.
  • rustls 0.23.41 -> 0.23.45; cargo update -p rustls --precise zog zusaetzlich rustls-webpki 0.103.13 -> 0.103.15 mit. cargo check und cargo clippy fehlerfrei, keine Warnungen.
  • Nichts musste zurueckgepinnt werden (kein Baustein als "offen wegen Bruch").

Neue-Namen-Pruefung (T-p0m-SC)

Vergleich der Paketnamen der packages:-Sektion vor/nach: 0 neue Namen, 232 entfallen. Kein Vater noetig. (Neue Versionen vorhandener Namen sind keine neuen Namen.)

Gates (Sentinel task4 ok gedruckt, Kette aus dem Plan wortgetreu)

frozen-Lockfile-Install, Versionspruefung (pakete ok), rustls 0.23.45, cargo check/clippy, pnpm type-check, pnpm lint, API-Tests 169 Dateien / 3573 Tests, Web-Tests 160 Dateien / 1945 Tests, Audit nachher (0 kritisch, 9 < 73 hoch), docker compose up -d --build --wait api web, /login = 200, e2e-changelog, e2e-preload-api, e2e-cert all, nc-test-setup, e2e-files, e2e-transfer, e2e-shares all, mail-smoke (mail ok), Anleitungs-Ueberschrift, >= 3 "Sicherheit:"-Bullets. Link-Pruefer: 11 Links, 0 kaputt.

Deviations (Task 4)

  1. [Rule 1] checks/mail-smoke.sh liest die E-Mail-Adresse aus GET /users, nicht aus /auth/me. /auth/me liefert kein E-Mail-Feld (Plan-Annahme falsch); /users (als admin) liefert es. Der Test vorher auf dem unveraenderten Stack gruen (mail ok), danach ebenfalls; Mailhog total waechst.
  2. Rate-Limit beim Rerun: Ein zweiter Lauf von e2e-shares.sh innerhalb weniger Minuten laeuft in das Anlegelimit (tooManyShares, 429, 320 s). Kein Fehler im Code; nach 5,5 Minuten Pause lief die Kette komplett gruen. Bei jedem Wiederholen: erst warten.
  3. vitest (web) ^4.1.11 zusaetzlich zu den im Plan genannten Bumps (Audit meldete @vitest/mocker unter 4.1.11); Minor-Bump im selben Major, alle Web-Tests gruen.
  4. Protokoll in Task 4 laut Plan aktualisiert (Verlauf, Zeilen in "Einordnung der Befunde", "Auf einen Blick"); Zeile "Mailversand nodemailer" bleibt "offen" (Rest nur mit Hauptversion 10), Zeile "Übrige Bausteine" aufgeteilt in "behoben" und eine neue "offen"-Zeile fuer sharp und deepmerge-ts.

Hinweise fuer Task 5 und 6

  • Stack laeuft aus Task 4 gebaut (api, web, db, mailhog healthy); Plattenstand 80 % nach docker image prune -f (vorher kurzzeitig 84 %).
  • Protokoll-Zeilen noch zu schliessen: "Entwicklungswerkzeuge im Server-Abbild", "Paketverwalter npm im Node-Grundabbild" (Task 5), "Laenge der Echtheitspruefung bei AES-GCM" (Task 5); Task 6 muss "Stand nach der Behebung" mit den Abschlusszahlen, "Auf einen Blick" (Bullet "Was noch zu tun ist" nennt bereits Abbilder + AES-GCM) und Verlauf nachziehen. Offen bleiben: xlsx, node-forge, glib, nodemailer (Hauptversion 10), sharp, deepmerge-ts, Gesundheitspruefung web, Kopfzeilen/HTTPS.
  • Die Abbild-Trivy-Zahlen im Protokoll ("Erste vollstaendige Pruefung") sind noch die Rohwerte; Task 6 misst die neuen.
  • Der Rest der api-Abhaengigkeiten ist unveraendert; die alten Trivy-Abbild-Zaehlungen (api C6 H116) sinken durch Task 4 bereits deutlich -- erst die Abschlussmessung zeigt es.
  • CLAUDE.md-Zeilen (D-26), die jetzt hinterherhinken (nicht angefasst): Next.js 15.5.19 -> 15.5.27; NestJS 11.1.27 -> 11.2.7; Vitest (apps/web) 4.1.9 -> 4.1.11. Express 5.2.1 und Prisma 6.19.3 unveraendert. Der Eintrag "Prisma: pin exactly" stimmt weiter (Lockfile 6.19.3; package.json hat ^6.0.0, das war schon vorher so).
  • Im Desktop-Lockfile hat sich Cargo.lock geaendert: der naechste CI-Push auf main baut die Desktop-Pakete neu (erwartet, D-24).
  • Die Dev-Bausteine tinypool/vitest 3 (API-Tests) sind nur mit einem vitest-4-Wechsel der API zu beheben (neue Hauptversion der Testwerkzeuge, nicht Teil dieses Auftrags); im Protokoll ist das als Verlaufsnotiz genannt, aber nicht als eigene Tabellenzeile.
  • Orchestrator-Frage fuer spaeter: nodemailer 10 ist die groesste uebrige Produktionsposition (2 hoch, 3 mittel); ein Wechsel waere ein eigener kleiner Auftrag (Nutzung beschraenkt auf createTransport/sendMail).

Task 5: AES-GCM-Tag, Laufzeitabbilder ohne Entwicklungswerkzeuge, Schutz-Kopfzeilen (erledigt)

Commits (nicht gepusht):

  • 072f955 fix(quick-261009-p0m): AES-GCM-Entschluesselung verlangt 16-Byte-Tag (Teil A, Tests zuerst rot)
  • 2a8a7d4 build(quick-261009-p0m): Laufzeitabbilder ohne Entwicklungswerkzeuge (Teil B + Doku)
  • fceec19 fix(quick-261009-p0m): Schutz-Kopfzeilen in der Weboberflaeche, X-Powered-By abgeschaltet (Zusatzauftrag des Orchestrators)

Nicht eingecheckt (Vorgabe): checks/task5-image.txt, checks/fresh-db-start.sh, diese Datei.

Teil A (AES-GCM)

Tests zuerst: 5 neue Faelle in crypto.service.spec.ts (4 Byte, 17 Byte, leer, gekipptes Bit, Rundlauf inkl. leerem Text und Umlauten). Vor der Aenderung 3 rot (4 Byte, 17 Byte, leer); echt unsicher war nur der 4-Byte-Fall (Node akzeptierte ihn und entschluesselte), 17 Byte und leer warf Node schon, aber mit anderer Meldung als die neue 16 bytes-Pruefung (daher rot); das gekippte Bit wurde schon vorher abgelehnt. Danach 13/13 gruen. decrypt prueft authTag.length === 16 vor dem Decipher und uebergibt authTagLength: 16 (auch bei createCipheriv, Ausgabe unveraendert). biome format --write + biome check --write (sortierte die Imports und brach alte Zeilen im Spec auf 100 Zeichen um, das Format der Projektkonfiguration; daher groesserer Diff im Spec als nur die neuen Tests).

Teil B (Abbilder) -- umgesetzt, kein Rueckbau noetig

  • apps/api/Dockerfile: neue Stufe prod-deps (kopiert Manifeste, Lockfile, Workspace-Datei und apps/api/prisma/, dann pnpm install --frozen-lockfile --prod --filter=@tessera/api...; der postinstall von apps/api erzeugt den Prisma-Client dort, weil prisma in dependencies liegt). Laufzeitstufe FROM node:24-alpine, entfernt als Erstes /usr/local/lib/node_modules/{npm,corepack}, /opt/yarn-v1.22.22, /usr/local/bin/{npm,npx,corepack,yarn,yarnpkg}; node_modules kommen aus prod-deps; die lange .prisma-COPY-Zeile mit festem Pfad entfaellt. apps/web/Dockerfile: nur dieselbe Entfernung in der Laufzeitstufe.
  • Groessen und Fundzahlen (checks/task5-image.txt, Trivy 0.75.0 --scanners vuln, --image-src docker, Betriebssystem + Node-Pakete):
Abbild Groesse vorher nachher critical high medium low
api vorher (nach Task 4) 1,59 GB (1592651888 B) 3 45 41 2
api nachher 1,12 GB 0 6 4 0
web vorher (nach Task 4) 359 MB 0 11 13 1
web nachher 359 MB 0 3 1 0

Die Web-Groesse bleibt gleich: rm in einer spaeteren Schicht verkleinert das Abbild nicht (nur die Sicht von Trivy); das ist im Protokoll so erklaert und die Doku verspricht "kleiner" nur fuer das Server-Abbild. Die Rohwerte der ersten Messung im Protokoll (api C6 H116, web C2 H19) bleiben dort als "Erste vollstaendige Pruefung"; Task 6 misst neu.

  • Gates (Sentinel task5 ok, Kette aus dem Plan wortgetreu, Log scratchpad/task5-gate.log): Crypto-Tests, biome, authTagLength vorhanden, tsc --noEmit, komplette API-Tests, docker compose up -d --build --wait api web, /login = 200, @prisma/client loest aus /app/apps/api auf, node im web-Container, keine Paketverwaltung in api und web, keine Entwicklungswerkzeuge in /app/node_modules/.pnpm (vitest, tinypool, turbo, biome, Nest-CLI, schematics: alle weg), @prisma+client@ vorhanden, fresh-db-start.sh -> migrationen=63, frische-datenbank ok, Fundzahl nachher < vorher, "keine Paketverwaltung" in der Betriebsanleitung, e2e-cert all, e2e-changelog, nc-test-setup, e2e-files, e2e-transfer, mail-smoke (mail ok), "Zugangsdaten"-Bullet, Link-Pruefer (12 Links, 0 kaputt). Zusaetzlich vorab ein eigener Lauf der e2e-Kette (E2E-ALL-OK).
  • apk (Paketverwaltung des Betriebssystems) bleibt im Abbild; die Vorgabe nannte nur npm, npx, corepack und yarn.
  • Rueckbau/Fallback: nicht benutzt. Keine devDependency wird zur Laufzeit importiert (Start gegen frische DB und alle Rauchtests gruen).

Zusatzauftrag: Schutz-Kopfzeilen (eigener Commit fceec19)

  • apps/web/next.config.ts: poweredByHeader: false und headers() fuer /:path*: X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, X-Frame-Options: SAMEORIGIN, Content-Security-Policy: frame-ancestors 'self' (nur diese Richtlinie), Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), Cross-Origin-Opener-Policy: same-origin-allow-popups. Keine COEP/CORP (koennten eingebettete Bilder/Kacheln sperren, nichts zu schuetzen).
  • Vorpruefung im Code: Zwischenablage nur clipboard.writeText (clipboard-write bleibt erlaubt, per featurePolicy im Browser bestaetigt: clipboard-write und fullscreen true, camera und geolocation false); Benachrichtigungen haben keine Permissions-Policy-Direktive; keine Nutzung von getUserMedia/Geolocation/Zahlung/USB. Alle window.open/target=_blank tragen noopener; kein window.opener, postMessage oder BroadcastChannel im Web-Quelltext -> COOP sicher. Nextcloud Login Flow v2 ist ein einfacher <a target=_blank rel=noopener> (Abfrage laeuft serverseitig). Desktop: Tauri navigiert das Hauptfenster direkt auf die Server-Adresse (window.navigate), kein Rahmen. XFrame-Kacheln/eigene Module setzen sandbox und allow="" und rahmen fremde Seiten ein -- die Kopfzeilen von Tessera betreffen nur das Einrahmen von Tessera.
  • API: Express sendete X-Powered-By: Express (vorher per curl -I bestaetigt). configureHttp ruft jetzt app.disable('x-powered-by') (Typ HttpApp auf NestExpressApplication, main.ts erzeugt NestFactory.create<NestExpressApplication>); Test in http-setup.spec.ts (der Test "Anfragelog und Cookies kommen zuerst" ignoriert die disable-Aufrufe).
  • Tests: neu apps/web/src/next-config.test.ts (4 Faelle; eine Konfigurationsdatei-Test gab es vorher nicht). Web-Suite gesamt 161 Dateien / 1949 Tests, pnpm lint, pnpm type-check gruen.
  • Pruefung am neu gebauten lokalen Stack: curl -I /login, / (307) und 404-Seiten tragen alle Kopfzeilen, kein X-Powered-By bei Web und API (auch nach dem Neubau auf HEAD erneut geprueft). Der Pfad /api-proxy/... (Umleitung zur API) traegt die Web-Kopfzeilen nicht, nur die Antwort der API; sie sind fuer JSON-Antworten nicht noetig, ZAP hat sie dort nicht beanstandet (nur auf /login, /reset-password).
  • Browser (Playwright-Kern, headless Chromium, Dunkelmodus, scratchpad/smoke/*.cjs): Anmeldung und Dashboard, 0 Konsolenmeldungen (kein Hinweis auf unbekannte oder verletzte Richtlinien); Zertifikatsmanager laedt, Testzertifikat + Schluessel hochgeladen, "Fullchain herunterladen" liefert p0m-smoke.test-fullchain.pem; Dateien: "Im Browser anmelden" -> Link oeffnet neuen Tab auf .../login/v2/flow, window.opener === null; eigenes Modul (https://example.com/) im Rahmen geladen (Modul danach geloescht, API 200). Der MCP-Browser stand dem Agenten nicht zur Verfuegung, daher das Playwright-Paket aus dem npx-Cache.
  • Protokoll: Zeilen "Fehlender Schutz gegen Einrahmen", "Weitere fehlende Schutz-Kopfzeilen", "X-Powered-By" jetzt "behoben in der Anwendung, beim oeffentlichen Lauf erneut pruefen"; neue Zeile "Vollstaendige Inhaltsrichtlinie (Content Security Policy)" = offen mit Grund; "Auf einen Blick" (Bullet Pruefung von aussen) und Verlaufseintrag "Schutz-Kopfzeilen in der Anwendung". CHANGELOG: ein "Sicherheit:"-Bullet unter "Geaendert". Entwicklungsanleitung: Absatz "Schutz-Kopfzeilen der Weboberflaeche" unter "Konventionen und Fallstricke" (inkl. Warnung: clipboard-write nie sperren; noopener beibehalten).

Abweichungen (Task 5)

  1. [Rule 3] Dokumentation getrennt committet: Die Doku von Teil B und die des Zusatzauftrags liegen in denselben Dateien; fuer zwei saubere Commits wurden die Zusatz-Hunks fuer den ersten Commit voruebergehend herausgenommen und danach wiederhergestellt (kein Inhaltsverlust, beide Commits einzeln pruefbar).
  2. Teil A: biome check --write sortierte die Imports in crypto.service.ts um (Biome-Regel organizeImports), und biome format brach alte Zeilen im Spec um. Verhalten unveraendert.
  3. Die CHANGELOG-Aenderung kam nach dem ersten Bau; deshalb wurde der Stack nach dem letzten Commit nochmals mit --build neu gebaut (Voraussetzung von Task 6).

Hinweise fuer Task 6

  • Stack laeuft gebaut aus HEAD fceec19 (docker compose up -d --build --wait api web nach dem letzten Commit, alle healthy). Lokale Abbilder: tessera-ctl-api:latest 1,12 GB, tessera-ctl-web:latest 359 MB; fuer den Abschlusslauf SCAN_IMAGES="tessera-ctl-api:latest tessera-ctl-web:latest" verwenden. Erwartung der Skriptzaehlung (OS + Node, nicht --scanners vuln-identisch, kann abweichen): api ~C0 H6, web ~H3.
  • Protokoll noch zu schliessen in Task 6: "Stand nach der Behebung" mit Abschlusszahlen; die Tabelle "Erste vollstaendige Pruefung" behaelt die Rohwerte. Alle Zeilen "Einordnung der Befunde" haben jetzt einen Status; "offen" bleiben xlsx, node-forge, glib, nodemailer 10, sharp/deepmerge-ts, Gesundheitspruefung web, Vollstaendige Inhaltsrichtlinie, HTTPS/Proxy, Pnpm-Arbeitsbereichseinstellungen u. a.; die drei neuen Statuswoerter "behoben in der Anwendung, beim oeffentlichen Lauf erneut pruefen" enthalten das Wort "behoben" und passen auf die Task-6-Zeilenpruefung (Status + letzte Spalte nicht leer). Task-6-Gate: grep -qx "semgrep-regel gcm-no-tag-length=0": die Regel sollte jetzt nichts mehr finden.
  • Der Beweisdateiname checks/task5-image.txt enthaelt die Zeile ergebnis umgesetzt.
  • Platte: 55 % belegt (78 GB frei) nach den Arbeiten; docker image prune -f hat nichts mehr zu entfernen.
  • CLAUDE.md-Tabelle (D-26) unveraendert; zusaetzlich hinkt jetzt nichts Neues nach.
  • Orchestrator-Hinweis: der naechste ZAP-Lauf (oeffentliche Adresse, nach dem naechsten alpha-Pull) sollte die Zeilen der Kopfzeilen auf "behoben" setzen oder pruefen, ob der Proxy sie doppelt sendet; die Anwendung sendet sie jetzt selbst.

Task 6: Abschlusslauf, Protokoll-Abschluss, CLAUDE.md, Aufraeumen (erledigt)

Commits (nicht gepusht):

  • bc7fffe docs(quick-261009-p0m): Technology-Stack-Zeilen auf den Stand der Lockdatei gebracht (CLAUDE.md, Zusatzauftrag, ueberschreibt D-26)
  • b085889 docs(quick-261009-p0m): Sicherheitsprotokoll mit Stand nach der Behebung

Nicht eingecheckt (Vorgabe): checks/final-run.txt, baseline/SUMMARY.txt (Block "nach Behebung"), diese Datei. Gate-Sentinel task6 ok gedruckt (Kette aus dem Plan wortgetreu).

Abschlusslauf (checks/final-run.txt)

Frischer Klon (git clone --no-local) von HEAD fceec19, Container gitea/runner-images:ubuntu-latest (Netz gitea, Docker-Socket, frisches Werkzeug-Volume, GITHUB_REF=refs/heads/main, kein Kanarienvogel), Skript all: rc=0.

gitleaks findings=0
pnpm-audit-prod critical=0 high=9 moderate=3 low=0
osv-scanner packages=20
semgrep findings=33 errors=4
trivy-fs critical=0 high=9 medium=4 low=0 misconfig=2 secrets=0
trivy-image-api critical=0 high=6 medium=4 low=0
trivy-image-web critical=0 high=3 medium=1 low=0
semgrep-regel gcm-no-tag-length=0
  • Die Zeilen der Abbilder heissen trivy-image-api/-web, weil das Skript den Namen aus dem Abbildnamen ableitet. Mit den lokalen Compose-Namen (tessera-ctl-api:latest) hiessen sie trivy-image-tessera-ctl-api (erster Versuch, gleiche Zahlen) und haetten das Plan-Gate nicht getroffen. Loesung ohne Skriptaenderung: der gesamte Lauf wurde wiederholt mit den lokalen Abbildern kurz als api:p0m-final/web:p0m-final getaggt (kein Registry-Name; Tags danach entfernt). Die Zeilen der Datei stammen aus diesem zweiten Lauf; er war der erste Lauf mit wiederverwendetem Werkzeug-Volume (semgrep ... bereit (vorhanden)), also ein lokaler Beleg fuer Annahme A2.
  • Reste der Abbild-Funde (per Trivy am lokalen Abbild nachgemessen): api = deepmerge-ts, node-forge, nodemailer, xlsx (je HIGH, alle ohne Korrektur in der Versionslinie); web = nur sharp.
  • osv 20 = 8 npm (xlsx, node-forge, nodemailer, sharp, deepmerge-ts, vitest 3.2.6, @vitest/mocker 3.2.6, tinypool) + 12 crates (glib, anyhow, event-listener, h2, quick-xml x2, proc-macro-error, unic-* x5).
  • Semgrep Rest: 14 mutable Aktions-Tags (+1 curl|sh), 9 non-literal-regexp, 4 TLS-Umgehungen (bewusst), 1 open-redirect, 1 prototype-pollution-loop, 3 pnpm-Haertungsschluessel; die GCM-Regel ist weg.

Protokoll-Abschluss

  • Neuer Abschnitt ## Stand nach der Behebung (Vorher/Nachher-Tabelle je Werkzeug, Liste des Offenen in Alltagssprache) und Verlaufseintrag ### 2026-10-09 — Stand nach der Behebung (Kurzfassung mit Verweis; Tabelle steht aus Wartungsgruenden nur im Abschnitt). "Auf einen Blick" (Bausteine, automatische Pruefung, Was noch zu tun ist) auf den Endstand gebracht.
  • "Einordnung der Befunde": 33 Zeilen, alle mit Stand und Begruendung. Neu: Zeile "Weitere Hinweise zu Bausteinen der Desktop-App" (11 Rust-Bausteine, offen; Patch-Fassungen fuer anyhow 1.0.103, event-listener 5.4.2, h2 0.4.16 existieren) und Zeile "Testwerkzeuge vitest 3 / tinypool" (offen). Pipeline-Zeile auf 14 mutable Tags aktualisiert, GCM-Zeile um "Semgrep meldet nicht mehr" ergaenzt.
  • Wortregeln geprueft: kein "Lizenz", "Mandant", keine Du-Form, keine IP-Adressen; Link-Pruefer 12 Links 0 kaputt; gitleaks auf Protokoll und volle Historie (1377 Commits) 0 Funde.

CLAUDE.md (Zusatzauftrag, ueberschreibt D-26)

Zeilen angepasst nach pnpm-lock.yaml (importers:): Next.js 15.5.27, NestJS 11.2.7, Vitest (apps/web) 4.1.11, dazu der Satz unter "Recommended But Not Adopted" (Next 15.5.27). Express 5.2.1, Prisma 6.19.3, React 19.2.7, Tailwind 4.3.1, TypeScript 5.9.3, Biome 2.5.0, Turborepo 2.9.18, Vitest (apps/api) 3.2.6, next-intl, Zustand und die uebrigen Zeilen stimmen unveraendert. Kopfhinweis ergaenzt: Stand 2026-09-09; die drei Zeilen am 2026-10-09 gegen die Lockdatei neu geprueft (Docker-Zeilen bleiben bewusst mit ihrem Messdatum). nodemailer und undici stehen nicht in der Tabelle.

Aufraeumen

Entfernt (Name fuer Name): semgrep/semgrep:1.180.0, aquasec/trivy:0.75.0, ghcr.io/aquasecurity/trivy:0.75.0, ghcr.io/google/osv-scanner:v2.6.0, ghcr.io/gitleaks/gitleaks:latest und :v8.30.1, curlimages/curl:latest; docker image prune -f hatte danach nichts zu entfernen (nie -a). Behalten: ZAP-Abbild (Digest geprueft), gitea/runner-images:ubuntu-latest. Wegwerf-Klon, Werkzeug-Volume p0m-final-tools und die Zwischentags geloescht, root-eigene Berichte per Container entfernt; in security-reports/ bleibt nur der ZAP-Ordner von alpha. Platte: 54 % belegt (80 GB frei), weit unter 85 %.

Abweichungen (Task 6)

  1. Abbildnamen im Abschlusslauf: siehe oben, Lauf mit api:p0m-final/web:p0m-final statt tessera-ctl-*:latest (Plan-Gate verlangt trivy-image-api/-web; kein Skripteingriff noetig).
  2. Zusatz: CLAUDE.md-Aktualisierung (Orchestrator-Auftrag) als eigener Commit bc7fffe.
  3. Protokoll: Die Vorher/Nachher-Tabelle steht im Abschnitt "Stand nach der Behebung"; der Verlaufseintrag mit dem vom Plan verlangten Titel ist eine Kurzfassung mit Verweis (kein doppelt gepflegtes Zahlenwerk).
  4. Zwei Zeilen in "Einordnung der Befunde" ergaenzt (Desktop-Hinweise, Testwerkzeuge): sie fehlten bisher, obwohl osv-scanner sie zaehlt (Plan: jede Zahl braucht Stand und Grund).

Gesamtergebnis: vorher / nachher

Werkzeug Grundlinie (roh) nach Ausnahmelisten (Task 1) nach Behebung (Task 6)
gitleaks (Historie) 20 (0 echt) 0 0
pnpm audit --prod C5 H73 M68 L5 (151) unveraendert C0 H9 M3 L0 (12)
pnpm audit (alle) C7 H85 M74 L5 (171) -- C2 H9 M5 L0 (16) (Task 4)
osv-scanner 43 (nur pnpm-lock) 56 (mit Cargo.lock) 20
Semgrep 58 / 59 Scanfehler 34 (ERROR 2) / 4 33 (ERROR 1) / 4; GCM-Regel 0
Trivy fs C5 H73 M68 L5, secrets 7, misconfig 2 C5 H73 M70 L5, secrets 0 C0 H9 M4 L0, secrets 0, misconfig 2
Abbild api C6 H116 M98 L7, 1,66 GB C6 H116 M99 L7 C0 H6 M4 L0, 1,12 GB
Abbild web C2 H19 M21 L1, 359 MB C2 H19 M22 L1 C0 H3 M1 L0, 359 MB
ZAP alpha (direkt, ohne Proxy) -- -- 0 / 2 / 6 / 3 (Task 3, vor den Kopfzeilen; nicht neu gemessen)

Offene Punkte (alle im Protokoll mit Grund)

  1. xlsx 0.18.5, node-forge 1.4.0: keine bereinigte Fassung (Ersatz von xlsx waere ein eigener Auftrag).
  2. nodemailer (2 H, 3 M), sharp (3 H), deepmerge-ts (1 H): Korrektur nur mit neuer Haupt-/Null-Minor-Version (nodemailer 10 ist die groesste Restposition).
  3. vitest/@vitest/mocker 3.2.6, tinypool 1.1.1 (API-Tests): nur Entwicklung, Korrektur braucht vitest 4 in apps/api (nicht im Abbild).
  4. Desktop: glib 0.18.5 plus 11 weitere Rust-Hinweise; anyhow 1.0.103, event-listener 5.4.2, h2 0.4.16 sind Patch-Updates in der Versionslinie (cargo update -p ... --precise) und koennten als kleiner Folgeauftrag eingespielt werden (waere ein Cargo.lock-Commit).
  5. Web-Abbild ohne Gesundheitspruefung (weder Dockerfile noch Compose).
  6. Vollstaendige Inhaltsrichtlinie (CSP): bewusst nicht gesetzt, eigener Auftrag.
  7. Pipeline-Haertung: 14 bewegliche Aktions-Tags, ein curl|sh, drei pnpm-Arbeitsbereichsschluessel (niedrig).
  8. Ausstehender oeffentlicher ZAP-Lauf (siehe unten).
  9. Nicht gebaut: woechentlicher geplanter Lauf der Pruefung (Forschung: jetzt nicht noetig); Anheben der Aktions-Tags.

Ausstehender oeffentlicher ZAP-Lauf

Der erste Lauf ging direkt gegen die Anwendung auf alpha, weil die oeffentliche Adresse vom Dev-Host nicht erreichbar war (Zeitueberschreitung Port 443). Nach dem naechsten alpha-Pull (mit den Kopfzeilen aus fceec19): sh .gitea/scripts/zap-baseline.sh (Standardziel = oeffentliche Adresse; falls Basic Auth 401 liefert: ZAP_BASIC_AUTH_FILE mit benutzer:passwort ausserhalb des Repos). Danach im Protokoll: neue Zeile in "Pruefung von aussen gegen alpha", Verlaufseintrag, und die drei Zeilen "behoben in der Anwendung, beim oeffentlichen Lauf erneut pruefen" sowie HTTPS/Proxy auf ihren Endstand setzen.

Checkliste erster CI-Lauf nach dem Push (Nutzer/Orchestrator)

  • Job security erscheint nach publish (nur main und Tags v*), publish und Freigaben unveraendert.
  • Jobfarbe: gelb/gruen trotz Funden (Annahme A3: continue-on-error auf Job-Ebene), kein anderer Job haengt daran.
  • Im Lauf-Protokoll stehen alle SECURITY-SUMMARY-Zeilen (gitleaks, pnpm-audit-prod, osv-scanner, semgrep, trivy-fs, trivy-image-api, trivy-image-web, fertig); Zahlen etwa wie oben (Abbild-Zahlen mit Registry-Abbildern von publish).
  • Artefakt sicherheitsberichte herunterladbar? (Annahme A4, upload-artifact@v3; best effort, Ausfall ist kein Fehler.)
  • Zweiter Lauf: Werkzeuge aus dem act-toolcache-Volume wiederverwendet (Zeile "bereit (vorhanden)"; Annahme A2; lokal mit Volume bestaetigt).
  • Dauer des Jobs (Limit 30 min) und Plattenstand des Runners.
  • Der Desktop-Job baut die Pakete neu, weil Cargo.lock sich geaendert hat (erwartet).

Hinweise an den Orchestrator

  • CHANGELOG: "Sicherheit:"-Bullets liegen innerhalb der bestehenden Gruppen Neu/Geaendert/Behoben (D-21: die "Was ist neu"-Anzeige liest nur diese drei).
  • Entfernte Pakete: @nestjs-modules/mailer, ews-javascript-api (D-22); keine neuen Paketnamen, 232 entfallen.
  • Overrides (15) und Gruende stehen in Task 4; Prisma 6.19.3, Next 15, NestJS 11 unveraendert.
  • Keine Modulversion geaendert, nichts gepusht, nichts deployed.
  • CLAUDE.md-Tabelle ist jetzt aktuell (siehe oben).

Self-Check: PASSED

Gefunden: docs/sicherheitsprotokoll.md, checks/final-run.txt, baseline/SUMMARY.txt (Block "nach Behebung"), Commits bc7fffe und b085889 sind Vorfahren von HEAD, git status ausserhalb von .planning/ sauber; Gate-Sentinel task6 ok.

Review fixes

Review findings WR-01, WR-02, WR-04, WR-05 and IN-01 to IN-04 fixed; commits bf632d9 (scripts, Dockerfiles, next.config comment) and 8a1218a (docs). Not pushed. Details per finding: "Fix status" in 261009-p0m-REVIEW.md.

  • Image start proof is now versioned: bash .gitea/scripts/image-start-check.sh [Abbild] (replaces checks/fresh-db-start.sh, which stays here as history). Rebuilt stack (docker compose up -d --build api web): api healthy; proof printed migrationen=63, frische-datenbank ok.
  • Build fails when it must: broken schema.prisma in a throwaway Dockerfile fails at the explicit prisma generate step (rc 1); a runner stage that keeps npm fails at the absence check. Both throwaway images removed; real Dockerfiles unchanged by the experiment.
  • security-scan.sh in gitea/runner-images:ubuntu-latest (fresh throwaway clone, socket, fresh tool volume, GITHUB_REF=refs/heads/main, both local images): rc 0, 63 s, per-tool lines (gitleaks 0, pnpm-audit-prod critical 0 high 9, osv 20, semgrep 33/4, trivy-fs secrets 0, images api C0 H6, web C0 H3). Also: shallow clone -> unvollstaendig; forced limits -> zeitgrenze, forced tiny budget -> gesamtbudget; offline run rc 0 with 12 skipped lines (Task 1 gate needs >= 7); no semgrep container left behind.
  • ZAP: local passive proof (POST=0, header only to the target, never to a second host or a prefix-port lookalike); special-character password works; handover path for uid != 1000 exercised.
  • Gates: ci.yml node check from PLAN Task 1 prints ci.yml ok (file unchanged); sh -n on all .gitea/scripts/*.sh, bash -n on the new script, shellcheck (via container image, removed afterwards) shows only SC2329 info for trap handlers; link-check 12 links, 0 broken; protocol table gate 34 rows ok; gitleaks dir on the changed docs/scripts clean.
  • Environment notes: semgrep image (1.5 GB) now stays cached on the dev host and will on the runner host after the first CI run (needed for the digest-pinned scan; documented in docs/ci-cd-setup.md). Disk 57 % after cleanup (docker image prune -f only). No shellcheck installed on the host; it was run from koalaman/shellcheck:stable.
  • Docs updated: docs/sicherheitsprotokoll.md (Verlauf entry "Codeprüfung des Sicherheitsauftrags", table row, sums 9 reviews / 98 findings / 84 fixed, new open row for WR-03), docs/ci-cd-setup.md, docs/anleitung-entwicklung.md, docs/anleitung-betrieb.md.

Checklist after the first real CI run (WR-03)

  • Job security ends green or neutral, not red (red would mean the Gitea version ignores job-level continue-on-error).
  • timeout-minutes: 30 is honoured; first run duration (semgrep image pull of about 1.5 GB and Trivy DB included) stays below 25 minutes; the SECURITY-SUMMARY dauer=... line shows it.
  • The next push's quality/test is not noticeably delayed by the single runner; if it is, move the scan to a nightly schedule plus tags v*.
  • SECURITY-SUMMARY semgrep findings=... errors=... appears (container path works inside the real job container with the host socket) and artifact sicherheitsberichte is best effort.