Files
tessera-ctl/.planning/quick/260811-f9i-fix-objectguid-existence-sweep-to-use-eq/SUMMARY.md
T
schalli f0c763d3e2
Tessera CI/CD / Lint & Type Check (push) Successful in 44s
Tessera CI/CD / Tests (push) Successful in 47s
Tessera CI/CD / Build & Publish Images (push) Successful in 27s
docs(16): record the objectGUID sweep defect found by UAT test 2
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>
2026-08-11 11:06:27 +02:00

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
d2019dc fix(ldap)
search objectGUID by raw bytes, not an escaped filter string

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.