[sec-check] SVG remote-reference gate misses src attributes, link hrefs and bare-string @import
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 88/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- javascript
- Domain
- security
Research direction
Start in scripts/lib/svg-active-content.mjs at findRemoteReferences(), RESOURCE_ELEMENTS, CSS_URL_PATTERN, and cssTargets(); run the three reproduction snippets from the issue. The work is done when remote src attributes, link hrefs, and quoted bare-string @import targets are reported while ordinary anchor navigation remains allowed.
Written by the indexing model from the issue text.
Description
Security Finding
Severity: medium
Type: unsafe-pattern (incomplete input validation / detection bypass)
findRemoteReferences() in scripts/lib/svg-active-content.mjs is the control
that stops an imported SVG from making a visitor's browser fetch a subresource
from a third-party host. scripts/validate-architecture-assets.mjs:188-190
records every one of its findings as a hard error, so it blocks CI on every
pull request and every scheduled import.
It only ever inspects two things: an href attribute on an element in
RESOURCE_ELEMENTS, and a url(...) token in CSS. Three reference shapes that
load a remote subresource carry neither, and walk past the gate silently.
1. src is never inspected
src is not an SVG attribute, so it reaches the scanner only through the HTML
a <foreignObject> may carry — and foreignObject is deliberately allowed
("editors such as draw.io emit it for ordinary text", svg-active-content.mjs:19-20;
static/img/architectures/swisscom-cloud-native-telco/swisscom-cloud-native-telco-automation-layers.svg
already contains one). The src attribute on img, video, source and friends
is all permitted today.
2. <link> is not in RESOURCE_ELEMENTS
<link rel="stylesheet" href="https://evil.example/x.css"> inside a
foreignObject fetches a third-party stylesheet on load. link is absent from
the set at svg-active-content.mjs:83-89, so its href is never tested.
3. CSS @import with a bare string target
@import "https://evil.example/x.css"; is valid CSS and fetches the stylesheet
exactly as @import url(...) does, but carries no url( token for
CSS_URL_PATTERN to match. STYLE_BLOCK_PATTERN finds the block; the scan
inside it finds nothing.
Reproduction
Against scripts/lib/svg-active-content.mjs at 7772dcf:
import { findRemoteReferences } from './scripts/lib/svg-active-content.mjs';
const svg = (b) => `<svg xmlns="http://www.w3.org/2000/svg">${b}</svg>`;
findRemoteReferences(svg('<foreignObject><img src="https://evil.example/beacon.png"/></foreignObject>'));
// [] <- not flagged
findRemoteReferences(svg('<foreignObject><link rel="stylesheet" href="https://evil.example/x.css"/></foreignObject>'));
// [] <- not flagged
findRemoteReferences(svg('<style>@import "https://evil.example/x.css";</style>'));
// [] <- not flagged
findRemoteReferences(svg('<image href="https://evil.example/x.png"/>'));
// ['references a remote resource in <image> href: ...'] <- control, flagged
Impact
static/img/architectures/** is published verbatim at the site origin, and the
module's own threat model is that a visitor may open an SVG directly rather
than through an <img> (svg-active-content.mjs:4-7). When they do, every
subresource in the three shapes above is fetched from a host the diagram's
author chose, disclosing the visitor's IP address, User-Agent and Referer to
that third party — exactly the outcome findRemoteReferences() exists to
prevent and that the artwork-mirroring in scripts/lib/project-assets.mjs
exists to prevent elsewhere.
The author is third-party in both paths that write these files: the daily
import-architectures job from the upstream cncf/architecture checkout, and
import-architecture-issue.mjs from a community submission issue. Neither is
reviewed byte-by-byte; the CI error is the review. A remote stylesheet
additionally lets the third party restyle the document it is loaded into.
The current corpus is clean (50 SVGs under static/, no src=, <link href> or @import), so this is a gap in the gate rather than an exposure that
has already landed.
Recommendation
Extend the detection in scripts/lib/svg-active-content.mjs to cover all
three. Exact replacement text:
-
Add
'link'toRESOURCE_ELEMENTS(svg-active-content.mjs:83-89). -
Check
srcon any element — unlikehrefthere is no element on which it
reads as visitor-chosen navigation, so it needs no allow-list. In
findRemoteReferences(), replaceif (name === 'href' && RESOURCE_ELEMENTS.has(element)) {with
if ( (name === 'href' && RESOURCE_ELEMENTS.has(element)) || name === 'src' ) { -
Add a bare-string
@importpattern besideCSS_URL_PATTERNand scan for
both incssTargets():const CSS_IMPORT_PATTERN = /@import\s+(?:"([^"]*)"|'([^']*)')/gi;function cssTargets(css) { const targets = []; for (const pattern of [CSS_URL_PATTERN, CSS_IMPORT_PATTERN]) { for (const match of String(css).matchAll(pattern)) { const value = match[1] ?? match[2] ?? match[3] ?? ''; const target = remoteTarget(value); if (target) targets.push(target); } } return targets; }
a must stay out of RESOURCE_ELEMENTS: a hyperlink is navigation the visitor
chooses, and the existing draw.io corpus carries one.
Scope
One deliverable, one file, one function — a single pull request closes this.
-
srcon any element is reported as a remote reference - a
linkelement href is reported as a remote reference - bare-string
@importin a<style>block is reported as a remote reference
This does not overlap #818, which adds branch-coverage tests to
tests/svg-active-content.test.mjs and does not change
scripts/lib/svg-active-content.mjs.
Filed by sec-check agent (ACMM L4/L5 - hold-gated mode)
🐝 Hive Agent: security | Instance: hosted-available-lke648397-260827-5n31 | SHA: 7772dcf
— hive: agent=sec-check backend=copilot model=claude-opus-5 copilot=1.0.88
- Dominant language
- JavaScript
- Stars
- 0
- Forks
- 2
- Avg merge
- 17h
- Merged PRs (30d)
- 587
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from cncf/endusers
-
[quality] audit-schedule.yml is the only workflow with no contract test: nothing asserts where the check:audit gate runsPossibly taken @hivecommons-hive claimed this today. Openagent/quality hive/covered-by-pr hive/hosted-available-lke648397-260827-5n31 quality testing
Difficulty 2/5 1-3 hours Newbie friendliness 30/100
cncf/endusers#1507 · 1 comment ·
Maintainers usually reply within 1 day
-
[architect] Four validators gate an 'ISO 8601' timestamp with plain Date.parse, accepting loose strings the format claims to rejectPossibly taken @hivecommons-hive claimed this today. Openagent/architect architecture hive/covered-by-pr hive/hosted-available-lke648397-260827-5n31 tech-debt
Difficulty 3/5 1-2 days Newbie friendliness 20/100
cncf/endusers#1505 · 2 comments ·
Maintainers usually reply within 1 day
-
[sec-check] Served SVGs carry no script-src: the one CSP layer a meta tag can never reach is still missing after #1496Possibly taken @hivecommons-hive claimed this today. Openagent/sec-check hive/covered-by-pr hive/hosted-available-lke648397-260827-5n31 security
Difficulty 3/5 1-2 days Newbie friendliness 22/100
cncf/endusers#1501 · 1 comment ·
Maintainers usually reply within 1 day
-
[quality] e2e coverage gate's recorded measurement is stale at 1ae3403: 651/671 and 20 zeros against a measured 655/676 and 21Possibly taken @hivecommons-hive claimed this today. Openagent/quality hive/covered-by-pr hive/hosted-available-lke648397-260827-5n31 quality testing
Difficulty 2/5 1-3 hours Newbie friendliness 25/100
cncf/endusers#1497 · 4 comments ·
Maintainers usually reply within 1 day
-
agent/strategist hive/hosted-available-lke648397-260827-5n31 needs-decision roadmap
Difficulty 4/5 1-2 days Newbie friendliness 15/100
cncf/endusers#1458 · 1 comment ·
Maintainers usually reply within 1 day
Similar issues
-
area:install bug needs-triage
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
yetone/magpie#2406 · 1 comment ·
Maintainers usually reply within 1 day
-
[BUG]: Prompt compressor crashes with TypeError when a past chat has a non-string response.textPossibly taken @ManojDevarajulu claimed this today. Openpossible bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Mintplex-Labs/anything-llm#6737 ·
Maintainers usually reply within 1 day
-
check:passed feeds:add
Difficulty 2/5 Under an hour Newbie friendliness 62/100
-
Remove the dead tree 'loadfail' handler in browser.js, which calls a non-existent tree.isSelected()OpenHousekeeping
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
pgadmin-org/pgadmin4#10601 ·
Maintainers usually reply within 1 day