Skip to content

highlightFields cannot declare read-only, so the detail page's header chips let users overwrite hook-maintained columns #5077

Description

@yinlianghui

Summary

The header chip strip on a record detail page is built from the object's highlightFields. Those chips are inline-editable and the edits save. There is no way to declare a highlight field read-only, so columns that are exclusively maintained by hooks can be overwritten by anyone who clicks on them.

Why there is no authoring knob

highlightFields: z.ZodOptional<z.ZodArray<z.ZodString>>;
// → highlightFields?: string[] | undefined

A plain array of field names. No per-field options at all — no readonly, no disabled, nothing.

The three things an app can reach for, and why each fails

  1. View-level readonly on the form view's section fields. This works, but only for the form/dialog render path. The detail page's header chips are a different surface and do not consult it. That asymmetry is the reported symptom: the create/edit dialog refuses the edit while the detail page accepts it.

  2. readonly: true on the object field. stripReadonlyFields drops caller-supplied readonly keys for any non-isSystem context. Our columns are written by cross-object hooks, which inherit the acting user's non-isSystem context, so marking them readonly makes the hook's writes silently vanish and the columns never update again.

  3. Field-level editable: false in a permission set. getFieldPermissions has no super-user bypass — modifyAllRecords does not apply at field level, and field-level entries take precedence over the object-level decision. plugin-security's detectForbiddenWrites then throws 403 for non-isSystem writes, which kills the same hook write-back. Turning it off for the admin turns it off for the hook.

So an app that has hook-maintained columns and wants them on the header strip has no legal way to protect them.

Reproduce

  1. Declare a column written only by an afterInsert/afterUpdate hook on another object.
  2. Put it in highlightFields, and mark it readonly on the form view's section field.
  3. Open the record detail page. The header chip for that column is editable; changing it and saving persists the value, overwriting what the hook computed. The create/edit dialog for the same record refuses the same edit.

Impact

For us these are derived business figures — a customer grade written back by a rating approval, and a supply-share percentage rolled up from two child objects. A user clicking a chip silently replaces a computed value with a typed one, with no indication that the column is machine-owned.

A readonly flag on highlightFields entries — or having the header chips honour the form view's field-level readonly — would close this.

Environment

@objectstack/* protocol ^17.0.0-rc.1, console from @objectstack/console, driver-sql on SQLite.

Filed from a downstream app (hotcrm-heimao), tracked there as issue #61.

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions