Repository navigation
[sec-check] Third-party data validators never run in CI: community-people/case-studies/radar-reports/community-groups gates are dead code at deploy time #523
Description
Security Finding
Severity: medium
Type: permission-issue / missing security gate (CI wiring)
scripts/validate-community-people.mjs, scripts/validate-case-studies.mjs,
scripts/validate-radar-reports.mjs and scripts/validate-community-groups.mjs
are the host/protocol gates on data this repository mirrors from third parties
and renders to every visitor. No workflow runs any of them.
Verified at 9c2d9d7:
.github/workflows/deploy-gh-pages.ymlruns exactly four validators —
validate:metrics,validate:architectures,validate:architecture-assets,
validate:awards— then builds and publishes..github/workflows/ci.ymlrunsnpm run test:unitandnpm run build:production
and no validator at all.npm run checkexpands to thecheck:*family only (format, markdown,
spelling, links, community-group-links) — it does not include anyvalidate:*
script, and no workflow invokesnpm run checkeither.
So the four gates above are dead code at deploy time. What still runs is weaker
than what they enforce.
Impact
data/community-people.json is regenerated unattended from
https://raw.githubusercontent.com/cncf/people/main/people.json, and
src/components/CommunityPeople/index.js:25,52 renders person.image as a raw
<img src> with no runtime gate. scripts/validate-community-people.mjs
resolves that field through profileImageUrl() (host allow-list, userinfo
rejected) precisely to catch a value "hand-edited in or cached from a run that
predates the gate in fetch-community-people.mjs" — its own comment. That check
never executes in CI.
The only image check that does execute is
tests/community-people-data.test.mjs:121, which asserts /^https:\/\/\S+$/ —
a prefix test, not a host check. Demonstrated on a clean checkout:
# data/community-people.json, one person's image set to:
# https://raw.githubusercontent.com@tracker.example/a.png (real host: tracker.example)
$ node --test tests/community-people-data.test.mjs
ℹ tests 12
ℹ pass 12
ℹ fail 0 <-- the gate CI actually runs accepts it
$ npm run validate:community-people
1 error(s) in community people:
[error] people.tab: <name> image must be an https URL on an allowed host:
"https://raw.githubusercontent.com@tracker.example/a.png"
<-- the gate CI never runs rejects it
An off-host <img src> on the community page is a third-party beacon: it
receives every visitor's IP, User-Agent and Referer on page load, and the
disguised-userinfo form reads as raw.githubusercontent.com to a human
skimming the generated JSON diff.
The same wiring gap covers the other three: validate:case-studies and
validate:radar-reports gate URLs mirrored from the cncf.io WordPress REST API
and rendered as <a href> by src/components/CaseStudies/index.js:49,190 and
src/components/RadarReports/index.js:15,32. Hardening merged into those
validators cannot protect the published site while nothing executes them —
which also means the host allow-list in open PR #501 lands as dead code unless
this is fixed.
All six uninvoked validators pass on today's data, so wiring them in is a no-op
for the current corpus and only catches drift:
validate:case-studies Validated 211 case studies
validate:radar-reports Validated 6 radar reports
validate:community-people Validated 16 community profiles
validate:community-groups Validated 2 End User Group links
validate:launch-metrics Validated 5 launch metrics
validate:button-contrast Button contrast validated for 4 theme backgrounds.
Recommendation
- Add the four validator steps to
.github/workflows/deploy-gh-pages.yml - Add the same four steps to
.github/workflows/ci.yml
Exact replacement text
1. .github/workflows/deploy-gh-pages.yml — insert immediately after the
existing Validate awards data step (before the # Build for the GitHub Pages project URL. comment):
- name: Validate case studies
run: npm run validate:case-studies
- name: Validate radar reports
run: npm run validate:radar-reports
- name: Validate community people
run: npm run validate:community-people
- name: Validate community group links
run: npm run validate:community-groups2. .github/workflows/ci.yml — insert immediately after the existing
Run unit tests step (before Build production site):
- name: Validate case studies
run: npm run validate:case-studies
- name: Validate radar reports
run: npm run validate:radar-reports
- name: Validate community people
run: npm run validate:community-people
- name: Validate community group links
run: npm run validate:community-groupsNothing else changes: no script, no package.json entry, no dependency. Both
blocks use npm run scripts that already exist and already pass.
Why there is no PR attached
The entire fix lives under .github/workflows/. This agent's token is minted at
the contributor tier, which does not carry the Workflows permission, so GitHub
rejects any push whose diff touches that directory. This needs a human
maintainer or an agent with workflow-write to land — "issue, no PR" here is a
hard tooling ceiling, not a judgement that the change is optional.
Disjointness
Claims .github/workflows/deploy-gh-pages.yml and .github/workflows/ci.yml
(CI step wiring only). No open PR touches either file: #195/#212 cover imported
architecture SVG/MDX validators, #266 project logos, #291 package.json,
#501 scripts/validate-case-studies.mjs, #508 scripts/validate-awards.mjs,
#510 prettier drift, #225/#435/#476/#479/#482/#486/#490/#496/#505/#512/#517
test files. This issue changes no validator's logic, so it cannot collide with
#501, #508 or #496 — it is what makes their work reachable.
Filed by sec-check agent (ACMM L4/L5 — hold-gated mode)
🐝 Hive Agent: security | Instance: hosted-available-lke648397-260827-5n31 | SHA: 9c2d9d7
— hive: agent=sec-check backend=copilot model=claude-opus-5
Activity
mrbobbytables commentedon Sep 23, 2026
Closing: the premise no longer holds. tests/validators-smoke.test.mjs executes all read-only validators against repo data, and npm run test:unit runs in both ci.yml and deploy-gh-pages.yml, so these validators run on every PR and every deploy.