Repository navigation
fix(mdx-active-content): match the element allowlist case-sensitively and reject string style attributes - #1334
Conversation
|
Important Held for human review by the hive's ACMM level gate. This PR was opened by the "sec-check" agent while Hive policy required a human checkpoint for that agent. Non-outreach agents are held at ACMM L3–L5; the Hive will keep the |
|
CI triage (ci-maintainer): Lint repository / Check spelling is red on head 3e1d274 The word I cannot push to this lane's branch; leaving this as the finding record. 🐝 Hive Agent: — hive: agent=ci-maintainer backend=copilot model=kimi-k3 copilot=1.0.88 |
… and reject string style attributes
findActiveContent() states that text the parser rejects is a finding,
because a scan that cannot read a document cannot vouch for it. Two
inputs broke that invariant in the other direction: the scan returned no
findings while Docusaurus threw during prerender.
The element allowlist was matched through name.toLowerCase(). MDX reads
a lower-case tag name as an HTML element and any other casing as a
component reference, so <B> is not the inert <b> the allowlist permits:
it compiles to a _missingMdxReference lookup that throws "Expected
component `B` to be defined". Every entry in ALLOWED_ELEMENTS is already
lower case, so matching case-sensitively only removes the
component-reference casings.
A string-valued style attribute was also unreported. React requires
style to be an object and throws when handed a string, and
style={"..."} reaches React as a string too because literalValue()
reads a lone string literal as inert. The object form is already
reported as an MDX expression, so no style attribute now reaches a
rendered page by any route.
No content in docs/ uses either construct, so the tightening is
non-breaking.
Closes #1333
Signed-off-by: sec-check <sec-check@hive.kubestellar.io>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: mrbobbytables <mrbobbytables@users.noreply.github.com>
3e1d274 to
8afdb51
Compare
Security Fix
findActiveContent()states its own invariant up front: text the parser rejectsis a finding, because a scan that cannot read a document cannot vouch for it.
Two inputs broke that invariant in the other direction — the scan returned no
findings,
validate:architecturespassed, and Docusaurus threw whileprerendering the page.
Element allowlist matched case-insensitively
scripts/lib/mdx-active-content.mjsresolved the tag name throughALLOWED_ELEMENTS.has(name.toLowerCase()). MDX reads a lower-case tag name as anHTML element and any other casing as a component reference, so
<B>is notthe inert
<b>the allowlist permits — it compiles to a_missingMdxReferencelookup that throws
Expected componentBto be defined. Verified atfb75c1a:findActiveContent()returned[]for<B>bold</B>,<Code>x</Code>and<HR />.Every entry in
ALLOWED_ELEMENTSis already lower case, so matchingcase-sensitively removes only the component-reference casings.
String-valued
styleattribute unreportedReact requires
styleto be an object;renderToStaticMarkupon a stringstyleprop throws "Thestyleprop expects a mapping from style properties tovalues, not a string".
style={"..."}reached React as a string too, becauseliteralValue()deliberately reads a lone string literal as inert. The objectform
style={{ ... }}was already reported as anMDX expression, so with thischange no
styleattribute reaches a rendered page by any route.Scope
Files and functions claimed by this PR:
scripts/lib/mdx-active-content.mjs—findActiveContent()element-name matchand attribute loop; new
STYLE_ATTRIBUTEconstanttests/mdx-active-content.test.mjs— two added regression testsCONTENT-SECURITY.md— "Markdown/MDX page rules" sectionDisjoint from the open hold-gated PR #1330, which covers
DirectoryFreshnessend-to-end coverage.
Verification
tests/mdx-active-content.test.mjs: 41 pass, 0 fail (39 pre-existing + 2 added)tests/content-security-contract.test.mjs: 13 pass, 0 fail — the documentedelement list is unchanged, so the exact-match contract still holds
npm run validate:architectures— "Validated 8 architecture records"npm run validate:architecture-assets— cleannpx prettier --checkclean on all three filesdocs/uses either construct, so the tightening is non-breakingThis is a gate-correctness and availability fix, not an exploitable-XSS fix. I
confirmed the remote-beacon angle is independently closed:
stylenever reachesa rendered page, and CSP
img-srcindocusaurus.config.jsgoverns CSSbackground images regardless.
Closes #1333
Filed by sec-check agent (ACMM L4/L5 — hold-gated mode). Hold-gated: human review required.
— hive: agent=sec-check backend=copilot model=claude-opus-5 copilot=1.0.88