Repository navigation
[architect] Six more duplicated https-URL-safety validators left uncentralized after #1381's checkUrl() consolidation #1388
Description
Architecture Finding
Type: coupling / incomplete-abstraction (duplicated security-relevant validation logic)
Affected area: scripts/validate-*.mjs, scripts/lib/project-card-links.mjs, src/lib/profile-links.mjs
Open PR #1381 ("refactor: share https-URL validation across validator scripts")
consolidates the "parse as URL, require https:, reject a userinfo component
that can disguise the real host" check into a single checkUrl() helper in
scripts/lib/validate-utils.mjs, replacing independent copies in
validate-awards.mjs, validate-launch-metrics.mjs, validate-members.mjs,
validate-metrics.mjs, and validate-projects-born.mjs.
That consolidation is not complete. The identical pattern — new URL(value),
reject non-https: protocol, reject url.username/url.password, optionally
hold url.hostname to an allow-list — still exists as independent, drift-prone
copies in six more places:
scripts/validate-case-studies.mjs—publishableUrl()(host allow-list:cncf.io)scripts/validate-radar-reports.mjs—publishableUrl()(host allow-list:cncf.io, and its own comment says it "mirrorsscripts/validate-case-studies.mjs")scripts/validate-community-groups.mjs—publishableRepositoryUrl()(host allow-list:github.com, comment says it "mirrorspublishableUrl()invalidate-case-studies.mjsandisCncfProjectHref()inlib/project-card-links.mjs")scripts/validate-architectures.mjs—isHttpsUrl()(no host allow-list)scripts/lib/project-card-links.mjs—isCncfProjectHref()(host allow-list:cncf.io, plus a path-prefix check)src/lib/profile-links.mjs—websiteUrl()(scheme-optional variant: a bare host is coerced tohttps://, and non-httpsprotocols inALLOWED_PROTOCOLSare kept rather than rejected)
Each of these files' own comments already point at the sibling copies by name —
the duplication is known and acknowledged in-tree, it just hasn't been
centralized. checkUrl()'s options.allowedHostSuffixes already covers the
cncf.io/github.com allow-list cases; isCncfProjectHref()'s path-prefix
check and websiteUrl()'s scheme-optional coercion are the two call sites that
need an option added to checkUrl() rather than a drop-in call.
Impact
Six copies of a security-relevant parser (guarding against userinfo-authority
spoofing such as https://cncf.io@evil.example/) can silently drift from each
other and from the now-canonical checkUrl(), the same way validate-metrics.mjs
had a host allow-list the other four pre-#1381 copies lacked. A future
hardening (e.g. tightening the allow-list, rejecting IDN homograph hosts) has to
be remembered and re-applied at up to seven call sites instead of one.
Recommendation
Once #1381 merges checkUrl() into scripts/lib/validate-utils.mjs, extend it
(e.g. an options.pathPrefix and an options.coerceScheme/defaultScheme
option) and replace the six remaining local implementations with calls to it:
-
scripts/validate-case-studies.mjs -
scripts/validate-radar-reports.mjs -
scripts/validate-community-groups.mjs -
scripts/validate-architectures.mjs -
scripts/lib/project-card-links.mjs -
src/lib/profile-links.mjs
This is a single follow-up PR (one mechanical pattern, six sites), not six
separate PRs. It is blocked on #1381 merging first, since checkUrl() does not
exist on main yet — filing now so the remaining call sites are tracked and
the follow-up is mechanical once the base helper lands.
Filed by architect agent (ACMM L5 — hold-gated mode)
🐝 Hive Agent: architect | Instance: hosted-available-lke648397-260827-5n31 | SHA: f5586b0
— hive: agent=architect backend=copilot model=claude-opus-4-6 copilot=1.0.88
Activity
mrbobbytables commentedon Oct 13, 2026
Kept open as the follow-up to #1381 (merged; closed #1380). Note for whoever picks this up: #1406 deliberately reuses
checkUrl()for the end-user-talks gate rather than adding a seventh copy, so the remaining list here is unchanged.hivecommons-hive commentedon Oct 13, 2026
task-list sweep: 0 of 6 items ticked. Not closing yet — outstanding boxes remain.
Outstanding items:
scripts/validate-case-studies.mjsscripts/validate-radar-reports.mjsscripts/validate-community-groups.mjsscripts/validate-architectures.mjsscripts/lib/project-card-links.mjssrc/lib/profile-links.mjsMerged PRs referencing this issue so far:
This comment is edited in place by the task-list sweep on every cycle; it is not duplicated.
refactor: centralize https-URL safety checks through checkUrl()/isSaf…
hivecommons-hive commentedon Oct 13, 2026
Verification pass (scanner, 2026-10-13) against main @40c6e97:
validate-case-studies.mjs,validate-radar-reports.mjs,validate-community-groups.mjs,validate-architectures.mjs, andscripts/lib/project-card-links.mjsall delegate tocheckUrl()/isSafeUrl().src/lib/profile-links.mjsstill carried its own copy — remainder implemented in refactor: centralize remaining URL-safety validators on the shared core (#1388, #1421) #1420 (held for human review):checkUrl()/isSafeUrl()move into a new fs-freescripts/lib/url-safety.mjs(re-exported fromvalidate-utils.mjs, so no validator import changes), adefaultSchemeoption coverswebsiteUrl()'s bare-host coercion, and the browser bundle builds cleanly with the shared import.New related finding filed separately:
scripts/validate-enduser-members.mjscarries a seventh local copy that neither #1381 nor this issue's six named (its messages and null-handling differ from the shared contract, so it needs its own pass).🐝 Hive Agent:
scanner| Instance:hosted-available-lke648397-260827-5n31| SHA:unknown— hive: agent=scanner backend=copilot model=kimi-k3 copilot=1.0.88
mrbobbytables commentedon Oct 13, 2026
Superseded by #1421 for the remaining validator; both are now closed by #1420.
refactor: centralize remaining URL-safety validators on the shared co…
refactor: centralize the cncf.io host allow-list behind CNCF_HOST_SUF…