Skip to content

finding: sys_audit_log's shipped record_views list view has an ip_address column that a read row can never populate #9865

Description

@os-steve

Observation-class finding, measured while verifying the record-view auditing docs page against the implementation (#9540 / PR #9860). Cheap to lose, and it is exactly the kind of edge a compliance evaluation trips over.

Measurement

The record_views list view — the shipped surface for the read action, reachable in the Setup app under Diagnostics via the Audit Logs entry — declares five columns (packages/plugins/plugin-audit/src/objects/sys-audit-log.object.ts:85):

columns: ['created_at','user_id','object_name','record_id','ip_address'],filter: [{field: 'action',operator: 'in',value: ['read']}],

The record-view writer's buildRow (read-audit.ts) stamps action, created_at, user_id, object_name, record_id, old_value, new_value, tenant_id, and conditionally organization_id and actor. It never stamps ip_address, and ReadAuditEvent carries no field that could — the hook reads session.isSystem, session.userId, session.actor and session.organizationId only.

grep -rn "ip_address" packages/plugins/plugin-audit/src/*.ts | grep -v test# packages/plugins/plugin-audit/src/auth-event-audit.ts:249: ip_address: event.ipAddress ?? null,

ip_address is stamped only on login / logout rows. Because this view filters to action: 'read', the column is empty for every row it can ever show.

Why it might matter, stated narrowly

Nothing is false and nothing breaks: an empty column renders empty. The cost is that the one shipped screen for "who viewed this record" presents an IP column, and an evaluator reading that screen concludes viewer IP is on the trail. It is not, and the docs page in PR #9860 says so positively ("No IP address and no user agent") rather than describing the column — so the page and the screen currently point different directions.

Two honest dispositions

  • Drop ip_address from the record_views columns. One-line change, makes the screen agree with what the writer produces. Cheapest, and correct if viewer IP is deliberately out of scope for views.
  • Stamp it. The read path has a session; whether it carries a request IP at afterFind time needs checking, and the audit: no read/view audit — "who viewed which record" is unanswerable, blocking regulated-industry deals #8992 ruling's MVP scope pins were deliberate, so widening the row is a decision rather than a fix. Note the module header's argument for recording as little as possible about a view.

⛔ Deliberately not fixed inside PR #9860 — that is a docs card, and this is a metadata change with a scope question attached.

Refs: #9540 / PR #9860 (where it was measured) · #8992 / PR #9515 (the capability and its MVP scope ruling).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions