Skip to content

finding(plugin-detail): record-alert's readProps object-spreads a degenerate properties — the same class as #6783, a different expression #6790

Description

@os-sales

Measured while implementing #6783 (the five readProps() copies in packages/components/src/renderers/basic/), outside its fence — that card's file surface is packages/components/src/renderers/basic/ only — so filed separately. Observation-class in the same sense #6752, #6760 and #6783 were: nothing is known to depend on it today.

The fact, measured on 107babef6

packages/plugin-detail/src/renderers/record-alert.tsx:168:

function readProps(schema: any) {
  const fromNested = (schema?.properties ?? {}) as any;
  return { ...schema, ...fromNested };
}

?? only replaces null / undefined, so a degenerate properties passes straight into the object spread and is re-read as its own character indices — Object.keys({ ...'not-a-bag' }) is ["0","1","2","3","4","5","6","7","8"]. This is the identical hazard #6783 closed on the five renderers/basic/ copies, on the same channel (the config bag a renderer reads), in a different expression: this one merges { ...schema, ...schema.properties } rather than { ...schema.props, ...schema.properties }, so it has no props alias leg but does put the node's own keys underneath.

The alias-precedence-cross-channel.test.tsx docblock already names this site as the sixth member of the family, so it is known ground, not a new discovery — what is new is that #6783 has now converged the other five and left this one alone.

Blast radius, honestly bounded

Same denominator as the sibling cards: #6708's census walked every JSON document, every json doc fence and every TypeScript object literal and found zero authored nodes carrying a degenerate config bag. Latent shape, not a live failure.

And the same measurement #6783 recorded about its own five applies here: record-alert reads only named keys off the bag (props.title, props.body, props.severity, props.action?.label, props.dismissible, …) and never spreads it onto a DOM element, so today the indexed keys are computed and then dropped. Nothing rendered moves either way. What the guard would buy is what #6752 measured its own guard buys: the authored value's shape is not reinterpreted.

Arms, if anyone takes it

Not findings, recorded so the next sweep does not re-derive them

The same repo-wide sweep (?? {} / || {} on a .props / .properties member) turned up hits that are a different question and should not be filed as this one:

  • packages/plugin-detail/src/renderers/record-activity.tsx:105const bag = (schema.properties ?? {}) followed by keyed reads bag[key]. No spread, so nothing is enumerated; a degenerate value yields undefined per key.
  • packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx:1285 — keyed read props[key]?.title, no spread.
  • The remaining metadata-admin hits (SchemaForm.tsx, widgets.tsx, mergeServerFields.ts, json-schema-to-fields.ts, datasource/DatasourceResourcePage.tsx, previews/ToolPreview.tsx) read a JSON Schema properties map, not the SDUI config bag.

Related: #6783 (the five, converged), #6760 (the properties hoist), #6752 (the props half), #6761 (one predicate in packages/react), #6708 (the census). Found by PR #6789.

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