Skip to content

fix(security): detect remote SVG subresources loaded via src, link href and bare-string @import - #836

Merged
mrbobbytables merged 1 commit into
mainfrom
sec/svg-remote-subresources
Sep 29, 2026
Merged

mrbobbytables merged 1 commit into
mainfrom
sec/svg-remote-subresources

Conversation

@hivecommons-hive

Copy link
Copy Markdown
Contributor

Security Fix

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, and scripts/validate-architecture-assets.mjs:188-190
records each of its findings as a blocking CI error.

It only inspected an href attribute on a RESOURCE_ELEMENTS element and a
url(...) token in CSS. Three shapes that load a remote subresource carry
neither and walked past it silently:

  1. src was never inspected at all. It is not an SVG attribute, so it
    reaches the scanner through the HTML a foreignObject is allowed to carry
    (foreignObject is deliberately permitted, and the swisscom diagram in the
    corpus already contains one).
  2. link was absent from RESOURCE_ELEMENTS, so a stylesheet href on a
    link element was never tested.
  3. A CSS @import with a bare-string target (@import "https://...";) is
    valid CSS and fetches the stylesheet exactly as the url(...) form does,
    but carries no url( token for CSS_URL_PATTERN to 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-architectures job and
import-architecture-issue.mjs.

What changed

scripts/lib/svg-active-content.mjs only:

  • inspect src on every element. Unlike href there is no element on which
    src reads as visitor-chosen navigation, so it needs no allow-list.
  • add link to RESOURCE_ELEMENTS. a deliberately stays out: a hyperlink is
    navigation the visitor chooses, and the existing draw.io corpus carries one.
  • add CSS_IMPORT_PATTERN and scan style blocks for it alongside
    CSS_URL_PATTERN. A target matched by both is still reported once.

Verification

  • New tests in tests/svg-remote-subresources.test.mjs (a new file, so it
    cannot 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:check exit 0; scripts/lib/svg-active-content.mjs at
    100.00% lines.
  • validate:architecture-assets exit 0 over all 50 corpus SVGs (unchanged -
    the corpus contains no src, link href or @import, so this adds no new
    failures).
  • prettier --check . exit 0; build:production exit 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.mjs
and does not modify scripts/lib/svg-active-content.mjs. This PR modifies only
that 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

…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>
@hivecommons-hive

Copy link
Copy Markdown
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 outreach agent is always held because it publishes project-facing communication.

Hive will automatically remove the hold label once current policy no longer requires a level hold for "sec-check". If this is an outreach PR, a human must review it and remove the label.

@hivecommons-hive hivecommons-hive Bot added security Approved by a Hive merger/owner for auto-merge on green CI agent/security Approved by a Hive merger/owner for auto-merge on green CI hive/hosted-available-lke648397-260827-5n31 Approved by a Hive merger/owner for auto-merge on green CI labels Sep 29, 2026
@mrbobbytables
mrbobbytables added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit 61bbadb Sep 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent/security Approved by a Hive merger/owner for auto-merge on green CI hive/hosted-available-lke648397-260827-5n31 Approved by a Hive merger/owner for auto-merge on green CI security Approved by a Hive merger/owner for auto-merge on green CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[sec-check] SVG remote-reference gate misses src attributes, link hrefs and bare-string @import

1 participant