Repository navigation
fix(security): scan <style> CSS past a CDATA-hidden </style> - #1044
Merged
Merged
Conversation
findRemoteReferences() is an error-level gate in
validate-architecture-assets.mjs: an SVG that fetches from a third-party host
is refused, because the fetch discloses the visitor's IP, User-Agent and
Referer from the site's own origin.
Its <style> arm read the CSS with one lazy regex. A browser parses a
standalone .svg as XML, where a </style> inside a CDATA section is character
data and does not close the element -- but the lazy match stops there, so
every declaration after the decoy went unscanned in a file that is well-formed
and renders. A rule such as rect{fill:url(https://host/x.svg#g)} placed after
it passed both gates.
Read the CSS with a scan that consumes CDATA sections whole before '<' is
given any meaning. An unclosed <style> now reads to the end of the document:
an HTML parser runs it to EOF and XML rejects the file outright, so scanning
the remainder is the conservative answer under either grammar. A self-closing
<style/> still swallows no content.
Closes #1043
Signed-off-by: sec-check <sec-check@hive.kubestellar.io>
Contributor
Author
|
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 automatically remove the |
This was referenced Oct 4, 2026
This was referenced Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Security Fix
findRemoteReferences()is an error-level gate inscripts/validate-architecture-assets.mjs:268-270: an SVG published fromstatic/is refused if it fetches a resource from a third-party host, becausethat fetch discloses the visitor's IP address, User-Agent and
Refererto ahost the diagram's author chose, from the site's own origin.
Its
<style>arm read the CSS with one lazy<style ...>([\s\S]*?)</style>regex. A browser parses a standalone.svgasXML, where a
</style>inside a CDATA section is character data and doesnot close the element — but the lazy match stops there, so every declaration
after the decoy went unscanned, in a file that is well-formed and renders.
At
main900592b this well-formed SVG returned[]from bothfindRemoteReferencesandfindActiveContent, sonpm run validate:architecture-assetsaccepted it:What changed
scripts/lib/svg-active-content.mjsonly.STYLE_BLOCK_PATTERNis replaced bystyleBlockContents(), which walks from the open tag and consumes CDATAsections whole before
<is given any meaning, so an inner</style>cannotend the block. Two related behaviours fall out of the same walk:
<style>is now read to the end of the document. Itpreviously matched nothing and was not scanned at all; an HTML parser runs it
to EOF and XML rejects the file outright, so reading the remainder is the
conservative answer under either grammar.
<style/>still swallows no content.The close-tag test uses a sticky regex rather than re-slicing the source, so
the walk stays linear over the 2 MB asset ceiling the validator enforces.
Five regression tests in
tests/svg-active-content.test.mjscover the CDATAdecoy, a second block following a decoy, an unclosed block and
<style/>; thestale
STYLE_BLOCK_PATTERNcomment on the existing empty-block test isupdated.
Verification
npm run test:unit— 1916/1916 pass (1912 before, +4 net new)npm run check— exit 0npm run validate:architecture-assets— 172 real assets, no new finding(the one pre-existing
foreignObjectwarning is unchanged)Scope
Files:
scripts/lib/svg-active-content.mjs,tests/svg-active-content.test.mjs.Function cluster:
STYLE_BLOCK_PATTERN→styleBlockContents/findRemoteReferences.Disjoint from the open held PRs: #1042 is test-only and covers
XML_DECLARATION_ENCODINGandPROCESSING_INSTRUCTIONin the same file —different functions and different test blocks; #1039 is
scripts/lib/mdx-active-content.mjs; #1034 and #1040 are the e2e coveragebuild.
Closes #1043
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