Skip to content

/api/audit-trails/readable shows other users' draft contents #4666

Open

Description

@rjzondervan

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
  • A still receives them

Review thread

#4580 (comment)

Activity

  1. github-actions commented on Oct 16, 2026

    @github-actions
    Contributor

    Thank you for opening this issue! It has been assigned to @rubenvdlinde for review.

    What happens next:

    1. A maintainer will review this issue and determine the appropriate action
    2. If accepted, it may be converted into an OpenSpec change for structured development
    3. You will be notified of any status changes

    Triage labels:

    • triage:accept-feature — accepted as a new feature
    • triage:accept-bugfix — accepted as a bug fix
    • triage:needs-info — more information needed
    • triage:wontfix — will not be addressed
    • triage:duplicate — duplicate of another issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingtriageAwaiting triage

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions