fix: validate architecture catalog sourceUrl scheme and asset containment (scripts/validate-architectures.mjs) - #334
Open
kubestellar-hive[bot] wants to merge 1 commit into
Conversation
…sset containment scripts/validate-architectures.mjs is the only gate between data/architectures/catalog.json -- regenerated nightly from the third-party cncf/architecture repository -- and the published site, but it checked neither of the two fields that reach a render sink. - sourceUrl was not validated at all. generate-members.mjs copies it into members.json sourceAttribution, which MemberDirectory renders as an href with no scheme guard, so a javascript: value passed validation and reached the published site. - assets[] containment was never asserted, and the existence probe built its path with join(root, 'static', asset.replace(/^\//, '')), which normalises '..' away -- so the probe escaped static/ entirely. sourceUrl must now parse as an https: URL via new URL(). Assets must start with /img/architectures/ and, after resolution, still be contained in that directory -- resolve first, then assert the prefix, so '..' cannot normalise the guard away. id must be a lowercase slug, since it is used both as a route segment and as a filesystem path component. Verified: the clean catalog still validates (7 records, exit 0); a catalog carrying a javascript: sourceUrl, an escaping asset, an asset with interior '..', and a '../evil' id is rejected with four errors and exit 1. Signed-off-by: kubestellar-hive[bot] <kubestellar-hive@hive.kubestellar.io>
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 Hive will automatically remove the |
This was referenced Sep 20, 2026
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.
Security Fix
scripts/validate-architectures.mjsis the only gate betweendata/architectures/catalog.json— regenerated nightly from the third-partycncf/architecturerepository — and the published site. It checked neither ofthe two fields that reach a render sink.
sourceUrlwas not validated at all.scripts/generate-members.mjs:145-146copies it into
members.jsonsourceAttribution, whichsrc/components/MemberDirectory/index.js:196renders as anhrefwith noscheme guard. A
javascript:value passednpm run validate:architecturesandreached the published site.
assets[]containment was never asserted. The existence probe built itspath with
join(root, 'static', asset.replace(/^\//, '')), which normalises..away, so the probe escapedstatic/entirely.Changes (all in
scripts/validate-architectures.mjs)isHttpsUrl()—sourceUrlmust be present and parse as anhttps:URL vianew URL(), not a prefix or substring test.resolveContainedAsset()— everyassets[]entry must start with/img/architectures/and, after resolution, still be contained in thatdirectory. Resolve first, then assert the prefix, so
..cannot normalise theguard away.
existsSyncruns only on a contained path. This is the sameresolve-then-contain ordering [sec-check] validate-awards.mjs logo containment check is defeated by '..' — path escapes static/img/awards/ and existsSync probes the build runner #327 records for
validate-awards.mjs.idmust match/^[a-z0-9][a-z0-9-]*$/, since it is used both as a routesegment (
/architectures/<id>) and as a filesystem path component inimport-architectures.mjs.Verification
Clean catalog still validates:
A catalog carrying a
javascript:sourceUrl, an escaping asset, an asset withan interior
.., and a../evilid is now rejected:npx prettier --check scripts/validate-architectures.mjsfails identically onmainand on this branch — the file is part of the pre-existing 62-fileformatting baseline tracked in #305, so it is deliberately not reformatted here.
Scope
Touches exactly one file,
scripts/validate-architectures.mjs, and only thesourceUrl/assets[]/idvalidation cluster. Disjoint from the open workon
import-architectures.mjs(#195, #212, #216, #249, #266),validate-metrics.mjs(#304, #263),validate-awards.mjs(#329),validate-architecture-assets.mjs(#231) andgenerate-members.mjs(#208).Closes #332
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