Commit Graph

15 Commits

Author SHA1 Message Date
schalli bf5fc4d4c7 feat(quick-260911-cwh): Fehlerrichtung Bereich calendar messen (Aufgabe 1)
- rls-scratch-check.mjs: elfter Abschnitt runCalendarAreaChecks mit 13
  namentlich benannten Pruefungen gegen die aus 20260909140000_rls_remaining_tenant_tables
  geschnittene Regel, davon 4 ueber den generierten Client an einer
  schemagleichen Wegwerf-Tabelle (17 Spalten, gegen schema.prisma
  laufzeitgeprueft); Laufzeitpruefung, dass 20260910120000 keine eigene
  CalendarSource-Regel traegt (Messfalle 260910-jab)
- Alle 101 Pruefungen bestehen (88 bisherige + 13 neue)
- docs/mandantentrennung-etappe2-fehlerrichtung.md: Abschnitt
  "## Bereich calendar" mit (k1)-(k5) — Messung, Signaltabelle je Pfad,
  Leere-als-Abwesenheit in Backend UND Frontend samt Fehlerverschluckung,
  bewusst nicht geloest (Cache-Schluessel-Urteil, Zugangsdaten-Erhaltung,
  fehlende Benutzerdimension, 403/404), bewusst nicht angefasst
- Wettlauf-Fehlerklasse gemessen: PrismaClientKnownRequestError (P2025),
  NICHT PrismaClientUnknownRequestError wie im Bereich dashboard — Aufgabe
  2 braucht deshalb keine neue Fehleruebersetzung fuer die Besitzpruefungen

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
2026-09-11 09:48:44 +02:00
schalli 6e7120648b fix(quick-260910-krx): Konfliktklasse ueber den generierten Client messen statt sie zu behaupten 2026-09-11 09:16:39 +02:00
schalli 6744918a01 feat(quick-260910-krx): Fehlerrichtung fuer Bereich dashboard gemessen
Aufgabe 1 — misst die umgekehrte Fehlerrichtung des Bereichs dashboard an
den Regeln nach Migration 20260910120000, VOR der Umstellung:

- rls-scratch-check.mjs bekommt einen neunten Abschnitt
  (runDashboardAreaChecks) mit 13 neuen, namentlich benannten Pruefungen
  gegen die aus der ausgelieferten Migration geschnittenen Regeln fuer
  DashboardLayout, WidgetInstance und SearchProvider. Alle 87 Pruefungen
  bestehen (74 bisherige + 13 neue).
- Die Konfliktmessung (Befund K) ist gemessen, nicht angenommen: ein
  gebundenes INSERT ... ON CONFLICT auf eine unter dem Mandanten
  unsichtbare Zeile scheitert laut mit SQLSTATE 42501. Zusaetzlich am
  echten generierten Prisma Client gemessen: prisma.dashboardLayout.upsert()
  wirft PrismaClientUnknownRequestError (nicht P2002) — das tenders-Muster
  laesst sich deshalb nicht woertlich uebernehmen.
