fix(security): client href scheme check, outbound fetch timeouts, origin-scoped CORS (closes task/resolver-security-hardening)

Source: task/resolver-security-hardening (plan/steward-linker-security) — defense-in-depth: client trusted resolver href, outbound fetches had no timeout, CORS was blanket-*.

- F1: extension/content.js makeLink re-validates primary.url scheme via a new
  pure Extract.isSafeHttpUrl helper (in extract.js, unit-tested); a non-http(s)
  url (javascript:/data:/garbage) is dropped and the plain text is kept.
- F2: each of the three resolver outbound fetches (Spotify token, Spotify
  search, ollama /api/chat) now carries signal: AbortSignal.timeout(5000) so a
  hung upstream fails fast (caught per-candidate -> null), zero-dep.
- F3: resolver CORS reflects the request Origin only for moz-extension://* or
  null (the extension's background-script origin), with Vary: Origin, instead
  of blanket access-control-allow-origin: *.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
paul
2026-06-23 02:29:42 +00:00
co-authored by Claude Opus 4.8
parent 8496e55baf
commit 7adb99231a
4 changed files with 55 additions and 5 deletions
+15 -1
View File
@@ -119,5 +119,19 @@
return all;
}
return { matchesIn, cueMatchesIn, albumMatchesIn, allMatchesIn };
// True only for http/https URLs — reject javascript:, data:, garbage, empty.
// The resolver's url flows into an injected link's href, so the content script
// re-validates the scheme client-side before assigning it (defense in depth; the
// resolver already validates server-side, but a swapped/buggy one must not inject
// a javascript:/data: href into reddit's DOM).
function isSafeHttpUrl(u) {
try {
const p = new URL(u).protocol;
return p === "http:" || p === "https:";
} catch {
return false;
}
}
return { matchesIn, cueMatchesIn, albumMatchesIn, allMatchesIn, isSafeHttpUrl };
});