Commit d2a8695
* fix(approvals)!: scope approval requests to their participants, not the tenant (#3590)
getRequest / listRequests / countRequests deliberately query with SYSTEM_CTX to
bypass RLS — as their own comments say, the approver-visibility rule spans
identity forms RLS cannot model cleanly, so it has to be expressed in the
service. Only the TENANT half of that rule was ever written. The comment names
the participant half; the code never applied it. So any authenticated user could
read any approval request in their tenant: its payload snapshot, its full
decision history, and — once decision attachments derived their access from the
request (#3580) — its files.
`approverId` on listRequests is a FILTER, not authorization. Omitting it
returned the whole tenant.
A caller now sees a request when they participate in it: the submitter, a
current approver (via the normalized approver index, so every identity form the
write path recorded is covered), or someone who already acted on it — a past
approver whose slot has moved on, a commenter. Override admins keep the
unrestricted view the "all requests" console surface depends on; a tokenless
context sees nothing.
Keying on the concrete user id is sufficient rather than an approximation, and
that is worth stating because under-granting here would silently hide approvals
someone must act on: position/team/manager/field approvers are resolved to
concrete user ids at open time, and the `type:value` literal is only the
fallback for a spec that resolved to NOBODY — a slot no one can act on either
way, since can_act is a plain membership test over the resolved ids.
A write path's own result is NOT re-gated. Every operation echoes back the
request it just changed via getRequest; the operation authorized itself before
writing, and re-asking answers a settled question — wrongly, for a context that
carries no userId (a flow-driven resume, a service-to-service call), turning a
successful write into a null result. Four existing tests caught exactly that,
which is why the echo now goes through readBackRequest instead.
Verified in the browser on app-showcase: a non-admin approver's unfiltered list
drops from 3 requests to the 1 she is on, an admin still sees all 3, her
decision attachment still downloads (200, real PDF bytes — #3580 composes), and
a request she has nothing to do with reads back empty.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfd
* docs(approvals): state who can see a request, now that it is participant-scoped
The drift check flagged approvals.mdx, and rightly: it documented `approverId`
as one filter among several and never said who may see what — so a reader would
reasonably assume omitting it returns the whole tenant, which is exactly what
this PR stops being true.
Also records that a decision's attachments inherit the same rule, since that is
the non-obvious half: the file is as readable as the decision, never more.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfd
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3d5f726 commit d2a8695
4 files changed
Lines changed: 310 additions & 14 deletions
File tree
- .changeset
- content/docs/automation
- packages/plugins/plugin-approvals/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
244 | 244 | | |
245 | 245 | | |
246 | 246 | | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
247 | 261 | | |
248 | 262 | | |
249 | 263 | | |
| |||
Lines changed: 114 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
740 | 740 | | |
741 | 741 | | |
742 | 742 | | |
743 | | - | |
744 | | - | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
745 | 747 | | |
746 | 748 | | |
747 | 749 | | |
| |||
1477 | 1479 | | |
1478 | 1480 | | |
1479 | 1481 | | |
1480 | | - | |
1481 | | - | |
| 1482 | + | |
| 1483 | + | |
| 1484 | + | |
| 1485 | + | |
| 1486 | + | |
1482 | 1487 | | |
1483 | 1488 | | |
1484 | 1489 | | |
1485 | 1490 | | |
| 1491 | + | |
| 1492 | + | |
| 1493 | + | |
| 1494 | + | |
| 1495 | + | |
| 1496 | + | |
| 1497 | + | |
1486 | 1498 | | |
1487 | 1499 | | |
1488 | 1500 | | |
| |||
2114 | 2126 | | |
2115 | 2127 | | |
2116 | 2128 | | |
| 2129 | + | |
| 2130 | + | |
| 2131 | + | |
| 2132 | + | |
| 2133 | + | |
| 2134 | + | |
| 2135 | + | |
| 2136 | + | |
| 2137 | + | |
| 2138 | + | |
| 2139 | + | |
| 2140 | + | |
| 2141 | + | |
| 2142 | + | |
| 2143 | + | |
| 2144 | + | |
| 2145 | + | |
| 2146 | + | |
| 2147 | + | |
| 2148 | + | |
| 2149 | + | |
| 2150 | + | |
| 2151 | + | |
| 2152 | + | |
| 2153 | + | |
| 2154 | + | |
| 2155 | + | |
| 2156 | + | |
| 2157 | + | |
| 2158 | + | |
| 2159 | + | |
| 2160 | + | |
| 2161 | + | |
| 2162 | + | |
| 2163 | + | |
| 2164 | + | |
| 2165 | + | |
| 2166 | + | |
| 2167 | + | |
| 2168 | + | |
| 2169 | + | |
| 2170 | + | |
| 2171 | + | |
| 2172 | + | |
| 2173 | + | |
| 2174 | + | |
| 2175 | + | |
| 2176 | + | |
| 2177 | + | |
| 2178 | + | |
| 2179 | + | |
| 2180 | + | |
| 2181 | + | |
| 2182 | + | |
| 2183 | + | |
| 2184 | + | |
| 2185 | + | |
| 2186 | + | |
| 2187 | + | |
| 2188 | + | |
| 2189 | + | |
| 2190 | + | |
| 2191 | + | |
| 2192 | + | |
| 2193 | + | |
| 2194 | + | |
| 2195 | + | |
| 2196 | + | |
| 2197 | + | |
| 2198 | + | |
| 2199 | + | |
| 2200 | + | |
| 2201 | + | |
| 2202 | + | |
| 2203 | + | |
| 2204 | + | |
| 2205 | + | |
| 2206 | + | |
| 2207 | + | |
| 2208 | + | |
| 2209 | + | |
| 2210 | + | |
| 2211 | + | |
| 2212 | + | |
| 2213 | + | |
| 2214 | + | |
| 2215 | + | |
| 2216 | + | |
| 2217 | + | |
| 2218 | + | |
| 2219 | + | |
| 2220 | + | |
| 2221 | + | |
| 2222 | + | |
| 2223 | + | |
| 2224 | + | |
| 2225 | + | |
| 2226 | + | |
0 commit comments