Skip to content

[sec-check] fix: validate imported project card href by URL, not substring (scripts/import-architectures.mjs renderProjectCards + scripts/lib/project-card-links.mjs) - #249

Open
kubestellar-hive[bot] wants to merge 1 commit into
mainfrom
sec/card-href-allowlist

Conversation

@kubestellar-hive

Copy link
Copy Markdown
Contributor

Security Fix

renderProjectCards() in scripts/import-architectures.mjs chose the link target for
every published <CNCFProjectCard> with a substring test:

links.find((link) => link.includes('cncf.io/projects/'))

String.prototype.includes matches anywhere in the URL, so any host satisfies it by
putting that text in a path, query or fragment — verified on main @ 00b44df:

$ node -e '
const content = `[docs](https://evil.example/r?u=cncf.io/projects/kubernetes/)`;
const links = [...content.matchAll(/\]\((https?:\/\/[^)]+)\)/g)].map(m=>m[1]);
console.log(links.find(l=>l.includes("cncf.io/projects/")));'
https://evil.example/r?u=cncf.io/projects/kubernetes/

Card content comes from cncf/architecture, which
.github/workflows/import-architectures.yml clones and imports unattended every day,
so upstream Markdown could aim a CNCF-branded project card at an arbitrary origin.

What this changes

Claimed ground: scripts/import-architectures.mjsrenderProjectCards() href
selection only
, plus the new scripts/lib/project-card-links.mjs and
tests/project-card-links.test.mjs.

  • scripts/lib/project-card-links.mjs (new) — isCncfProjectHref() parses the URL
    with new URL() and requires https:, no userinfo, a host equal to cncf.io or
    ending in .cncf.io, and a path under /projects/.
  • scripts/import-architectures.mjsrenderProjectCards() selects the href with
    links.find(isCncfProjectHref); the existing https://www.cncf.io/projects/<slug>/
    fallback is untouched, so a rejected upstream link degrades to the canonical CNCF URL
    rather than disappearing.
  • tests/project-card-links.test.mjs (new) — 7 tests covering the bypass URL above,
    lookalike hosts (cncf.io.evil.example, notcncf.io), userinfo disguise
    (https://www.cncf.io@evil.example/...), http:// downgrade, javascript:, CNCF
    URLs outside /projects/, and non-URL input.

Verification

  • node --test tests/62/62 pass (55 before, 7 new).
  • No generated-output change: all 50 href= values currently in
    docs/architectures/*.md satisfy the new predicate (checked programmatically,
    0 rejected), so the next import produces identical cards.
  • npx prettier --check clean on all three files.

Disjointness

Closes #247


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

…tring

renderProjectCards() chose each published <CNCFProjectCard> link target with
link.includes('cncf.io/projects/'), which any host satisfies by putting that
text in a path, query or fragment.  Card content comes from cncf/architecture
and is imported unattended by the daily import workflow, so upstream Markdown
could point a CNCF-branded card at an arbitrary origin.

Add scripts/lib/project-card-links.mjs with isCncfProjectHref(), which parses
the URL and requires https, no userinfo, a cncf.io (or subdomain) host and a
/projects/ path, and use it to select the href.  All 50 cards currently in
docs/architectures/ already satisfy the predicate, so generated output is
unchanged.

Signed-off-by: kubestellar-hive[bot] <kubestellar-hive@hive.kubestellar.io>
@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