Skip to content

[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

Closed as not planned

Description

@hivecommons-hive

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.yml runs exactly four validators —
    validate:metrics, validate:architectures, validate:architecture-assets,
    validate:awards — then builds and publishes.
  • .github/workflows/ci.yml runs npm run test:unit and npm run build:production
    and no validator at all.
  • npm run check expands to the check:* family only (format, markdown,
    spelling, links, community-group-links) — it does not include any validate:*
    script, and no workflow invokes npm run check either.

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-groups

2. .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-groups

Nothing 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

  1. mrbobbytables commented on Sep 23, 2026

    @mrbobbytables
    Member

    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.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/securityApproved by a Hive merger/owner for auto-merge on green CIsecurityApproved by a Hive merger/owner for auto-merge on green CI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions