Repository navigation
fix(security): consolidated hardening of data validators and URL gates - #753
Merged
mrbobbytables merged 10 commits intoSep 27, 2026
Merged
Conversation
The imported-page gate reported disallowed elements, event handlers, script-capable URL schemes and unexpected ESM statements, but never modelled the one MDX construct that is JavaScript by definition: a braced expression. Docusaurus compiles docs/architectures/*.md as MDX, and those bodies are written verbatim from a third-party repository, so upstream text could ship arbitrary script into the published origin. Flag every remaining brace after code spans and fences are blanked, allowing only the inert string-literal attribute form the importer emits itself. Only the braces of that form are neutralized, so a script URI smuggled into a prop value is still reported. Signed-off-by: sec-check <sec-check@hive.kubestellar.io>
data/projects-born.json was the only hand-maintained data file feeding an <a href> with no validator behind it. src/components/ProjectsBorn renders each entry's url verbatim, and that component is mounted in src/theme/Footer, so the links ship on every page of the site. Add scripts/validate-projects-born.mjs, which parses every url and rejects non-https, userinfo-bearing and unparseable values, and requires name, origin and description to be non-empty and names to be unique. Parsing rather than prefix-testing matches checkHttpsUrl() in validate-awards.mjs and checkUrl() in validate-metrics.mjs: /^https:\/\// accepts "https://www.cncf.io@evil.example/", whose visible prefix and real host disagree. No host allow-list, since these are legitimately third-party project sites. Registering the script in READ_ONLY_VALIDATORS is what puts it on the pull request gate; tests/validator-smoke-coverage.test.mjs already enforces that every scripts/validate-*.mjs appears there. Signed-off-by: hivecommons-hive[bot] <hivecommons-hive@hive.kubestellar.io>
…e origin scripts/validate-architecture-assets.mjs states that it gates every directory published verbatim at the site origin, but assetDirs listed only static/img/architectures, static/img/cncf-projects and static/img/awards. static/img itself and static/favicons were never walked, so the five SVGs served from them - including the footer logo and both favicons - reached the origin with no active-content check and no extension allow-list. SVG is a document format: a browser that navigates directly to one parses it as XML and executes any script it carries, in the site's own origin. A pull request refreshing a logo or favicon therefore had no automated gate, while the identical file one directory deeper was rejected. Add both roots. static/img is walked shallowly because its image subdirectories are already listed with their own quality settings, so walk() takes a recurse flag; the symlink check runs before the directory branch so a symlinked directory is still reported in a shallow walk. static/img also serves the legacy favicon.ico, so the two chrome roots use an extension set of ALLOWED_ASSET_EXTENSIONS plus .ico - kept separate because ALLOWED_ASSET_EXTENSIONS mirrors what the importer mirrors, and the importer never writes an .ico. ICO is a raster container no browser parses as markup. static/fonts and the static/ root stay outside the gate: no SVG, and extensions legitimately outside the image allow-list. The five previously ungated SVGs already pass unchanged; this is a gate-only change. Coverage goes from 69 to 82 published assets. Signed-off-by: hivecommons-hive[bot] <hivecommons-hive@hive.kubestellar.io>
The new shallow walk of static/img enumerated static/img/architectures, static/img/cncf-projects and static/img/awards as ordinary entries. Each of those is already an asset root with its own entry in assetDirs, which decides for itself whether a symlink is an error and whether a non-directory root is skipped. Re-entering them from the parent walk duplicated the symlink error and, where a root was a regular file rather than a directory, rejected it as an extensionless asset - breaking the pre-existing 'a regular file at an asset root is skipped, not walked' test. Skip any entry whose path is itself a declared asset root. Signed-off-by: hivecommons-hive[bot] <hivecommons-hive@hive.kubestellar.io>
…ithub.com validate-community-groups.mjs checked group.repository with a bare new URL() parse, which succeeds for javascript:, data:, cleartext http, and a userinfo-spoofed authority such as https://github.com@evil.example. It was the only URL gate in data/ that decided nothing about the destination beyond parseability, while validate-case-studies.mjs, validate-radar-reports.mjs, validate-awards.mjs, validate-architectures.mjs and lib/project-card-links.mjs all require https, reject userinfo, and pin the host. Require https, reject userinfo, and pin the host to github.com, which is the only host check-community-group-links.mjs ever writes. The missing-field branch still reports an absent repository on its own. Signed-off-by: hivecommons-hive[bot] <hivecommons-hive@hive.kubestellar.io>
… just rostered ones scripts/validate-community-people.mjs drove its person-level loop from data/community-roster.json's section keys, while src/components/CommunityPeople renders data/community-people.json's. A section present only in the generated file therefore skipped every check in the loop body, including the profileImageUrl() host gate, and reached <img src> with an arbitrary host. The loop now runs over the union of both files' section keys, and a generated section the roster does not declare is an error in its own right. Signed-off-by: hivecommons-hive[bot] <hivecommons-hive@hive.kubestellar.io>
data/members.json renders as <a href> on /community/members via MemberProfile.js, but it has no validator script: there is no validate:members, nothing for it in ci.yml's validator list, and it is absent from READ_ONLY_VALIDATORS in validators-smoke.test.mjs. Its only gate was the /^https:\/\// prefix regex in members-data.test.mjs. That is the test validate-awards.mjs and validate-architectures.mjs already document as insufficient: "https://www.cncf.io@evil.example/" matches the prefix while resolving to evil.example, and the bare string "https://" matches while failing to parse at all. The awards cross-check does not compensate, because it compares only the slug/year/award key and never the URL values. Gate architectures[].sourceUrl, the three award URLs and every sourceAttribution entry on a parsed URL that must use https and must carry no userinfo, mirroring isHttpsUrl() in validate-architectures.mjs, and cover the spoofed forms. Signed-off-by: hivecommons-hive[bot] <hivecommons-hive@hive.kubestellar.io>
This was referenced Sep 27, 2026
Closed
The consolidated security fixes add the validate:projects-born gate, and main now asserts that every validate:*/check:* script is run by a workflow or recorded as exempt. Wire the new gate into ci.yml alongside the other data validators, which also unblocks the narrow coverage-report runs that execute workflow-scripts.test.mjs as a fixture. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Bob Killen <bkillen@linuxfoundation.org>
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 cncf#752 Signed-off-by: sec-check <sec-check@hive.kubestellar.io>
mrbobbytables
added a commit
to mrbobbytables/endusers
that referenced
this pull request
Sep 29, 2026
CONTRIBUTING.md's data contribution model claimed twice that data/projects-born.json has no validation script. PR cncf#753 added scripts/validate-projects-born.mjs and wired npm run validate:projects-born into CI, but no doc mentioned it. Updated the table row and the rules bullet to reference the validator. Fixes cncf#776 Signed-off-by: mrbobbytables <mrbobbytables@users.noreply.github.com> Co-authored-by: mrbobbytables <mrbobbytables@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Consolidates the seven open security-fix PRs into a single reviewable change. Every commit is an unmodified cherry-pick from its source PR (original authorship and DCO sign-offs preserved).
npm run test:unit: 1333 pass, 0 fail.Also wires the new
validate:projects-borngate into ci.yml's validate job, required by main's gate-wiring test (from #742/#750).Consolidated PRs
The source PRs above can be closed in favor of this one.
Note: this PR touches package.json (new validate:projects-born script from #684) and will need a trivial rebase if #674 merges first.
Closes #675, closes #683, closes #690, closes #716, closes #724, closes #730, closes #752