Split out of #10734 (MEMBER-filed console defect, moved to objectstack-ai/objectui#5553 for its UI half) during triage. This card is the server-side residual: even after the UI stops rendering its "Raw data (JSON)" panel, the data itself still crosses the wire.
Observation
@objectstack/plugin-approvals writes the submitted record's complete raw row into sys_approval_request.payload_json unconditionally at request-creation time (per the #10734 locator). That snapshot includes internal columns (id, created_by, updated_by, owner_id, organization_id, bare lookup ids) and fields the source object's metadata declares hidden: true.
Any approver who can read the approval request receives the whole blob — the field-visibility machinery (object metadata hidden, FLS) governs columns of objects, and cannot see inside a JSON column of sys_approval_request. So the app author's "these fields are not for users" declaration is structurally unenforceable on the approval path, for every app, today (measured on 17.1.0 by the #10734 reporter's deployment).
Why filed as a finding rather than queued
The right treatment has real design weight and at least three shapes with different audit consequences:
- Trim at write time (snapshot only non-hidden, business-relevant fields) — cheapest, but the approval record then no longer proves what was actually submitted (audit/replay cost);
- Trim at serve time by requester (full snapshot at rest, metadata-driven redaction when a non-admin reads it) — preserves audit, needs a serve-path seam;
- Snapshot an explicit projection declared by the approval process shape — contract-first, but touches
ApprovalNodeConfigSchema ($strict, currently has no such field), i.e. spec surface.
Grading should also answer the standing triage question "who is the producer?" — the producer is plugin-approvals' request-creation path, so the domain is services; option 3 would pull domain:spec in.
Not queued pending first-touch grading; no user-visible fix should ride the objectui#5553 UI card.
Split out of #10734 (MEMBER-filed console defect, moved to objectstack-ai/objectui#5553 for its UI half) during triage. This card is the server-side residual: even after the UI stops rendering its "Raw data (JSON)" panel, the data itself still crosses the wire.
Observation
@objectstack/plugin-approvalswrites the submitted record's complete raw row intosys_approval_request.payload_jsonunconditionally at request-creation time (per the #10734 locator). That snapshot includes internal columns (id,created_by,updated_by,owner_id,organization_id, bare lookup ids) and fields the source object's metadata declareshidden: true.Any approver who can read the approval request receives the whole blob — the field-visibility machinery (object metadata
hidden, FLS) governs columns of objects, and cannot see inside a JSON column ofsys_approval_request. So the app author's "these fields are not for users" declaration is structurally unenforceable on the approval path, for every app, today (measured on 17.1.0 by the #10734 reporter's deployment).Why filed as a finding rather than queued
The right treatment has real design weight and at least three shapes with different audit consequences:
ApprovalNodeConfigSchema($strict, currently has no such field), i.e. spec surface.Grading should also answer the standing triage question "who is the producer?" — the producer is
plugin-approvals' request-creation path, so the domain isservices; option 3 would pulldomain:specin.Not queued pending first-touch grading; no user-visible fix should ride the objectui#5553 UI card.