Skip to content

finding(types): MarkdownSchema.sanitize and .components are declared and mirrored but read by no renderer — and sanitize implies a switch that does not exist #6972

Description

@claude

Measured on main = 8f1d995ef447 while converging MarkdownSchema for #6172.

The fact

MarkdownSchema declares two members that no renderer reads:

member declared at read by
sanitize?: boolean (@default true) packages/types/src/data-display.ts nothing
components?: Record<string, any> same nothing

The Zod mirror carries both as well (packages/types/src/zod/data-display.zod.ts).

The registered markdown renderer is MarkdownRenderer in
packages/plugin-markdown/src/index.tsx. It forwards exactly two values to the
implementation — content and className:

LazyMarkdown content={schema.content || ''} className={schema.className}

MarkdownImplProps accepts only those two. Nothing anywhere destructures or
indexes schema.sanitize or schema.components.

sanitize is the more pointed of the two, because the key implies a switch that
does not exist: sanitization is unconditional. rehypeSanitize is a fixed
link in the rehype chain in MarkdownImpl.tsx, documented there as "the final
gate". Authoring sanitize: false is accepted by the type and by the Zod mirror,
changes nothing, and reads as a security-relevant control.

Why it is worth a card

ADR-0049 enforce-or-remove: declared = enforced. This is the same class as #6942
(TextSchema.variant) and #6910 (EventHandlersSchema), and it is the shape that
makes AI-authored metadata wrong in a way nothing catches: the key is offered by
the type, accepted by the validator, and silently inert.

⚠️ The population grew slightly as a side effect of #6172's convergence: with
@object-ui/plugin-markdown now re-exporting the one authority instead of
declaring its own narrower copy, both members are visible on the plugin's
published face too. That was a deliberate, recorded trade in that PR (converging
onto the canonical shape) and is called out in its body — but it does mean the
retirement, if that is the ruling, now covers two published faces rather than one.

Not fixed there: removing a published key is an ADR-0049 retirement with its own
route (registries, mirror, parity test, changeset), not a convergence.

Reproduce

grep -rn "sanitize\|components" packages/plugin-markdown/src/index.tsx packages/plugin-markdown/src/MarkdownImpl.tsx
grep -rn "schema.sanitize\|schema.components" packages/ apps/ --include=*.ts --include=*.tsx

Refs: #6172 (where this was measured), #6942 / #6910 (same class).


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions