You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
finding(plugin-dashboard): the #3291 toDomProps whitelist stops at packages/fields — SDUI widgets elsewhere still close their DOM leak by hand, if at all #4425
Observation-class finding, recorded while implementing #4357. Nothing a user sees today beyond what #4357 fixes; filed so the next agent closing a DOM-prop leak knows this repo already has a decided answer, and so the divergence is a deliberate choice rather than an accident.
The two answers, both live
objectui#3291 / PR #3313 closed exactly this class in packages/fields, and the reasoning is written out in packages/fields/src/widgets/toDomProps.ts under "Why a whitelist, and not a list of keys to drop":
The biggest leak source is not any NAMED renderer prop — it is the open tail of author-supplied keys. […] A blacklist enumerating today's renderer-only keys (error, emptyHint, dataSource, dependentValues, dependsOn, options, inputType, …) would pass every one of the canaries above and would not stop the next authored key either.
That doc also names this exact path as the harder one:
SchemaRenderer is wider still: it spreads the whole authored node as props and has no strip layer at all, so on the SDUI path a widget's own spread is the ONLY line of defence.
#4357 closes the same defect in MetricWidget / MetricCard the other way — a deny-list, destructured out (packages/plugin-dashboard/src/schemaHostProps.ts), per the ruling on that card. It is correct for the seven measured props and it is verified, but it is the shape toDomProps argues against, and it demonstrates the predicted weakness in miniature: the first pass enumerated six keys from a schema-only measurement and missed the seventh (dataSource, the injected adapter, which only appears on a dashboard that actually loads data). One authored key that a widget does not declare still reaches the DOM — measured on metric-card, an authored props: { colorVariant } lands as colorvariant="success" because MetricCard has no such prop.
What is and is not covered today
packages/fields: whitelist + a real gate. widget-dom-leak-e2e.test.tsx renders every registered field widget through both hosts and checks every attribute of every element against what HTML defines, with planted canaries. It does not reach any other package.
Everywhere else: unchecked. Components that end in a ...props spread onto a DOM element and are reachable from the registry exist in at least plugin-charts (ChartContainerImpl.tsx, AdvancedChartImpl.tsx), plugin-calendar and plugin-chatbot. Listed as candidates to measure, not asserted as defects — SchemaRenderer injects schema into every registered component, so the mechanism reaches all of them, but whether a given component spreads that onto a host element was not verified here.
Fix shape (needs a decision, do not start)
Promote the whitelist to the SDUI widget contract generally — a toDomProps equivalent (or the same one, generalized off FieldWidgetComponentProps) that dashboard/chart/calendar widgets use instead of a bare spread.
Generalize the #3291 sweep so the gate covers registered widgets in every package, not only fields. The canary technique is what catches the open tail; a pin on named keys cannot.
Or record the divergence deliberately: fields is whitelisted because its contract is closed, and SDUI plugin widgets stay deny-listed because their prop surface is open.
The choice belongs with the maintainer — 1 and 2 touch the public prop contract of every plugin widget. No pm:queue.
Observation-class finding, recorded while implementing #4357. Nothing a user sees today beyond what #4357 fixes; filed so the next agent closing a DOM-prop leak knows this repo already has a decided answer, and so the divergence is a deliberate choice rather than an accident.
The two answers, both live
objectui#3291 / PR #3313 closed exactly this class in
packages/fields, and the reasoning is written out inpackages/fields/src/widgets/toDomProps.tsunder "Why a whitelist, and not a list of keys to drop":That doc also names this exact path as the harder one:
#4357 closes the same defect in
MetricWidget/MetricCardthe other way — a deny-list, destructured out (packages/plugin-dashboard/src/schemaHostProps.ts), per the ruling on that card. It is correct for the seven measured props and it is verified, but it is the shapetoDomPropsargues against, and it demonstrates the predicted weakness in miniature: the first pass enumerated six keys from a schema-only measurement and missed the seventh (dataSource, the injected adapter, which only appears on a dashboard that actually loads data). One authored key that a widget does not declare still reaches the DOM — measured onmetric-card, an authoredprops: { colorVariant }lands ascolorvariant="success"becauseMetricCardhas no such prop.What is and is not covered today
packages/fields: whitelist + a real gate.widget-dom-leak-e2e.test.tsxrenders every registered field widget through both hosts and checks every attribute of every element against what HTML defines, with planted canaries. It does not reach any other package.packages/plugin-dashboard: afterMetricWidget/MetricCardspread...propsonto the DOM, emitting aschema="[object Object]"attribute on every KPI card #4357, two components with a measured deny-list and a pin (MetricWidget.domProps.test.tsx). No canary, no sweep....propsspread onto a DOM element and are reachable from the registry exist in at leastplugin-charts(ChartContainerImpl.tsx,AdvancedChartImpl.tsx),plugin-calendarandplugin-chatbot. Listed as candidates to measure, not asserted as defects —SchemaRendererinjectsschemainto every registered component, so the mechanism reaches all of them, but whether a given component spreads that onto a host element was not verified here.Fix shape (needs a decision, do not start)
toDomPropsequivalent (or the same one, generalized offFieldWidgetComponentProps) that dashboard/chart/calendar widgets use instead of a bare spread.#3291sweep so the gate covers registered widgets in every package, not only fields. The canary technique is what catches the open tail; a pin on named keys cannot.The choice belongs with the maintainer — 1 and 2 touch the public prop contract of every plugin widget. No
pm:queue.Generated by Claude Code