Skip to content

fix(security): hold data/metrics.json URLs to a host allow-list - #756

Closed
hivecommons-hive[bot] wants to merge 1 commit into
mainfrom
sec/fix-metrics-host
Closed

hivecommons-hive[bot] wants to merge 1 commit into
mainfrom
sec/fix-metrics-host

Conversation

@hivecommons-hive

Copy link
Copy Markdown
Contributor

Security Fix

scripts/validate-metrics.mjs was the only validator guarding a rendered href
that applied no host allow-list. Its checkUrl() asserted an absolute URL,
protocol === 'https:', and the absence of userinfo — and stopped. Any host
satisfying those passed, so https://evil.example/x was a valid value for every
URL field in data/metrics.json.

Those fields are all rendered as links whose anchor text is hard-coded, so
the visible label and the real destination are independent:

  • src/components/ReferenceArchitectures/index.js:27 — text cncf/architecture, href sources.architectures.repository
  • src/components/ReferenceArchitectures/index.js:31 — text is the short revision, href `${repository}/commit/${revision}`
  • src/components/MetricsDashboard/index.js:55,111,132,184 — text Source ↗, href referenceArchitectureLifecycle.sourceUrl / metric.sourceUrl / series.sourceUrl / chart.sourceUrl

MetricsDashboard also prints fixed copy reading "collected from public CNCF
repositories", asserting a provenance nothing checked.

This is the sink shape the repository already decided needs an allow-list rather
than a scheme test: scripts/validate-case-studies.mjs:14 and
scripts/validate-radar-reports.mjs:15 both pin
ALLOWED_HOST_SUFFIXES = ['cncf.io'] for precisely this reason.
validate-metrics.mjs never got that half of the treatment.

What changed

scripts/validate-metrics.mjs only:

  • adds ALLOWED_HOST_SUFFIXES = ['cncf.io', 'github.com'],
  • checks the host (exact match or dot-bounded suffix) after the existing
    userinfo check, and makes that check return so a spoofed-userinfo URL keeps
    its more specific diagnostic instead of gaining a second, vaguer one.

github.com is in the list because sources.*.repository, sources.*.sourceUrl
and several metric.sourceUrl values legitimately point at
https://github.com/cncf/....

Every host in data/metrics.json today is already github.com or
landscape.cncf.io, so this is a no-op for the current data —
npm run validate:metrics still reports Validated 2 metrics.

Side effect worth noting: sources.*.revision is unvalidated and is
concatenated into `${repository}/commit/${revision}`. With repository
host-pinned, an arbitrary revision can now only vary the path on an allowed
host rather than the origin.

Tests

New file tests/validate-metrics-host-allowlist.test.mjs (10 cases), kept
separate from tests/validate-metrics.test.mjs so this does not collide with
the open test PRs on that file:

  • accepts github.com and cncf.io subdomains across every URL field
  • rejects an off-allow-list host at each of the six checkUrl() call sites
  • rejects notcncf.io and evil-github.com — suffix matching is dot-bounded,
    not a bare endsWith
  • asserts a userinfo-spoofed URL still reports the userinfo error and not the
    allow-list error
  • asserts the committed data/metrics.json satisfies the allow-list

Verification on this branch:

  • TZ=UTC node --test tests/validate-metrics-host-allowlist.test.mjs tests/validate-metrics.test.mjs — 38/38 pass
  • TZ=UTC node tests/tools/coverage-report.mjs --check 97 --check-source 99 — exit 0; scripts/validate-metrics.mjs at 100.00% lines / 98.11% branch
  • npx prettier --check on both touched files — clean
  • npm run validate:metrics — Validated 2 metrics

Scope / non-overlap

Touches exactly two paths: scripts/validate-metrics.mjs and the new
tests/validate-metrics-host-allowlist.test.mjs. No open PR touches either.
scripts/validate-launch-metrics.mjs has the same shape but
data/launch-metrics.json is not rendered by any component, so it has no href
sink and is deliberately out of scope.

Closes #752


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

scripts/validate-metrics.mjs was the only validator guarding a rendered
href that applied no host allow-list: checkUrl() asserted an absolute
https URL with no userinfo and stopped, so any host passed.

Every URL in data/metrics.json is rendered under hard-coded anchor text
-- 'cncf/architecture' in src/components/ReferenceArchitectures, 'Source'
in src/components/MetricsDashboard -- so a plain https URL on a host that
is not CNCF's publishes as a CNCF-labelled link to an unrelated origin.
That is the same sink shape validate-case-studies.mjs and
validate-radar-reports.mjs already pin with ALLOWED_HOST_SUFFIXES.

Pin the host to cncf.io or github.com after the existing userinfo check,
which keeps the more specific userinfo diagnostic for a spoofed-userinfo
URL. Both hosts are already the only ones present in data/metrics.json,
so this is a no-op for the current data.

Closes #752

Signed-off-by: sec-check <sec-check@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.

@mrbobbytables

Copy link
Copy Markdown
Member

Superseded by #753, which consolidates the open security-fix PRs (commit cherry-picked unmodified, authorship and DCO preserved).

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] validate-metrics.mjs applies no host allow-list, so any https host in data/metrics.json publishes as a CNCF-labelled link

1 participant