Commit 4035b31
feat(authz): ADR-0066 Phase 1 — secure-by-default object posture + requiredPermissions (D2/D3) (#2235)
* docs(adr): refine ADR-0066 — posture-gated superuser RLS bypass, precedence order, roadmap
- ① Generalize the private RLS exemption into one explainable rule: viewAllRecords
bypasses read RLS and modifyAllRecords bypasses write RLS, gated by the object's
platform-global/private posture (Salesforce View All Data / Dataverse Org level).
Covers the write path too; replaces the narrow "private skips tenant_isolation".
- ② Add an explicit "Precedence / combination semantics" section (AND-gates →
union grants → RLS OR-within/AND-with-global → deny overrides), closing the
ADR's own open edge-case; fix the vague Consequences bullet.
- ③ Author guidance for private (posture) vs requiredPermissions (capability
contract); clarify requiredPermissions is an AND-gate.
- ④–⑦ "Future refinements": deny-by-default target, per-operation
requiredPermissions, capabilities in the CEL surface, permission-set-group muting.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(spec): ADR-0066 D2/D3 — object access posture + requiredPermissions
- access: { default: 'public' | 'private' } — secure-by-default object posture
(ObjectAccessConfigSchema), a data-model posture like tenancy. private opts the
object out of wildcard '*' grants and (via the engine) out of wildcard RLS.
- requiredPermissions: string[] on Object — capability contract (mirrors
App.requiredPermissions), enforced by the engine as an AND-gate.
- Both optional; absent ⇒ public / no gate (no migration for existing objects).
- Tests + full-DTS build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(plugin-security): ADR-0066 D2/D3 — enforce private posture, requiredPermissions, posture-gated RLS bypass
- D2 CRUD: a `private` object is not covered by a non-super-user '*' wildcard
grant (resolveObjectPermission); access needs an explicit per-object grant OR
the View/Modify All Data super-user wildcard. Explicit grants always honored.
- D3 capability AND-gate: object `requiredPermissions` checked before the CRUD
grant (caller's systemPermissions union); missing capability → deny.
- ① posture-gated super-user RLS bypass: on a private/platform-global object,
viewAllRecords bypasses read RLS and modifyAllRecords bypasses write RLS
(incl. pre-image + post-image check) — so a platform admin who is also an org
admin sees all rows, while the bypass never leaks cross-tenant data on
ordinary tenant business objects.
- New getObjectSecurityMeta cache (private/tenancy/requiredPermissions),
invalidated on metadata change. Evaluator: getSystemPermissions,
hasSuperuserRead/WriteBypass; checkObjectPermission/getEffectiveScope take an
isPrivate option (backward-compatible).
- 10 new tests (evaluator units + full-middleware integration). Full DTS green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(spec): classify ADR-0066 object.access + object.requiredPermissions in liveness ledger
ADR-0054 spec-property liveness gate requires every governed-type property to be
classified. Mark both new object props `live`, citing their plugin-security
enforcement sites. Not in BOUND_PROOF_PATHS, so no dogfood proof is required;
unit + full-middleware coverage lives in plugin-security/security-plugin.test.ts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(spec): refresh api-surface snapshot for ObjectAccessConfig (ADR-0066 D2)
Non-breaking: +ObjectAccessConfig (type) +ObjectAccessConfigSchema (const).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(runtime): platform-scoped (null-org) permission grants resolve under an active org
resolveExecutionContext queried sys_user_permission_set with
`organization_id = tenantId`, which DROPPED a platform-scoped (organization_id
IS NULL) grant the moment a user had an active org. A platform admin who also
owns an org therefore silently lost `admin_full_access` (and its
systemPermissions) — and with ADR-0066 that locked them out of a
requiredPermissions-gated control-plane object (sys_license). Mirror the
role-binding logic (null org = global, cross-org): fetch the user's grants and
keep null-org + active-org ones, dropping only grants scoped to a DIFFERENT org.
+2 regression tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>1 parent f2063f3 commit 4035b31
10 files changed
Lines changed: 525 additions & 21 deletions
File tree
- docs/adr
- packages
- plugins/plugin-security/src
- runtime/src/security
- spec
- liveness
- src/data
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
48 | 52 | | |
49 | | - | |
| 53 | + | |
50 | 54 | | |
51 | 55 | | |
52 | 56 | | |
| |||
76 | 80 | | |
77 | 81 | | |
78 | 82 | | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
79 | 92 | | |
80 | 93 | | |
81 | 94 | | |
82 | 95 | | |
83 | 96 | | |
84 | 97 | | |
85 | | - | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
Lines changed: 78 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
29 | 53 | | |
30 | 54 | | |
31 | 55 | | |
| |||
40 | 64 | | |
41 | 65 | | |
42 | 66 | | |
43 | | - | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
44 | 70 | | |
45 | 71 | | |
46 | 72 | | |
| |||
51 | 77 | | |
52 | 78 | | |
53 | 79 | | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
58 | 84 | | |
59 | 85 | | |
60 | 86 | | |
| |||
87 | 113 | | |
88 | 114 | | |
89 | 115 | | |
| 116 | + | |
90 | 117 | | |
91 | 118 | | |
92 | 119 | | |
93 | 120 | | |
94 | 121 | | |
95 | 122 | | |
96 | | - | |
| 123 | + | |
97 | 124 | | |
98 | 125 | | |
99 | 126 | | |
| |||
106 | 133 | | |
107 | 134 | | |
108 | 135 | | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
109 | 181 | | |
110 | 182 | | |
111 | 183 | | |
| |||
0 commit comments