- Die widerlegte Praemisse zu SearchProvider (WINDOWS #19) ist in diesem
  Durchlauf eigenstaendig nachgeprueft, mit benannter Suchreichweite.
- docs/mandantentrennung-etappe2-fehlerrichtung.md bekommt den Abschnitt
  "## Bereich dashboard" (w1)-(w5) samt der beweisvernichtenden Schleife
  (leeres Dashboard -> Neuaufbau -> automatisches Zurueckschreiben ->
  ueberschriebene Anordnung) und einen Nachtrag im Abschnitt
  "## Bereich module-registry" zur geerbten Bindungsentlastung.

Baseline gehalten: 839 Tests / 56 Dateien gruen, Typpruefung sauber.
Schalter bleibt aus.
2026-09-11 08:46:46 +02:00
schalli f4f3115d5a feat(quick-260910-jab): drei zu kurz greifende RLS-Regeln schliessen (T-JTS-02, T-JTS-03, WINDOWS #19)
- Neue, handgeschriebene Migration 20260910120000_rls_widen_membership_grant_and_platform_read:
  GroupMembership prueft jetzt beide Seiten der Beziehung (Gruppe UND Benutzer),
  ModuleGrant prueft zusaetzlich die referenzierte Gruppe/den referenzierten Benutzer
  (mit Leer-Zulassung, D-04), TenderRssFeedSource bekommt vier nach Befehl getrennte
  Regeln statt einer (Lesen schliesst plattformweite Zeilen ein, Schreiben verlangt
  weiterhin einen Mandanten). SearchProvider bewusst unveraendert (Befund E: Praemisse
  widerlegt). Lokal angewandt und gegen den Systemkatalog der lebenden Datenbank
  gemessen. Der Schalter bleibt aus (Rolle tessera).
- rls-scratch-check.mjs: die drei loch-behauptenden Pruefungen umgekehrt (nicht
  geloescht), Gegenmessungen ueber die Wartungsrolle ergaenzt, vier Befehlsrichtungen
  fuer TenderRssFeedSource gemessen, neuer Abschnitt fuer SearchProvider, Extraktion
  auf die neue Migration umgeleitet und um eine mehrfach-treffer-faehige Form ergaenzt
  (extractAllPolicySql).
- migration-sql.spec.ts: neuer Beschreibungsblock fuer die neue Migration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
2026-09-10 14:29:25 +02:00
schalli 7d45e2fffd test(quick-260910-exd): Fehlerrichtung fuer module-registry messen, kein Produktivcode
- rls-scratch-check.mjs: achter Abschnitt runModuleRegistryAreaChecks mit 13
  benannten Pruefungen gegen die echten, aus den ausgelieferten Migrationen
  geschnittenen Regeln (Group/GroupMembership/ModuleGrant/TenantModuleActivation),
  neu angelegt nur: Modulkatalog-Tabelle ohne Zeilenschutz, Eindeutigkeitsindex
  auf TenantModuleActivation, zwei Direkt-Freigaben, eine fremde Mitgliedschaft
- Alle 66 Pruefungen bestanden (53 bisherige + 13 neue), 810 Tests gruen,
  Typpruefung sauber
- docs/mandantentrennung-etappe2-fehlerrichtung.md: neuer Abschnitt
  "Bereich module-registry" mit den fuenf Unterabschnitten (m1-m5), inklusive
  Praezisierung aus Befund E (Katalogbindung ist HEUTE wirkungslos, nicht
  katastrophal — die Bedingung wird als Bedingung notiert) und der
  unbeschoenigten Antwort auf die Signalfrage ("keines")

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
2026-09-10 11:17:07 +02:00
schalli b848ba6baa feat(quick-260910-das): messen die Kette user unsichtbar->frei->Eindeutigkeitsfehler
- runUserAreaChecks in rls-scratch-check.mjs: 12 neue Pruefungen gegen die
  ausgelieferte User-Policy (baut auf der vom Anmeldeweg-Abschnitt
  angelegten Tabelle auf, legt zusaetzlich Tenant ohne Zeilenschutz an)
- Belegt: ungebundene Suche nach vorhandenem Benutzernamen liefert 0
  Zeilen, gebundene Suche nach fremdem Benutzernamen ebenso ("frei"), und
  das anschliessende gebundene INSERT scheitert hart an SQLSTATE 23505
  (Eindeutigkeitsverletzung), nicht an 42501 (Zeilenschutz)
- SQLSTATE wird aus err.meta.code gelesen, nicht err.code (das bei
  $executeRaw-Fehlern immer den generischen Prisma-Code P2010 traegt,
  empirisch gegen tessera-ctl-db-1 geprueft)
- docs/mandantentrennung-etappe2-fehlerrichtung.md: neuer Abschnitt
  "Bereich user" (u1-u5) mit der tatsaechlich beobachteten Ausgabe,
  Signaltabelle, der vollstaendigen Kette (Befund L) und den Grenzen zu
  auth.service.ts/ldap.service.ts
- Teil 2/3 gemessen: keine Transaktion in apps/api/src/user (Befund B
  haelt), findByUsername hat genau einen Treffer, die eigene Definition
  (Befund D haelt)
- 789 Tests weiterhin gruen, Typpruefung sauber, Wegwerf-Werkzeug meldet
  alle 53 Pruefungen bestanden (41 bisherige + 12 neue)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
2026-09-10 10:16:12 +02:00
schalli 761e5e2c36 feat(quick-260909-mir): dkv-Fehlerform messen und Kritikschrift erweitern
- rls-scratch-check.mjs: runDkvAreaChecks() misst die drei ausgelieferten
  Policies (DkvInvoiceHistory/DkvModuleConfig/DkvVehicleMaster) wortgleich
  aus der Migration, plus die Nebenlaeufigkeitsform von getHistory()
  (Promise.all ueber zwei gebundene Einzelabfragen); alle 9 neuen plus
  32 bestehende Pruefungen bestehen (41 gesamt)
- Belegt Befund H (kein P2002-Fall, Mandant ist Teil des zusammengesetzten
  Schluessels) und Befund I (gebundenes INSERT mit fremder tenantId wird
  ohne eigene WITH-CHECK-Klausel trotzdem abgewiesen) an der echten
  Datenbank statt am Policy-Text
- docs/mandantentrennung-etappe2-fehlerrichtung.md: neuer Abschnitt
  "Bereich dkv" mit der dritten Fehlerform der Etappe (ein Einzelobjekt
  wird null, wo null bereits "nicht eingerichtet" bedeutet), der
  Signaltabelle je umzustellendem Pfad, den sieben Stellen aus Befund K
  (zerstoerend/lautlos/irrefuehrend) und der ausgeschriebenen
  Planer-Entscheidung (Form c, mit Unsymmetrie zum ldap-Praezedenzfall)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
2026-09-09 16:38:57 +02:00
schalli 349814747e feat(laa-01): messe die drei Sonderfaelle des Bereichs tenders und schreibe die Fehlerrichtung
rls-scratch-check.mjs bekommt einen fuenften Abschnitt (runTendersAreaChecks)
mit den fuenf Policies von TenderEmailConfig/TenderNotificationPref/
TenderRssFeedSource/TenderSavedSearch/TenderTriage, wortgleich aus der
ausgelieferten Migration extrahiert. Neun neue Pruefungen belegen: die
Policies haben keine Benutzerdimension (Befund E), eine plattformweite
RSS-Zeile ist unter jedem Mandantenkontext unsichtbar und ein gebundenes
Einfuegen ohne Mandant wird abgewiesen (WINDOWS #19), und ein gebundenes
upsert auf eine unsichtbare Zeile verletzt die Eindeutigkeitsbedingung,
nicht die Policy (Befund F). Alle 32 Pruefungen (23 bisherige + 9 neue)
bestehen. Nachmessung bestaetigt Befund A: genau eine $transaction in
diesem Bereich, Array-Form auf der plattformweiten Tabelle Tender,
ausserhalb jeder Mandantenbindung — withTenantTransaction() wird hier
nicht gebraucht.

docs/mandantentrennung-etappe2-fehlerrichtung.md bekommt einen
`## Bereich tenders`-Abschnitt mit der beobachteten Messausgabe, einer
Signaltabelle je umzustellendem Pfad und einem eigenen Unterabschnitt zur
lautlosen Fehlerform der beiden Hintergrunddienste (fuenf benannte
Stellen, keine protokolliert etwas).

743 Tests gruen, Typpruefung sauber, kein Schema-/Migrations-/Compose-/
Umgebungsdatei-Diff.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
2026-09-09 15:43:35 +02:00
schalli 604428a91d fix(quick-260909-jts): Lastprobe nachreichen statt sie zu behaupten 2026-09-09 15:18:55 +02:00
schalli fd0b9f7d21 feat(jts-01): messen statt annehmen — groups-Policies und Transaktionsform vor jedem Dienstcode
rls-scratch-check.mjs bekommt einen vierten Abschnitt (Group/GroupMembership/
ModuleGrant/TenantModuleActivation, Policies woertlich aus den ausgelieferten
Migrationen) sowie eine eigene Messung, welche der drei Transaktionsformen
(Array auf gebundenem Client, interaktiv auf gebundenem Client, interaktiv auf
ungebundenem Client mit set_config auf tx) den Mandantenkontext tatsaechlich
auf derselben Verbindung traegt. Ergebnis: Form (i) versagt nachweisbar
(unterschiedliche pg_backend_pid() je Teilschritt); Form (ii) und (iii)
bestehen die Einzelmessung, aber eine zusaetzliche Lastprobe mit 40 parallelen
Aufrufen zeigt, dass Form (ii) unter echter Nebenlaeufigkeit mit P2028
(Transaction API error) abbricht, waehrend Form (iii) 0 Verletzungen zeigt.
Die Kritikschrift bekommt einen eigenen groups-Abschnitt mit den tatsaechlich
beobachteten Werten, der Signaltabelle je Pfad und der Liste der Stellen, die
Leere als Abwesenheit deuten (inkl. der einen Stelle, an der zu wenig Lesen zu
viel Schreiben ausloest). Kein Dienstcode angefasst; 719 Tests und die
Typpruefung bleiben gruen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
2026-09-09 14:48:04 +02:00
schalli a0c9ef070f feat(quick-260909-ipc): Fehlerrichtung des Bereichs ldap messen und schriftlich festhalten
Aufgabe 1 der Etappe 2: erweitert das Wegwerf-Werkzeug rls-scratch-check.mjs
um fuenf Messungen des forTenant()-Musters gegen die echte, aus der
ausgelieferten Migration geschnittene LdapConfig/LdapFieldMapping-Policy
unter einer Rolle ohne BYPASSRLS. Belegt insbesondere, dass ein ungebundener
Zugriff nach dem Scharfschalten 0 Zeilen liefert, nicht alle -- die
Fehlerrichtung dreht sich um. Die neue Kritikschrift
docs/mandantentrennung-etappe2-fehlerrichtung.md haelt das schriftlich fest,
mit Signaltabelle je Pfad und den vier Stellen, die Leere als Abwesenheit
deuten. Kein Dienstcode angefasst.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
2026-09-09 13:53:56 +02:00
schalli de50297467 feat(quick-260909-eor): schmale SECURITY-DEFINER-Ausnahme fuer den Anmeldeweg
WINDOWS #18/#20, Aufgabe 2: der Anmeldeweg muss den passenden Benutzer
finden, bevor sein Mandant bekannt ist — unter der kuenftigen Rolle ohne
BYPASSRLS (tessera_app) wuerde ein gewoehnlicher SELECT auf "User" sonst
null Zeilen liefern und die Anmeldung waere unmoeglich.

Drei SECURITY-DEFINER-Funktionen (STABLE, fester Suchpfad public/pg_temp,
fester Spaltensatz, LIMIT 1, Ausfuehrungsrecht ausschliesslich fuer
tessera_app) ersetzen die drei pre-tenant Lesezugriffe in auth.service.ts:

- auth_lookup_user_by_username (validateUser)
- auth_lookup_user_by_email (requestPasswordReset)
- auth_lookup_reset_token (resetPassword)

Sobald der Benutzer und damit sein Mandant bekannt sind, laufen alle
Schreibzugriffe (lastLoginAt, passwordHash, Reset-Token) ueber forTenant(),
gebunden an genau diesen Mandanten (Aufgabe 1). getMe/changePassword/
adminResetPassword bleiben bewusst unangetastet — sie kennen den Mandanten
bereits aus dem Sitzungsnachweis und gehoeren in Etappe 2.

rls-scratch-check.mjs um einen zweiten Abschnitt erweitert: spielt die
Migration in die Wegwerf-Datenbank ein und misst live unter der Rolle ohne
BYPASSRLS — Anmeldesuche findet den Benutzer, unbekannter Name liefert
nichts ohne zu werfen, gewoehnlicher SELECT auf "User" liefert null Zeilen.
Alle 8 Pruefungen (5 aus Aufgabe 1 + 3 neue) bestehen gegen die lokale
Datenbank. Volle Testsuite (695 Tests) und type-check bleiben gruen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
2026-09-09 10:56:41 +02:00
schalli bbf179503c fix(quick-260909-eor): forTenant() bindet Mandantenkontext auf dieselbe Verbindung
WINDOWS #20: set_config() lief auf einer anderen Postgres-Verbindung als die
eigentliche Abfrage, weil die interaktive Callback-Form von $transaction
verwendet wurde. Ersetzt durch die Array-Form, die set_config und Abfrage als
eine Transaktion auf einer Verbindung ausfuehrt (Prismas empfohlenes Muster
fuer RLS-ueber-Extensions). Injektionsfestigkeit (T-02-05) bleibt ueber ein
getaggtes $executeRaw-Template statt $executeRawUnsafe erhalten.

- prisma-tenant.extension.spec.ts: prueft die Form des Aufrufs (Array mit
  zwei Eintraegen, Rueckgabewert ist der zweite Eintrag) ohne laufende
  Datenbank
- rls-scratch-check.mjs: neues Werkzeug, das eine Wegwerf-Datenbank anlegt
  und live misst — gleiche Backend-Verbindung, gesetzter Kontext, keine
  Fremdmandanten-Zeilen, keine Zeilen ohne Kontext. Alle 5 Pruefungen
  bestehen gegen die lokale Datenbank.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
2026-09-09 10:50:37 +02:00
schalli 44a90e5bfd feat(quick-260909-dgj): Pruefwerkzeug fuer den Nachweis plus Betriebsanleitung (Task 3/4)
- apps/api/scripts/rls-preflight.mjs misst fuenf benannte Eigenschaften
  (rollenrechte, kontext-setzbar, ohne-kontext-leer, mit-kontext-sichtbar,
  schreibrechte) jeweils in einer eigenen Transaktion gegen eine per
  TESSERA_PREFLIGHT_DATABASE_URL angegebene Verbindung; --print-plan
  verbindet nicht, das Werkzeug schreibt in keiner Betriebsart
- 5 Tests in rls-preflight.spec.ts (rot vor dem Werkzeug, jetzt gruen)
- docs/mandantentrennung-datenbankrolle.md: Befund, Sperrgrund (182
  unskalierte Zugriffe, Anmeldeweg), Handgriffe des Betreibers samt
  Kennwortsetzung, Freigabebedingung und Rueckweg; docs/README.md verweist
  darauf

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
2026-09-09 10:03:25 +02:00
schalli a5f99e52e3 feat(quick-260909-dgj): Migrationsverbindung von Laufzeitverbindung trennen (Task 2/4)
- apps/api/scripts/migrate-and-start.sh: TESSERA_MIGRATE_DATABASE_URL fuer
  den Migrationsschritt, DATABASE_URL bleibt unveraendert fuer den
  Laufzeitschritt; leer/nicht gesetzt = bisheriges Verhalten
- Dockerfile kopiert scripts/ und ruft das Skript als CMD auf; exec statt
  &&-Verkettung, damit Signale den Node-Prozess erreichen
- docker-compose.yml/.prod.yml reichen TESSERA_MIGRATE_DATABASE_URL durch
  (leerer Vorgabewert); docker-compose.dev.yml/.ci.yml unveraendert
- .env.example erklaert beide Variablen mit Platzhaltern, Umstellung bleibt
  auskommentiert
- 5 Tests in start-script.spec.ts (rot vor dem Skript, jetzt gruen)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
2026-09-09 10:00:45 +02:00