fix(security): detect remote SVG subresources loaded via src, link href and bare-string @import - #836
Merged
Merged
Conversation
…ef and bare-string @import findRemoteReferences() is the gate that stops an imported SVG from making a visitor's browser fetch a subresource from a third-party host, and validate-architecture-assets.mjs records each of its findings as a blocking CI error. It only inspected an href on a RESOURCE_ELEMENTS element and a url() token in CSS, so three shapes that load a remote subresource walked past it silently: - a src attribute, which reaches the scanner through the HTML a foreignObject is allowed to carry, and which was never inspected at all; - a link element's stylesheet href, because link was absent from RESOURCE_ELEMENTS; - a CSS @import whose target is a bare string, which carries no url() token. Inspect src on every element (unlike href there is no element on which it reads as visitor-chosen navigation, so it needs no allow-list), add link to RESOURCE_ELEMENTS, and scan style blocks for the bare-string @import form alongside url(). The a element stays out of RESOURCE_ELEMENTS: a hyperlink is navigation the visitor chooses and the existing draw.io corpus carries one. Signed-off-by: hivecommons-hive[bot] <hivecommons-hive@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 Sep 29, 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()inscripts/lib/svg-active-content.mjsis the controlthat stops an imported SVG from making a visitor's browser fetch a subresource
from a third-party host, and
scripts/validate-architecture-assets.mjs:188-190records each of its findings as a blocking CI
error.It only inspected an
hrefattribute on aRESOURCE_ELEMENTSelement and aurl(...)token in CSS. Three shapes that load a remote subresource carryneither and walked past it silently:
srcwas never inspected at all. It is not an SVG attribute, so itreaches the scanner through the HTML a
foreignObjectis allowed to carry(
foreignObjectis deliberately permitted, and the swisscom diagram in thecorpus already contains one).
linkwas absent fromRESOURCE_ELEMENTS, so a stylesheethrefon alinkelement was never tested.@importwith a bare-string target (@import "https://...";) isvalid CSS and fetches the stylesheet exactly as the
url(...)form does,but carries no
url(token forCSS_URL_PATTERNto match.When a visitor opens such an SVG directly at the site origin - the threat model
this module is written against - each one discloses their IP address,
User-Agent and Referer to a host the diagram's author chose. Both writers of
these files take third-party input: the daily
import-architecturesjob andimport-architecture-issue.mjs.What changed
scripts/lib/svg-active-content.mjsonly:srcon every element. Unlikehrefthere is no element on whichsrcreads as visitor-chosen navigation, so it needs no allow-list.linktoRESOURCE_ELEMENTS.adeliberately stays out: a hyperlink isnavigation the visitor chooses, and the existing draw.io corpus carries one.
CSS_IMPORT_PATTERNand scanstyleblocks for it alongsideCSS_URL_PATTERN. A target matched by both is still reported once.Verification
tests/svg-remote-subresources.test.mjs(a new file, so itcannot conflict with test: cover reachable fallback branches in svg-active-content.mjs #818 - see below). Mutation-checked both directions:
5 of the 9 fail without the source change, and all 9 pass with it.
test:unit:coverage:checkexit 0;scripts/lib/svg-active-content.mjsat100.00% lines.
validate:architecture-assetsexit 0 over all 50 corpus SVGs (unchanged -the corpus contains no
src,linkhref or@import, so this adds no newfailures).
prettier --check .exit 0;build:productionexit 0.Relationship to open PRs
Disjoint from #818 ("test: cover reachable fallback branches in
svg-active-content.mjs"), which appends to
tests/svg-active-content.test.mjsand does not modify
scripts/lib/svg-active-content.mjs. This PR modifies onlythat source file and adds a separate test file, so the two can merge in either
order.
Closes #835
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