Skip to content

[P3][icebox] Enforce role-gated / dependent select option values on the server (visibleWhen is UX-only) #2729

Description

@os-zhuang

Context

Per-option visibleWhen cascading + dependsOn gating for select / multiselect / radio is implemented client-side (ADR-0058, #2284 · multiselect parity #2717 · radio parity #2728). The resolver is resolveCascadingOptions in @object-ui/core, wrapped by the useCascadingOptions hook and the form renderer.

The code is explicit that this is UX only, not a security boundary — see the header comment in packages/core/src/evaluator/optionRules.ts:

Client-side hiding is UX, not a security boundary: when an option is gated for access-control reasons the server must also reject writes of its value.

The gap

A role-gated option (e.g. visibleWhen: "'admin' in current_user.positions") is hidden from non-admins in the UI, but nothing stops a crafted API write from submitting that value directly. For access-control-sensitive option sets the server must reject a write whose value is not in the caller's offered set (evaluating the same per-option predicate + dependsOn against the server-side record + current user).

Why icebox

No role-gated option set is relied on as a security control today; this is a hardening / correctness follow-up. Pick up when a real access-controlled option field ships.

Scope (when actioned)

  • Server-side field validation for select / multiselect / radio: on write, reject values whose per-option visibleWhen evaluates FALSE (and/or whose dependsOn parent is unmet) against the server record + current user.
  • Reuse the same predicate semantics as the client (optionRules / CEL evalFieldPredicate) so client and server agree on the offered set.
  • This lives in framework (write-time field validation), not objectui.

Refs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions