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(security)!: ADR-0090 P3 — security publish linter (D7), delegated administration (D12), BU assignment anchor
D7 — validateSecurityPosture in @objectstack/lint, gating os compile and
reported by os lint. Seven rules, each with a failing fixture: unset OWD on
custom objects (the objectui#2348 leave_request shape), retired OWD aliases
(fix-it), external dial wider than internal (D11), wildcard VAMA outside the
platform admin set (ADR-0066), high-privilege everyone-suggested sets, the
reserved word 'role' in security identifiers/labels (D3), and advisory
private-object-read-without-depth. The linter immediately caught real gaps:
app-crm shipped six objects with no OWD, app-showcase two, plus a 'role'
field on showcase_project_membership and the CLI golden corpus — all fixed
in this commit (grandfather stamps + engagement rename), proving the rule
set against its own repo.
D12 — DelegatedAdminGate in plugin-security. PermissionSetSchema.adminScope
(persisted as sys_permission_set.admin_scope) declares WHERE (BU subtree),
WHAT (manageAssignments / manageBindings / authorEnvironmentSets) and WHICH
sets a delegate may hand out (allowlist). Writes to sys_user_position,
sys_position_permission_set, sys_user_permission_set and sys_permission_set
are now governed: tenant admins (superuser wildcard) pass to ordinary
CRUD/RLS; delegates need a covering scope — subtree-anchored assignments,
allowlisted sets only (to others AND themselves), single-row writes,
granted_by audit stamping; plain CRUD holders with no scope are denied.
Granting/authoring a set that carries an adminScope requires strict
containment. everyone/guest anchors stay tenant-level only, and stored
assignments to an anchor are rejected for every caller.
ADR-0090 Addendum — sys_user_position.business_unit_id lands with its three
consumers: D12 delegation boundary (enforced), audit fact, and the
depth-anchor contract documented on IHierarchyScopeResolver for enterprise
resolvers.
D9 tightening — describeHighPrivilegeBits moved to @objectstack/spec/security
(shared by lint + runtime, re-exported from plugin-security) plus new
describeAnchorForbiddenBits: guest bindings now also reject edit bits.
D3 sweep — SysRole→SysPosition, SysUserRole→SysUserPosition,
SysRolePermissionSet→SysPositionPermissionSet (one-step, no aliases);
sys_position actions/list views/labels de-roled;
sys_business_unit_member.role_in_business_unit→function_in_business_unit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
* fix: stamp todo_task OWD (D7 linter), drop always-true ql conditional (CodeQL)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
---------
Co-authored-by: Claude <noreply@anthropic.com>
**D7 — `validateSecurityPosture` (@objectstack/lint), wired into `os compile` (errors gate the build) and `os lint`.** Rules, each with a failing fixture: `security-owd-unset` (custom object with no `sharingModel` — the objectui#2348 leave_request shape), `security-owd-alias` (retired D4 alias values, with fix-it), `security-external-wider-than-internal` (D11 `external ≤ internal`), `security-wildcard-vama` (`'*'` + View/Modify All outside the platform admin set, ADR-0066), `security-anchor-high-privilege` (an `isDefault`/everyone-suggested set carrying anchor-forbidden bits), `security-role-word` (D3 vocabulary freeze in security identifiers/labels; ARIA/page roles exempt), and advisory `security-private-no-readscope`.
12
+
13
+
**D12 — delegated administration (@objectstack/plugin-security `DelegatedAdminGate`).** `PermissionSetSchema.adminScope` (new in spec, persisted as `sys_permission_set.admin_scope`) declares WHERE (a `sys_business_unit` subtree), WHAT (`manageAssignments` / `manageBindings` / `authorEnvironmentSets`), and WHICH sets a delegate may hand out (`assignablePermissionSets` allowlist). Writes to `sys_user_position`, `sys_position_permission_set`, `sys_user_permission_set`, and `sys_permission_set` are now governed: tenant-level admins (ADR-0066 superuser wildcard) pass through; delegates need a covering scope — inside their subtree, allowlisted sets only (to others AND themselves), single-row writes, `granted_by` audit-stamped; everyone else (including holders of plain CRUD on RBAC tables) is denied. Granting or authoring a set that itself carries an `adminScope` requires a held scope that STRICTLY contains it. The `everyone`/`guest` anchors stay tenant-level only, and direct position assignments to an anchor are rejected for every caller.
14
+
15
+
**ADR-0090 Addendum — assignment-level BU anchor.**`sys_user_position.business_unit_id` lands with its three consumers scoped: D12 delegation boundary (enforced here), audit fact, and the depth-anchor contract for enterprise `hierarchy-scope-resolver` implementations (documented on `IHierarchyScopeResolver`).
16
+
17
+
**D9 tier tightening.**`describeHighPrivilegeBits` moved to `@objectstack/spec/security` (re-exported from plugin-security) alongside new `describeAnchorForbiddenBits`: `guest` bindings now additionally reject edit bits (read-only by default; create stays the case-by-case exception).
18
+
19
+
**BREAKING (@objectstack/plugin-security):** exports renamed to the ADR-0090 D3 vocabulary — `SysRole`→`SysPosition`, `SysUserRole`→`SysUserPosition`, `SysRolePermissionSet`→`SysPositionPermissionSet` (no aliases, pre-launch one-step rename). `sys_position` row actions/list views renamed (`activate_position`, …), labels relabeled Role→Position. Non-tenant-admin writes to the RBAC link tables without an `adminScope` are now denied (previously any CRUD grant on those tables sufficed).
0 commit comments