Skip to content

fix(import): stop hot-linking third-party project logos - #266

Open
kubestellar-hive[bot] wants to merge 1 commit into
mainfrom
sec/fix-project-logos
Open

kubestellar-hive[bot] wants to merge 1 commit into
mainfrom
sec/fix-project-logos

Conversation

@kubestellar-hive

Copy link
Copy Markdown
Contributor

Security Fix

Imported architecture cards took their logo straight from upstream cncf/architecture Markdown. projectAsset() only rewrote the narrow cncf/artwork icon/color shape and returned every other URL unchanged, so 16 of the generated cards hot-linked hosts the CNCF does not control — upload.wikimedia.org, raw.githubusercontent.com/netbox-community, kubernetes-sigs.github.io, landscape.cncf.io.

Every visitor to those pages disclosed IP, User-Agent and Referer to those third parties, and the artwork itself remained mutable by its owners (all referenced main, not a pinned SHA).

What this changes

  • Adds scripts/lib/project-assets.mjs, which resolves cncf/artwork URLs — both raw.githubusercontent.com and github.com/.../raw/ forms, any variant rather than only icon/color — to a mirrored local path, and returns null for anything else. Traversal segments, query/fragment tricks and lookalike hosts (raw.githubusercontent.com.evil.test) are rejected.
  • projectAsset() now fails closed and can never return a remote URL.
  • renderProjectCards() omits logo= when no local asset exists; CNCFProjectCard already renders a first-letter fallback badge in that case.
  • cleanMarkdown() handles the null return by emitting a plain link instead of throwing.
  • mirrorProjectAssets() is widened to mirror every referenced cncf/artwork asset. Mirrored filenames keep the historical <name>-<file> shape, so the 19 existing assets are untouched.

Result

Widening the mirror keeps 9 of the 16 logos (argo, containerssh, csi, dapr, helm, kube-ovn, kubevirt, kyverno, metallb — now served locally). The remaining 7 are projects with no CNCF-hosted artwork (PowerDNS, NetBox x2, Kueue, ExternalDNS, SDC x2) and fall back to the badge.

docs/architectures/ is regenerated so grep -c 'logo="http' docs/architectures/*.md is now 0.

Verification

  • npm run test:unit — 67/67 pass, including all validators against the regenerated docs
  • 12 new unit tests in tests/project-assets.test.mjs cover local/mirrored/remote/traversal/lookalike-host cases
  • npx prettier --check clean on all changed source files
  • All 9 newly mirrored SVGs scanned for <script>, on*=, javascript: and <foreignObject> — clean

Closes #248


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

Imported architecture cards took their logo straight from upstream
Markdown. projectAsset() only rewrote the narrow cncf/artwork
icon/color shape and returned every other URL unchanged, so 16 of the
generated cards hot-linked hosts the CNCF does not control
(upload.wikimedia.org, raw.githubusercontent.com/netbox-community,
kubernetes-sigs.github.io, landscape.cncf.io). Every visitor to those
pages disclosed IP, User-Agent and Referer to those hosts, and the
artwork itself was mutable by its owners.

Add scripts/lib/project-assets.mjs, which resolves cncf/artwork URLs
(both raw.githubusercontent.com and github.com/.../raw/ forms, any
variant, not just icon/color) to a mirrored local path and returns
null for anything else. projectAsset() now fails closed and never
returns a remote URL; renderProjectCards() omits logo= when there is
no local asset, and CNCFProjectCard already renders a first-letter
fallback badge in that case. cleanMarkdown() handles the null return
by emitting a plain link.

Widening the mirror keeps 9 of the 16 logos by mirroring them from
cncf/artwork; the remaining 7 have no CNCF-hosted artwork and now use
the fallback badge. docs/architectures is regenerated accordingly, so
no generated card references a remote logo.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: kubestellar-hive[bot] <kubestellar-hive[bot]@users.noreply.github.com>
@kubestellar-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.

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

Labels

Projects

None yet

0 participants