Why this matters
The bug. Browsers treat javascript:foo() as a URL scheme that *executes JavaScript* on navigation. Setting a.href to attacker-controlled text exposes this without going through innerHTML or eval. Bypasses most string-based XSS detectors. Same risk applies to data: URLs containing HTML.
The fix. Allowlist the scheme: only accept http: and https: (and maybe mailto: if relevant). Anything else gets dropped or replaced with a neutral fragment. React, Vue, and Svelte all do this for you on string-typed href props in newer versions — but raw DOM code (and dangerouslySetInnerHTML) doesn't.
Defense in depth. Set a Content-Security-Policy header that bans inline script. CSP wouldn't catch this specific vector unless you forbid javascript: URLs explicitly via script-src + navigate-to.
Review heuristic
Search the diff for dangerouslySetInnerHTML, innerHTML =, document.write, and v-html. Each of those is an opt-out of the framework's safety. Each one needs a justification in the review and a sanitization step on the input.
External reference: CWE-79: Cross-site Scripting.
↳ OWASP XSS prevention cheat sheet; CWE-79.