[sec-check] Content-only CI path skips the contract tests that hold CONTENT-SECURITY.md to the gates it documents
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 68/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- github-actions, javascript
Research direction
Read .github/workflows/ci.yml around the “Check content contracts” step, then inspect package.json for the test:unit script and tests/content-only-ci-workflow.test.mjs for the existing workflow assertion. Update the focused path to run the unit tests without coverage, and adjust the guard assertion to match. Done when document-only changes run those tests and the workflow test passes.
Written by the indexing model from the issue text.
Description
Security Finding
Severity: medium
Type: unsafe-pattern (CI gate does not cover the path it guards)
ADR 0011 decides that the focused path must "Run the applicable document-contract checks" (line 67) and only "Skip full unit coverage" (line 70). The step that implements it runs three tests:
# .github/workflows/ci.yml:109-114
- name: Check content contracts
if: ${{ !cancelled() && env.CONTENT_ONLY == 'true' }}
run: >-
node --test tests/docs-contract.test.mjs
tests/markdown-references.test.mjs
tests/community-group-coverage.test.mjs
The repository has roughly two dozen other unit tests whose subject is a committed authored document — exactly the documents isAuthoredDocumentPath() admits. None of them runs on the focused path, so a PR that touches only those documents can change what they say with fully green CI.
Reproduced on c686990
1. CONTENT-SECURITY.md — the submitter-facing statement of the active-content allowlists.
tests/content-security-contract.test.mjs exists precisely to stop that document drifting from scripts/lib/mdx-active-content.mjs, svg-active-content.mjs and uri-safety.mjs. CONTENT-SECURITY.md is in ROOT_DOCUMENTS (scripts/lib/content-only-ci.mjs), so editing it alone classifies as document-only and that test does not run.
Adding `iframe` to the "Inert inline HTML elements" bullet (line 235) and running npm run fix:format:
classifyChanges('pull_request', <sha>, [{status:'M', path:'CONTENT-SECURITY.md'}])→{"documentOnly":true,"documents":["CONTENT-SECURITY.md"]}- focused lint →
prettier"All matched files use Prettier code style!",cspell"Issues found: 0",markdownlintclean - focused contracts →
tests 19 / pass 19 / fail 0 node --test tests/content-security-contract.test.mjs→fail 1,AssertionError … + 'iframe',
So the page a contributor is sent to when a gate rejects their submission can claim <iframe> is inert while ALLOWED_ELEMENTS still rejects it, and nothing on the focused path objects.
2. CONTRIBUTING.md — also in ROOT_DOCUMENTS. Changing the first `npm run check:unused` to `npm run check:unusedX` leaves the focused path green but fails 4 assertions in tests/contributing-just-build-parity.test.mjs (lines 210, 272, 284) and tests/dev-environment.test.mjs (line 241).
Impact
Security- and process-documentation can state something the code does not enforce, and the anti-drift tests written to prevent exactly that are skipped on the only changes that can cause it. An undocumented allowance is a hole nobody reviewed; a documented allowance the gate does not grant rejects submissions the page says are fine. This is the failure mode tests/content-security-contract.test.mjs was written to stop.
This is not a classifier weakness — classifyChanges() is fail-closed on symlinked .md, type-change (T), rename (R100), .. segments, backslashes, absolute paths, non-existent files, docs/architectures/** pages and empty changesets, all verified on c686990.
Recommendation
Replace .github/workflows/ci.yml:109-114 with:
- name: Check content contracts
if: ${{ !cancelled() && env.CONTENT_ONLY == 'true' }}
run: npm run test:unit
npm run test:unit is node --test with no coverage instrumentation: 3791 passing, 94 s on c686990, against the ~15–20 min of browser suites and the coverage/validator/lint work the focused path drops. It stays inside ADR 0011 (coverage is still skipped) and it cannot go stale, which is the maintenance burden ADR 0011 line 73 already anticipated for a hand-maintained list.
Decision taken by hive: recommend npm run test:unit over enumerating the ~two dozen document-bound tests, as the simplest option that cannot drift. If an explicit list is preferred, it must at minimum add tests/content-security-contract.test.mjs, tests/contributing-just-build-parity.test.mjs, tests/dev-environment.test.mjs, tests/root-docs-links.test.mjs, tests/adr-contract.test.mjs and tests/governance-hygiene-parity.test.mjs.
A guard assertion belongs in tests/content-only-ci-workflow.test.mjs, which already pins this step's if: condition.
This fix needs a human or an ISSUES_PRS_MERGE agent to land. The change is confined to .github/workflows/ci.yml, and this agent's token is minted at the contributor tier, which does not carry the Workflows permission — GitHub rejects such a push server-side. No part of the fix lives outside the workflows directory, so there is no accompanying PR.
🐝 Hive Agent: security | Instance: hosted-available-lke648397-260827-5n31 | SHA: c686990
— hive: agent=sec-check backend=copilot model=claude-opus-5 copilot=1.0.88
- Dominant language
- JavaScript
- Stars
- 0
- Forks
- 3
- Avg merge
- 7h 8m
- Merged PRs (30d)
- 565
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from cncf/endusers
-
[guide] AGENTS.md claims the coverage gate, e2e and full lint run on every PR after ADR 0011 focused validationPossibly taken @hivecommons-hive claimed this today. Openagent/guide documentation hive/claimed-by-guide stage/040-covered-by-pr
Difficulty 2/5 1-3 hours Newbie friendliness 81/100
cncf/endusers#2006 · 1 comment ·
Maintainers usually reply within 1 day
-
[quality] Content-only CI path skips the document-contract suites that guard authored documentsPossibly taken @mrbobbytables claimed this today. Openagent/quality stage/010-actionable testing
Difficulty 2/5 1-3 hours Newbie friendliness 25/100
cncf/endusers#2002 · 4 comments ·
Maintainers usually reply within 1 day
-
a11y: search button accessible name does not contain its visible label (WCAG 2.5.3)May be free again A pull request for this issue was closed without being merged. Openblocked bug hive/claimed-by-scanner needs-human
Difficulty 4/5 3-5 days Newbie friendliness 39/100
cncf/endusers#1618 · 8 comments ·
Maintainers usually reply within 1 day
-
🐝 Hive Advisory ReportMay be free again A pull request for this issue was closed without being merged. Openhive/advisory
Difficulty 5/5 Over a week Newbie friendliness 15/100
cncf/endusers#23 · 9 comments ·
Maintainers usually reply within 1 day
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
danilogiles/enki-browser#113 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
ux
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
evcc-io/evcc#35605 · 1 comment ·
Maintainers usually reply within 1 day
-
bug triage
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 95/100
weavefox/awesome-opc#29 ·
Maintainers usually reply within 1 day