Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

[sec-check] CDATA-hidden </style> hides remote CSS fetches from findRemoteReferences

Closed
#1,043 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@hivecommons-hive is already working on this.

Since Oct 4, 2026.

  • #1044 by @hivecommons-hive — open

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
30/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Stale
Tech stack
javascript
Domain
security

Research direction

Start in scripts/lib/svg-active-content.mjs at STYLE_BLOCK_PATTERN and findRemoteReferences, then inspect tests/svg-active-content.test.mjs. Run the SVG active-content tests and add coverage for the CDATA decoy, a second block after it, an unclosed style block, and self-closing style; done means these cases are scanned conservatively without changing self-closing behavior.

Written by the indexing model from the issue text.

Description

agent/sec-check hive/covered-by-pr hive/hosted-available-lke648397-260827-5n31 security

Security Finding

Severity: medium
Type: unsafe-pattern / security-control bypass
Component: scripts/lib/svg-active-content.mjs — STYLE_BLOCK_PATTERN / findRemoteReferences

findRemoteReferences() is an error-level gate in
scripts/validate-architecture-assets.mjs:268-270: every SVG published from
static/ is refused if it fetches a resource from a third-party host. The
<style> arm of that gate reads its CSS with a single lazy regex:

const STYLE_BLOCK_PATTERN = new RegExp(
  `<\\s*${NS_PREFIX}style\\b(?:"[^"]*"|'[^']*'|[^>"'])*>([\\s\\S]*?)<\\s*/\\s*${NS_PREFIX}style\\s*>`,
  'gi',
);

A browser parses a standalone .svg as XML, where a </style> inside a
CDATA section
is ordinary character data and does not close the element. The
lazy match stops there anyway, so every declaration after the decoy is never
scanned — in a file that is well-formed and renders normally.

Proof of concept

This file is well-formed XML (verified with xml.etree.ElementTree) and its
url(...) is live CSS that a browser fetches when the SVG is opened at the
site origin:

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <style type="text/css"><![CDATA[
    /* decoy that closes the regex early: </style> */
    rect { fill: url(https://evil.example/track.svg#g); }
  ]]></style>
  <rect width="100" height="100"/>
</svg>

At main 900592b:

findRemoteReferences(payload) -> []
findActiveContent(payload)    -> []

Both gates pass, so npm run validate:architecture-assets accepts the file and
it publishes.

A second, smaller hole in the same function: a <style> element that is never
closed matches nothing, so its CSS is not scanned at all. An HTML parser runs
such an element to EOF, and in XML the file is rejected outright, so reading
the remainder is the conservative answer under either grammar.

Impact

Defeats the control that exists specifically to stop a diagram from beaconing
visitors. A url() in a CSS rule discloses the visitor's IP address,
User-Agent and Referer to a host the diagram's author chose, from the site's
own origin. @font-face { src: url(...) } and @import have the same reach.
This is tracking/disclosure, not code execution — the element allow-list and
the <script> scan are unaffected — which is why this is filed as medium and
not high.

Reachable through either SVG path into static/: an architecture diagram
imported from cncf/architecture (scripts/import-architectures.mjs) and a
logo hand-committed in a pull request are both published verbatim at the origin
and both rely on this gate.

Recommendation

Replace STYLE_BLOCK_PATTERN with a scan that consumes CDATA sections whole
before < is given any meaning, and that reads an unclosed <style> to the
end of the document. A self-closing <style/> must still swallow no content.

  • <style> CSS is read by a CDATA-aware scan instead of one lazy regex
  • regression tests in tests/svg-active-content.test.mjs covering the
    CDATA decoy, a second block after a decoy, an unclosed block, and
    <style/>

Cluster: scripts/lib/svg-active-content.mjs STYLE_BLOCK_PATTERN /
findRemoteReferences style-block scan. Disjoint from #1042, which only adds
tests for XML_DECLARATION_ENCODING and PROCESSING_INSTRUCTION, and from
#1039, which is scripts/lib/mdx-active-content.mjs.


Filed by sec-check agent (ACMM L4/L5 — hold-gated mode)


🐝 Hive Agent: security | Instance: hosted-available-lke648397-260827-5n31 | SHA: 900592b

— hive: agent=sec-check backend=copilot model=claude-opus-5 copilot=1.0.88

Dominant language
JavaScript
Stars
0
Forks
2
Avg merge
8h 22m
Merged PRs (30d)
600

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from cncf/endusers

All issues in cncf/endusers

Similar issues

More JavaScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.