The measurement
Found while measuring the blast radius of objectui#8793 (which closes the block's
own fail-open branch). This is a SECOND, independent hole on the same path, and
it is reachable today, with entirely spec-canonical authoring — objectui#8793
does not cause it and does not fix it.
record:related_list filters its authored columns against the allow-list it
builds from enforceFieldSecurity / redactFields. When that filter removes
every member, it hands RelatedList an EMPTY array — and RelatedList reads
an empty array as "no columns were authored":
// packages/plugin-detail/src/RelatedList.tsx, effectiveColumns
if (columns && columns.length > 0) {
const normalized = columns.map(normalizeColumn);
return pruneEmpty(filterFLS(filterFK(normalized)));
}
// ...falls through to the highlightFields / heuristic auto-derivation
The auto-derived path runs filterFK, pruneEmpty and filterFLS — but the
block's redactFields list is not in scope there at all. It is a block-level
concept that never reaches RelatedList. So the redacted field comes back.
Reproduction (measured, real table, real cells)
Block schema: columns: [{ field: 'salary', label: 'Salary' }],
redactFields: ['salary']; child object schema declares subject and salary;
one row { subject: 'Fix the pump', salary: '90000' }.
Rendered body cells, read from the DOM:
[ 'Fix the pump', '90000' ]
90000 is the value the author redacted. Redacting the ONLY authored column
turns redaction into a no-op — worse than a no-op, because the fallback may
surface fields the author never listed.
Why it is not folded into objectui#8793
That card names one branch — an entry whose identity does not resolve was KEPT —
and its repair strictly narrows the authored array. This defect lives one layer
down, in what RelatedList does with an empty array, and it fires on authoring
that objectui#8793 never touches ({ field: 'salary' } resolves fine). Widening
that PR to cover it would have made the fail-closed change and a fallback-policy
change one diff.
⚠️ The two do interact, and that is worth stating: after objectui#8793 an
all-unresolvable authored column set also empties the array, so it reaches this
fallback too. Measured, same harness, same result — [ 'Fix the pump', '90000' ].
Shape of the fix (not a ruling)
The question is what an emptied-by-security column set MEANS. Two readings, and
they differ for users:
- A: an empty result after filtering is "the author authored columns and none of
them survived" — distinct from "no columns authored", so the list should render
its empty/denied state rather than deriving a replacement set.
- B: the derivation is legitimate, and
redactFields (plus whatever else the
block decides) must be pushed down so the derived set is filtered by the same
policy as the authored one.
B is the shape the block's own comment already assumes (FLS is pushed down, via
perms.checkField); redaction simply never was. A is the smaller change and the
one that keeps the two vocabularies apart.
Filed by the dev seat working objectui#8793; scope fence held, no diff here.
Generated by Claude Code
The measurement
Found while measuring the blast radius of objectui#8793 (which closes the block's
own fail-open branch). This is a SECOND, independent hole on the same path, and
it is reachable today, with entirely spec-canonical authoring — objectui#8793
does not cause it and does not fix it.
record:related_listfilters its authoredcolumnsagainst the allow-list itbuilds from
enforceFieldSecurity/redactFields. When that filter removesevery member, it hands
RelatedListan EMPTY array — andRelatedListreadsan empty array as "no columns were authored":
The auto-derived path runs
filterFK,pruneEmptyandfilterFLS— but theblock's
redactFieldslist is not in scope there at all. It is a block-levelconcept that never reaches
RelatedList. So the redacted field comes back.Reproduction (measured, real table, real cells)
Block schema:
columns: [{ field: 'salary', label: 'Salary' }],redactFields: ['salary']; child object schema declaressubjectandsalary;one row
{ subject: 'Fix the pump', salary: '90000' }.Rendered body cells, read from the DOM:
90000is the value the author redacted. Redacting the ONLY authored columnturns redaction into a no-op — worse than a no-op, because the fallback may
surface fields the author never listed.
Why it is not folded into objectui#8793
That card names one branch — an entry whose identity does not resolve was KEPT —
and its repair strictly narrows the authored array. This defect lives one layer
down, in what
RelatedListdoes with an empty array, and it fires on authoringthat objectui#8793 never touches (
{ field: 'salary' }resolves fine). Wideningthat PR to cover it would have made the fail-closed change and a fallback-policy
change one diff.
all-unresolvable authored column set also empties the array, so it reaches this
fallback too. Measured, same harness, same result —
[ 'Fix the pump', '90000' ].Shape of the fix (not a ruling)
The question is what an emptied-by-security column set MEANS. Two readings, and
they differ for users:
them survived" — distinct from "no columns authored", so the list should render
its empty/denied state rather than deriving a replacement set.
redactFields(plus whatever else theblock decides) must be pushed down so the derived set is filtered by the same
policy as the authored one.
B is the shape the block's own comment already assumes (FLS is pushed down, via
perms.checkField); redaction simply never was. A is the smaller change and theone that keeps the two vocabularies apart.
Filed by the dev seat working objectui#8793; scope fence held, no diff here.
Generated by Claude Code