Verifikation unabhaengig nachgemessen: 651/651 API-Tests, 233/233 Web-Tests,
beide Typpruefungen sauber.
Der Sicherheitsfund T-Q3-01 wurde nicht nur gruen getestet, sondern
falsifiziert: der Verifizierer hat die neue Besitzpruefung testweise
zurueckgebaut, woraufhin der Test fehlschlug und die fremde Adresse
tatsaechlich in prisma.user.update() landete. Danach sauber zurueckgesetzt.
Ebenso gegengeprueft: beide Anlege-Wege (Sync und Einzelimport) nutzen
denselben Kollisionsentscheider, im ausgelieferten Code stehen keine
kundenspezifischen Namen oder Adressen, rohe ORM-Texte erreichen die
Oberflaeche nicht mehr, und die Gruppensuche unter internem wie AD-Namen
(#6c) ist per Regressionstest gesichert.
Eine Abweichung des Ausfuehrenden ist dokumentiert und bestaetigt: die im
Plan vorgesehene Testvorlage war wegen Kurzschlussauswertung schon gegen den
unveraenderten Code gruen; mit einem zweiten, nicht passenden Konto
reproduziert sie den Absturz nun wirklich.
Status human_needed: die fuenf Browser-Pruefungen brauchen einen Neubau und
sind Sache des Users. WINDOWS #14 und #15 bleiben bis dahin offen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
Freigaben-Matrix im Browser nach `Claude_VT` bzw. `Vertrieb` durchsuchen
Gruppenspalte bleibt stehen UND alle Modulzeilen bleiben stehen, es gibt anklickbare Kaestchen
Plan verlangt Browser-Nachpruefung nach Neubau von API/Web (Konvention: kein Docker-Deploy auf Testserver durch den Ausfuehrenden); durch Unit-Tests (grants-matrix.test.tsx, WINDOWS #6c Regressionstest) bereits codeseitig abgesichert
test
expected
why_human
Dieselbe Matrix, Suche nach `Cert`
Modulzeile bleibt stehen UND alle Gruppenspalten bleiben stehen
Browser-Nachpruefung vom Plan explizit dem Nutzer zugewiesen; Unit-Test deckt die Achsen-Logik bereits ab
test
expected
why_human
Dieselbe Matrix, Suchbegriff ohne Treffer
Verstaendliche Meldung statt leerer Tabelle
Browser-Nachpruefung vom Plan explizit dem Nutzer zugewiesen
test
expected
why_human
AD-Sync im Browser ausloesen: uvertrieb_ro, uvertrieb_rw, uvertrieb_ro_ss, usoftware_rw
Alle vier Konten erscheinen in der Benutzerliste — ohne Adresse; das Konto mit mbuntz@ctl.de behaelt seine Adresse unveraendert
Verlangt einen echten Sync-Lauf gegen das reale AD nach Neubau von API/Web — nicht durch Unit-Tests mit Mock-AD abbildbar; Verhalten ist codeseitig durch resolveEmailForWrite() und fuenf Testfaelle in ldap.service.spec.ts abgesichert
test
expected
why_human
Derselbe Sync-Bericht im Browser pruefen
Keine Zeile enthaelt englischen Techniktext oder Datenbankwortlaut; die sechs Kontakte ohne Anmeldenamen stehen als neutraler Hinweis, nicht in Rot
Visuelle/Text-Pruefung der gerenderten Oberflaeche nach echtem Sync-Lauf; Backend-Seite (kein roher ORM-Text im Bericht) ist durch Test 5 aus Task 2 bereits programmatisch bewiesen
Quick Task 260909-ab3: Matrix-Suche und Sync-Meldungen reparieren — Verification Report
Task Goal: Matrix-Suche und Sync-Meldungen reparieren (WINDOWS #14 und #15)
Verified: 2026-09-09T08:05:00Z
Status: human_needed
Re-verification: No — initial verification
Goal Achievement
Observable Truths
#
Truth
Status
Evidence
1
Freigaben-Matrix: ein Suchbegriff, der nur einen GRUPPENNAMEN trifft, laesst ALLE Modulzeilen stehen
✓ VERIFIED
page.tsx:157-158 returns filteredModules: modules (unfiltered) when groupHits.length > 0 && moduleHits.length === 0; test 'a group-name search leaves all module rows standing (WINDOWS #14)' (line 231) passes
2
Freigaben-Matrix: ein Suchbegriff, der nur einen MODULNAMEN trifft, laesst ALLE Gruppenspalten stehen
✓ VERIFIED
page.tsx:154-155 returns filteredGroups: groups (unfiltered) when moduleHits.length > 0 && groupHits.length === 0; test 'a module-name search leaves all group columns standing (WINDOWS #14)' (line 208) passes
3
Spaltensuche findet importierte Gruppe unter internem UND AD-Namen (WINDOWS #6c)
✓ VERIFIED
page.tsx:149matches(g.internalName ?? g.name) || matches(g.name); dedicated regression test 'finds an imported group by both internal and AD name (WINDOWS #6c regression)' (line 253) searches both 'Vertrieb' and 'Claude_VT' and passes
4
Kein Treffer weder Modul noch Gruppe -> sichtbare Meldung statt stumm leerer Tabelle
✓ VERIFIED
page.tsx:219-222 renders t('noSearchResults', {search}) when noMatch; test 'shows a visible message when the search term matches neither modules nor groups' (line 283) asserts message visible and 0 checkboxes
5
AD-Sync: Konto mit bereits belegter Adresse wird angelegt — ohne Adresse; erster Halter behaelt seine Adresse
✓ VERIFIED
resolveEmailForWrite() (ldap.service.ts:416-427) queries findUnique and withholds the address unless !holder || holder.id === ownRecordId; wired into BOTH upsertMappedUser (create AND update branch) and importUsersByDn. Falsification test performed: reverting the ownership check to unconditionally allow the address makes 'nimmt einem bestehenden Konto seine Adresse nicht weg' fail with the real address leaking into the update() call — confirms the test genuinely exercises the takeover scenario, not just the happy path
6
Kein Bestandteil des Sync-Berichts traegt rohen ORM-/englischen Techniktext
✓ VERIFIED
ldap.service.ts:988-998: catch block puts only entry.dn into result.entryFailures; the raw exception message goes exclusively to this.logger.error(...). Test 'reicht keinen rohen Datenbanktext an den Bericht durch' asserts JSON.stringify(result) does not contain the thrown ORM message — passes
7
Eintraege ohne Anmeldenamen erscheinen als neutraler Hinweis, nicht als Fehler
✓ VERIFIED
ldap.service.ts:965-969 pushes to result.skippedNoLogin (not errors); UI renders this list with text-muted-foreground, explicitly not text-destructive (admin/ldap/page.tsx:1363-1374); test 'meldet Eintraege ohne Anmeldenamen getrennt und nicht als Fehler' passes
8
Benutzer ohne E-Mail-Adresse bricht weder Benutzerliste noch Mitgliedersuche
✓ VERIFIED
GroupMembersModal.tsx:104 guards with (u.email ?? ''); users/page.tsx declares email: string | null, table cell falls back to en-dash. Falsification test performed: reverting the guard to u.email.toLowerCase() makes the two-account fixture genuinely crash with TypeError: Cannot read properties of null at the exact predicted line — confirms the fixture (added as a deviation) really exercises the crash path, addressing the executor's self-reported deviation
18 lines, ALTER TABLE "User" ALTER COLUMN "email" DROP NOT NULL; only, unique index untouched, German header comment documents the locked product decision. Coherent with schema.prisma:32 (email String? @unique). Not applied to any database (only the file was written; no migrate/deploy command was run by this verifier or found evidence of prior application)
303 lines, useMemo truth table matches plan spec exactly (lines 140-162)
apps/api/src/ldap/ldap.service.ts
Shared collision decider wired into both create paths
✓ VERIFIED
1692 lines, resolveEmailForWrite (416-427) used in upsertMappedUser (464, 494) and importUsersByDn (697)
apps/web/src/app/(portal)/admin/ldap/page.tsx
Structured, German-only sync report display
✓ VERIFIED
1403 lines, SyncResult interface extended (79-81), three new report sections (1348-1386) in the plan-specified order and colors
Key Link Verification
From
To
Via
Status
Details
upsertMappedUser()
UserService.create({ email? })
Optional email passthrough
✓ WIRED
user.service.ts:51email?: string; ldap.service.ts:506-513 calls create with ...(createEmail && { email: createEmail }) — spreads nothing when withheld
LdapSyncResult (backend)
interface SyncResult (frontend)
Field-name parity
✓ WIRED
Both sides declare emailConflicts: {account, email}[], skippedNoLogin: string[], entryFailures: string[] with matching names/shapes
de.json
en.json
Key parity via umlaut-guard.spec.ts (3rd test)
✓ WIRED
Ran umlaut-guard.spec.ts independently — 3/3 tests pass, confirming both files flatten to identical key sets
Requirements Coverage
Requirement
Source Plan
Description
Status
Evidence
PERM-02
260909-ab3-PLAN.md
AD group import/sync (Phase 16, already Complete)
✓ SATISFIED
This quick task is a bugfix to already-shipped Phase 16/15 functionality (WINDOWS #14/#15), not new scope; no regression to the underlying requirement introduced (verified: full API+web suites green)
PERM-03
260909-ab3-PLAN.md
Module grants matrix (Phase 15, already Complete)
✓ SATISFIED
Same — matrix search bug fixed without altering the underlying grant-toggle mechanism (T-Q3-06 accepted: search only changes visibility, not server-checked grants)
Anti-Patterns Found
None. Scanned all 13 non-message-catalog files listed in files_modified for TBD/FIXME/XXX/TODO/HACK/PLACEHOLDER/not yet implemented/coming soon — zero hits.
Reverted resolveEmailForWrite to unconditionally allow the address, ran 'nimmt einem bestehenden Konto seine Adresse nicht weg' alone
Test failed exactly as expected (update() called with the foreign address in data); reverted, full suite re-confirmed 67/67 green
✓ PASS
Task-3 deviation (two-account fixture) genuinely exercises the crash
Reverted GroupMembersModal.tsx:104 guard to u.email.toLowerCase(), ran the new test alone
Test failed with TypeError: Cannot read properties of null (reading 'toLowerCase') at the predicted line; reverted, full suite re-confirmed 13/13 green
Only entry.dn pushed to result.entryFailures; raw message only reaches this.logger.error
✓ PASS
Working tree was restored to a clean state after each falsification (git status --short shows only the untracked SUMMARY.md, no residual diffs).
Human Verification Required
Five items are explicitly deferred to the user per the plan's own <verification> section (require a rebuild of API and web images before they can be exercised in a real browser against a real AD/Postgres). These are recorded as human-check items, not gaps — the underlying code paths are independently proven via unit tests and the falsification tests above.
Freigaben-Matrix: Suche nach Claude_VT/Vertrieb — Gruppenspalte und alle Modulzeilen bleiben stehen, anklickbare Kaestchen vorhanden. Why human: Browser-Nachpruefung nach Neubau, laut Projektkonvention "Kein Docker-Deploy auf Testserver durch Claude".
Dieselbe Matrix, Suche nach Cert — Modulzeile und alle Gruppenspalten bleiben stehen. Why human: Gleicher Grund.
Dieselbe Matrix, Begriff ohne Treffer — verstaendliche Meldung statt leerer Tabelle. Why human: Gleicher Grund.
AD-Sync ausloesen — die vier Konten uvertrieb_ro, uvertrieb_rw, uvertrieb_ro_ss, usoftware_rw erscheinen ab jetzt in der Benutzerliste, ohne Adresse; das Konto mit mbuntz@ctl.de behaelt die Adresse. Why human: Braucht einen echten Sync-Lauf gegen das reale AD und eine angewandte Migration — nicht durch Unit-Tests mit Mock-AD abbildbar.
Derselbe Bericht — keine Zeile enthaelt englischen Techniktext oder Datenbankwortlaut, die sechs Kontakte ohne Anmeldenamen stehen als neutraler Hinweis, nicht in Rot. Why human: Visuelle/Text-Pruefung der gerenderten Oberflaeche nach echtem Lauf.
Gaps Summary
No gaps found. All 8 must-have truths, all 4 required artifacts, and all 3 key links are verified against the actual codebase (not merely SUMMARY claims). Two of the plan's own risk points — the T-Q3-01 ownership-check regression risk and the executor's self-reported test-fixture deviation — were independently falsified by this verifier (reverting the fix and confirming the test goes red for the right reason, then restoring cleanly). All four required test/type-check commands were re-run independently from a clean working tree and matched the SUMMARY's reported counts exactly. Migration file exists, is coherent with the schema, and was not applied (confirmed no migrate deploy/migrate dev was executed by this verification). No customer-specific account names or domains leaked into shipped code. The only reason this report is human_needed rather than passed is the five browser-verification items the plan itself explicitly assigns to the user after their next rebuild — these are deferred, not failed.
Verified: 2026-09-09T08:05:00ZVerifier: Claude (gsd-verifier)