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
{{ message }}
Repository navigation
/api/audit-trails/readable shows other users' draft contents #4666
Found during a Strict review of #4580 (release development → beta). Present on development at 8e7bbd87; introduced in #4566, #4614.
Impact
The lister resolves objects across all magic tables and asks hasPermission('read'), which has no draft rule, so anyone who may read the schema sees the changed values of another user's draft.
The chain
A draft's saves write ordinary create/update audit rows, and their changed column holds every property's old and new value (AuditTrailMapper.php:878-884). GET /api/audit-trails/readable (@NoAdminRequired) decides per row through mayRead() → PermissionHandler::hasPermission(action: 'read', objectOwner, object). hasPermission() has no draft rule (the draft rule lives only in the SQL predicate, MagicRbacHandler::reachableRowSqlFor()). Any user with read rights on the schema therefore reads, in this list, what another user typed into a draft that the object API hides from them ("a draft answers to its owner only", decision 180). WITHHELD_FIELDS removes session, request and ipAddress but keeps changed.
Sibling of the open thread at DraftStatusPolicy.php:295, which has the same root cause (hasPermission() has no draft rule) but is about a different path.
Impact: draft content (possibly personal data a resident has not yet decided to send) is readable by colleagues while the draft lives.
Suggested fix: add the draft rule to PermissionHandler::hasPermission() when an object is passed: refuse read (and every other action) when (new DraftStatusPolicy())->keptFrom($object, $schema, $userId) is true. That one change also closes f2 and the bulk-save thread.
Done when:
a ReadableAuditTrailListerTest case: user B, with read rights on the schema, does not get the audit rows of user A's draft. It goes red without the draft rule
user A still gets them
Verified against 8e7bbd87eede51a19cd16b1eda5f1f9cbe21dfc0.
Reproduction
Verified by reading the code at 8e7bbd87; not executed.
Done when
ReadableAuditTrailListerTest: B (schema read rights) does not receive rows of A's draft; red without the draft rule in hasPermission
Activity
github-actions commentedon Oct 16, 2026
Thank you for opening this issue! It has been assigned to @rubenvdlinde for review.
What happens next:
Triage labels:
triage:accept-feature— accepted as a new featuretriage:accept-bugfix— accepted as a bug fixtriage:needs-info— more information neededtriage:wontfix— will not be addressedtriage:duplicate— duplicate of another issue