Deferred from #109 on the reviewer's own advice ("the rest can follow as issues"). Both halves are real; neither is fixable without care that would have rushed that PR.
1. Unresolvable authorities are dropped, not reported
scripts/validate-outbound.cjs returns null when an authority cannot be reduced to a host, and collectHosts skips it. Three live consequences, all real requests the check never mentions:
https://[2606:4700:4700::1111]/… — bracketed IPv6
https://gіthub.com/… — Cyrillic і; the browser punycodes it to an attacker domain, the check drops it
https://intranet-host/ — single label, no dot
Failing closed is the right instinct and was implemented and then reverted inside #109, because it immediately fired on eight legitimate patterns: ${hostHeader} for the local bind, ${LOCAL_BIND_HOST}:${port}, ${values.HTTPSProxy}, git-remote parsing in rollout.js, and a log string with an ANSI reset after the URL.
A check that flags ordinary code is a check someone switches off, so this needs per-pattern handling — most likely a small declared set of known-local interpolations — rather than a flag flip.
2. The walker sees two directories and one extension set
Named places a host could live and be missed:
| Where |
Why it matters |
dashboard/index.html, dashboard/pet.html |
The served SPA shell. A <link rel=preconnect>, font CDN or <script src> here is a browser request. Today they load only local assets — verified. |
dashboard/src/styles.css |
Inside a scanned directory, skipped by extension. CSS url() is a request. No url() today — verified. |
dashboard/src/content/i18n/*.json |
Same: strings that flow into href/src props. |
dashboard/vite.config.js |
Dev proxy target; a new one is invisible. |
scripts/build-pricing-seed.cjs |
Fetches raw.githubusercontent.com outside the scan. Maintainer-run today. |
| Half B |
Covers only dashboard/src. A server file adding fetch(cfg.someUrl) beside an existing host literal is unchecked, though the README's promise covers the server. |
Also: isTestFile exempts by filename, not by import graph. A module named analytics.test.js is importable by the app and exempt from the scan.
3. link_from is an unconditional per-file waiver
Once a file is on a host's link_from, any request to that host from it passes forever — including an <img src> added later.
The concrete scenario, which is the shape of #100: adding <img src={https://github.com/${skill.repoOwner}.png`} />toSkillDetailPanel.jsx— a plausible "show skill-author avatars" PR — passes today. That file holds agithub.com` waiver and already interpolates owner names.
Fix: pin each link_from entry to a line number or to the exact URL literal, so a new string in a waived file is a visible diff in the inventory rather than silently covered.
4. Trailing comments will produce false findings
COMMENT_LINE_RE matches only line-leading comments, so
const x = 5; // see https://github.com/nodejs/node/issues/123
classifies as a request. Reference links in trailing comments are common, and this is the likeliest source of pressure to disable the check.
Priority
4 first — it costs nothing and prevents the check being switched off, which would take the rest with it. Then 3, then 1, then 2.
Deferred from #109 on the reviewer's own advice ("the rest can follow as issues"). Both halves are real; neither is fixable without care that would have rushed that PR.
1. Unresolvable authorities are dropped, not reported
scripts/validate-outbound.cjsreturnsnullwhen an authority cannot be reduced to a host, andcollectHostsskips it. Three live consequences, all real requests the check never mentions:https://[2606:4700:4700::1111]/…— bracketed IPv6https://gіthub.com/…— Cyrillicі; the browser punycodes it to an attacker domain, the check drops ithttps://intranet-host/— single label, no dotFailing closed is the right instinct and was implemented and then reverted inside #109, because it immediately fired on eight legitimate patterns:
${hostHeader}for the local bind,${LOCAL_BIND_HOST}:${port},${values.HTTPSProxy}, git-remote parsing inrollout.js, and a log string with an ANSI reset after the URL.A check that flags ordinary code is a check someone switches off, so this needs per-pattern handling — most likely a small declared set of known-local interpolations — rather than a flag flip.
2. The walker sees two directories and one extension set
Named places a host could live and be missed:
dashboard/index.html,dashboard/pet.html<link rel=preconnect>, font CDN or<script src>here is a browser request. Today they load only local assets — verified.dashboard/src/styles.cssurl()is a request. Nourl()today — verified.dashboard/src/content/i18n/*.jsonhref/srcprops.dashboard/vite.config.jsscripts/build-pricing-seed.cjsraw.githubusercontent.comoutside the scan. Maintainer-run today.dashboard/src. A server file addingfetch(cfg.someUrl)beside an existing host literal is unchecked, though the README's promise covers the server.Also:
isTestFileexempts by filename, not by import graph. A module namedanalytics.test.jsis importable by the app and exempt from the scan.3.
link_fromis an unconditional per-file waiverOnce a file is on a host's
link_from, any request to that host from it passes forever — including an<img src>added later.The concrete scenario, which is the shape of #100: adding
<img src={https://github.com/${skill.repoOwner}.png`} />toSkillDetailPanel.jsx— a plausible "show skill-author avatars" PR — passes today. That file holds agithub.com` waiver and already interpolates owner names.Fix: pin each
link_fromentry to a line number or to the exact URL literal, so a new string in a waived file is a visible diff in the inventory rather than silently covered.4. Trailing comments will produce false findings
COMMENT_LINE_REmatches only line-leading comments, soclassifies as a request. Reference links in trailing comments are common, and this is the likeliest source of pressure to disable the check.
Priority
4 first — it costs nothing and prevents the check being switched off, which would take the rest with it. Then 3, then 1, then 2.