Quick task 260811-f9i: plan and summary of the fix, STATE.md row, and the UAT test 2 result. Test 2 was the read-only A2 check against the real directory -- it turned assumption A2 from "unverified" into "false as implemented" and surfaced a defect that would have deleted every AD-bound group on the first real sync. Also records why the defect survived review: the spec mocks built their expected filter with the same escape helper the production code used, so the test asserted self-consistency rather than directory behaviour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3.1 KiB
quick_id, slug, date, status, relates_to, severity, commits
| quick_id | slug | date | status | relates_to | severity | commits | |||
|---|---|---|---|---|---|---|---|---|---|
| 260811-f9i | fix-objectguid-existence-sweep-to-use-eq | 2026-08-11 | complete | 16-ad-gruppen-synchronisation | critical |
|
Summary: objectGUID-Existenzprüfung auf Rohbytes umgestellt
Was gemacht wurde
syncBoundGroupsForTenant() baut den Filter der Existenzprüfung jetzt als
EqualityFilter über den rohen 16-Byte-Buffer statt als interpolierten
(objectGUID=\xx…)-String. Beide Suchen — die schmale über die konfigurierten
Base-DNs und die weite über die domainRoots (WR-03-Absicherung) — teilen sich
denselben Filterwert, damit die Absicherung nicht erneut einen kaputten Filter
erben kann.
escapeLdapFilterBuffer() ist entfernt. An seiner Stelle steht ein Kommentar,
der erklärt, warum die Methode nicht zurückkommen darf.
escapeLdapFilterValue() (String-Werte nach RFC 4515) bleibt unverändert und
weiter in Gebrauch.
Wie der Fehler gefunden wurde
Über UAT-Test 2 aus Phase 16 — read-only gegen das echte AD (balios.ctl.local),
Sonde CN=Domain Admins,CN=Users,DC=ctl,DC=local, GUID
1e4b4df0b3caeb4c9b49fddf05227e10:
| Variante | Treffer |
|---|---|
(objectGUID=\1e\4b…) — bisheriger Code |
0 |
(objectGUID=\1E\4B…) — Großbuchstaben |
0 |
EqualityFilter mit rohem Buffer |
1, korrekte DN |
EqualityFilter mit escaptem String als Wert |
0 |
Kontrolle (cn=Domain Admins) |
1 |
Der Domain Controller war also nie das Problem. Annahme A2 aus 16-RESEARCH.md
galt für die gewählte Umsetzung nicht.
Warum es durch Review und Tests gekommen ist
Die Mocks in ldap.service.spec.ts haben den erwarteten Filter mit derselben
escapeLdapFilterBuffer()-Funktion gebaut, die der Produktionscode benutzt hat.
Damit war der Test tautologisch: er hat bestätigt, dass die Funktion sich selbst
gegenüber konsistent ist, nicht dass ein Verzeichnis den Filter versteht. Der
Code-Kommentar hat die Annahme sogar ausdrücklich als [ASSUMED — nicht gegen ein echtes AD verifiziert] markiert.
Die neuen Tests prüfen jetzt die Form des Aufrufs statt seines Inhalts: der
Filter muss ein Objekt mit attribute: 'objectGUID' und einem Buffer-Wert
sein, und kein Suchaufruf des Laufs darf einen String-Filter mit objectGUID=
tragen.
Verifikation
pnpm --filter @tessera/api test— 568 Tests grün (40 Dateien)npx tsc --noEmit— sauber- Gegenprobe: der alte String-Filter kurzzeitig wiederhergestellt → 8 Tests rot, darunter beide neuen. Die Regressionstests haben also Zähne.
Was offen bleibt
Der Fix belegt die Filtermechanik gegen ein echtes AD, nicht das Verhalten eines
vollständigen Sync-Laufs. Offen bleiben aus 16-UAT.md:
- Test 1 (A1: GUID überlebt eine Umbenennung) — braucht Schreibrechte im AD
- Test 3 (Umbenennung end-to-end) und Test 4 (Löschung end-to-end)
- Der Teil von Test 7, der die Zahlenzeilen eines erfolgreichen Sync-Laufs prüft
Nächster Schritt laut Absprache: ein Wegwerf-Domänencontroller im Container, gegen den Umbenennen und Löschen vollständig durchgespielt werden können.