Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NV9oijZukGyGbRQGGKnK4P Assistant: claude-code Assistant-Model: opus Assistant-Process: 713576@bnt-lap001 Assistant-Session: 384c511d-9bce-4cb8-a676-2aef6c0c8df6
4.3 KiB
| id | type | title | domain | repo | status | owner | topic_slug | created | updated | state_hub_workstream_id |
|---|---|---|---|---|---|---|---|---|---|---|
| KEY-WP-0021 | workplan | Make snapshot attribute validation enforce a real rule | infotech | key-cape | finished | claude | snapshot-attribute-validation | 2026-09-07 | 2026-09-07 | 733ad1a5-c4d8-5c2f-8608-c51bf1d5b17e |
Closes gap G06 of history/2026-09-05-011726-scope-intent-assessment.md:
checkNoUnknownAttributes is named for a rule it does not implement. It rejects
blank keys, which is not an allow-list, so a snapshot carrying arbitrary or
malformed LDAP attribute names passes a check whose name says otherwise. A rule
that passes for the wrong reason is worse than an absent one, because a report
listing no_unknown_attributes: passed is read as evidence.
One part of the assessment's description needs correcting rather than
implementing: it says group membership validation checks nonempty member IDs
rather than complete referenced identity existence. checkValidGroupMemberships
does only check emptiness, but the semantic rule checkReferencedUsersExist
already resolves every member against the user set and fails on an unknown one.
The reference constraint exists; it lives in a different rule than the one the
assessment looked at.
Enforce real attribute constraints
id: KEY-WP-0021-T01
status: done
priority: medium
state_hub_task_id: "e2921983-853c-501b-8dfc-158751dea093"
ldapAttributes is defined by the canonical model as raw attributes not covered
by the model, so an allow-list of permitted names cannot be derived from the
schema — the field exists precisely to carry what the schema does not name. Two
constraints are derivable and worth enforcing:
- the attribute name must be a syntactically valid LDAP attribute descriptor (RFC 4512: a letter followed by letters, digits or hyphens, or a numeric OID), since a name that cannot be written to a directory cannot round-trip; and
- it must not shadow a canonical field's documented LDAP mapping (
uid,cn,mail), because two sources for one value is how an export and a directory come to disagree, and the model already owns those.
Attribute descriptors are case-insensitive in LDAP, so keys differing only by case are a collision, not two attributes. Keep the blank-key check. Rename the rule to describe what it actually enforces rather than leaving a name that overpromises.
Implemented as raw_attributes_well_formed, replacing no_unknown_attributes
in both validator.go and spec/ldap-schema.yaml, with the schema entry stating
plainly that this is not an allow-list and why one cannot be derived.
Prove each rule with an invalid snapshot
id: KEY-WP-0021-T02
status: done
priority: medium
state_hub_task_id: "de4caf0a-39c6-5148-a986-db18a1308505"
The assessment asks for invalid-snapshot cases proving each stated rule. Add one per constraint — malformed descriptor, shadowed canonical mapping, case-only duplicate, blank key — plus passing cases for legitimate raw attributes and a valid numeric OID, so the rule is shown to be selective rather than merely strict.
Eleven malformed-descriptor cases, six canonical-mapping cases across casings, a case-only duplicate, a message-content case, and five accepted-attribute cases. Confirmed they test the rule rather than merely passing: making the check return early fails four of the five tests.
Reconcile the records
id: KEY-WP-0021-T03
status: done
priority: medium
state_hub_task_id: "8a48c6cc-1e7e-5fd4-abfa-2b893c3d400d"
Update SCOPE.md and G06's status. State what the rule now enforces and, just as
importantly, what it still does not: this is not schema-derived validation of
every attribute against a directory schema, and the canonical model cannot
provide one for a field defined as everything the model does not cover. Correct
the assessment's reference-constraint premise rather than restating it.
history/…-scope-intent-assessment.md was being edited by a concurrent session
when this workplan was written; make this edit only against a clean tree.
Done against a clean tree after the other session committed. G06's status records
both corrections: no allow-list is derivable for a field defined as what the
model does not cover, and the reference constraint was already implemented in
checkReferencedUsersExist rather than missing.