Skip to content

fix(security): reject remote image destinations in imported MDX pages - #1255

Merged
mrbobbytables merged 1 commit into
mainfrom
sec/mdx-remote-image-beacon
Oct 10, 2026
Merged

mrbobbytables merged 1 commit into
mainfrom
sec/mdx-remote-image-beacon

Conversation

@hivecommons-hive

Copy link
Copy Markdown
Contributor

Security Fix

scripts/lib/uri-safety.mjs states the invariant both content gates are built
on: each must decide "does it run script, and does it make a visitor's browser
contact another host", and a question one gate asks and the other does not is a
bypass of the more permissive one.

The SVG gate asks both — findActiveContent() and findRemoteReferences().
The MDX gate asked only the first: findActiveContent() in
scripts/lib/mdx-active-content.mjs never called remoteTarget(), so a remote
image destination in a page imported verbatim from cncf/architecture passed
the only gate that runs on published pages
(scripts/validate-architectures.mjs:142) and shipped as a third-party beacon
for every visitor's IP, User-Agent and Referer.

What this changes

findActiveContent() now reports a remote image destination, scoped to the
destinations a browser fetches with no user action:

  • image nodes whose url is a remote target;
  • imageReference nodes, resolved through their definition (the definition
    node itself is not flagged — linkReference resolves through the same map);
  • the logo attribute of CNCFProjectCard, the only allowed attribute that
    reaches an <img src>: it is passed through useBaseUrl(), which returns an
    absolute URL unchanged (src/components/CNCFProjectCard/index.js:16,20).

Deliberately untouched, because flagging them would reject every imported page:
ordinary links, remote link definitions (docs/architectures/colopl.md:48-52),
and the card's href, which is remote by contract.

Verification

  • node scripts/validate-architectures.mjs → Validated 8 architecture records
    on the current docs/architectures/ tree, unchanged.
  • npm run test:unit:coverage:check exits 0; scripts/lib/mdx-active-content.mjs
    is at 100.00% lines / 100.00% regions.
  • npx prettier --check clean on both changed files.

Files claimed by this PR: scripts/lib/mdx-active-content.mjs,
tests/mdx-active-content.test.mjs. Disjoint from the open hold-gated PRs
(#1249 scripts/lib/uri-safety.mjs + tests/svg-active-content.test.mjs,
#1250 package.json + tests/coverage-gate-thresholds.test.mjs,
#1252 tests/e2e-coverage-source-boundary-segments.test.mjs). This PR imports
remoteTarget/describeTarget from uri-safety.mjs but does not edit it, so
it composes with #1249 rather than conflicting with it.

Closes #1254


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

findActiveContent() answered only the first of the two questions
scripts/lib/uri-safety.mjs states both content gates must answer: does the
value run script, and does it make a visitor's browser contact another host.
The SVG gate answers both (findActiveContent plus findRemoteReferences); the
MDX gate never called remoteTarget(), so a remote <img> in a page imported
verbatim from cncf/architecture published as a third-party beacon.

Checked, scoped to destinations a browser fetches with no user action:
image nodes, imageReference nodes resolved through their definition, and the
logo attribute of CNCFProjectCard, which reaches an <img src> through
useBaseUrl(). Links, link definitions and the card's remote href are
deliberately untouched: rewriteImages() demotes a remote image to a remote
link, and imported pages carry legitimate remote link definitions.

Closes #1254

Signed-off-by: sec-check <sec-check@hive.kubestellar.io>
@hivecommons-hive hivecommons-hive Bot added the hold label Oct 9, 2026
@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 keep the hold label until a human removes it. Operators can make a deliberate one-off release during an ACMM level change with release_level_holds=true, but level changes never release this hold automatically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[sec-check] imported MDX pages publish remote image beacons: findActiveContent() never applies remoteTarget()

1 participant