You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat(identity): implement ADR-0068 unified user-context contract (D1–D4)
Implements the unified EvalUser contract so a predicate authored once
evaluates identically across server-formula, server-RLS, and client.
Identities are modelled as roles (roles: string[]), not booleans.
D1 — EvalUser contract (@objectstack/spec)
- New eval-user.zod.ts: EvalUser schema, canonical roles[], deprecated
singular `role`, derived isPlatformAdmin alias, built-in role
constants/metadata, mapMembershipRole(), createEvalUser() factory.
- formula: buildScope mounts current_user / user / ctx.user / os.user as
the SAME EvalUser object; deprecated `role` scalar kept read-only for
back-compat during migration.
D2 — Built-in roles + normalization
- plugin-security: bootstrap-builtin-roles seeds sys_role rows.
- plugin-auth: customSession emits one canonical roles[]; removes the
`role='admin'` overwrite footgun; platform_admin derived.
- runtime: normalize better-auth owner/admin/member -> org_*; derive
platform_admin from an UNSCOPED admin_full_access grant.
D3 — Role-definition authority
- sys_role gains a `managed_by` provenance field (system/declared/user).
D4 — Role-catalog validation (anti-hallucination)
- formula/validate.ts: closed-enum roleCatalog check rejects unknown
role names referenced in predicates.
Tests: formula 206, plugin-auth 120, plugin-security 143, runtime 432.
Full `turbo build` 77/77 (incl. all DTS typechecks).
* docs(spec): use canonical roles[] form in app visibility-predicate example
ADR-0068 D4 follow-up. The embedded `describe()` example shipped the
non-canonical, non-catalog pattern `os.user.role == "admin"`; users
copy-paste these. Switch to `'org_admin' in current_user.roles` so the
shipped example matches the canonical contract and passes role-catalog
validation.
* chore(spec): regenerate api-surface snapshot for ADR-0068 exports
The TypeScript Type Check CI job runs check:api-surface, which flagged
26 additive exports (EvalUser contract, built-in role constants/metadata,
createEvalUser/mapMembershipRole) with 0 breaking. Regenerate the
committed snapshot so the guard passes.
* fix(formula): harden D4 role-exists regex against ReDoS
CodeQL flagged js/polynomial-redos (high) on ROLE_EXISTS_RE: the
unbounded greedy [^,]* + lazy [^)]*? scanners backtrack O(n^2) on
adversarial input like repeated 'user.roles.exists('. Bound both
quantifiers ({0,64}/{0,128} — CEL exists bodies are tiny) and exclude
'=' from the pre-operator class so the run is linear and unambiguous.
Capture groups unchanged; 206 formula tests pass; a 720k-char
pathological probe now returns in ~14ms.
---------
Co-authored-by: zhuangjianguo <zhuangjianguo@steedos.com>
0 commit comments