{"record":{"id":"a14260f54f4c553f","repo":"facebook/react","slug":"react-has-blocked-a-javascript-url-as-a-security","errorCode":null,"errorMessage":"React has blocked a javascript: URL as a security precaution.","messagePattern":"React has blocked a javascript: URL as a security precaution\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-dom-bindings/src/shared/sanitizeURL.js","lineNumber":29,"sourceCode":"// and any newline or tab are filtered out as if they're not part of the URL.\n// https://url.spec.whatwg.org/#url-parsing\n// Tab or newline are defined as \\r\\n\\t:\n// https://infra.spec.whatwg.org/#ascii-tab-or-newline\n// A C0 control is a code point in the range \\u0000 NULL to \\u001F\n// INFORMATION SEPARATOR ONE, inclusive:\n// https://infra.spec.whatwg.org/#c0-control-or-space\n\nconst isJavaScriptProtocol =\n  /^[\\u0000-\\u001F ]*j[\\r\\n\\t]*a[\\r\\n\\t]*v[\\r\\n\\t]*a[\\r\\n\\t]*s[\\r\\n\\t]*c[\\r\\n\\t]*r[\\r\\n\\t]*i[\\r\\n\\t]*p[\\r\\n\\t]*t[\\r\\n\\t]*\\:/i;\n\nfunction sanitizeURL<T>(url: T): T | string {\n  // We should never have symbols here because they get filtered out elsewhere.\n  // eslint-disable-next-line react-internal/safe-string-coercion\n  if (isJavaScriptProtocol.test('' + (url as any))) {\n    // Return a different javascript: url that doesn't cause any side-effects and just\n    // throws if ever visited.\n    // eslint-disable-next-line no-script-url\n    return \"javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')\";\n  }\n  return url;\n}\n\nexport default sanitizeURL;\n","sourceCodeStart":11,"sourceCodeEnd":35,"githubUrl":"https://github.com/facebook/react/blob/eafeac097ba51e1eab809c07102126bd5f8e5425/packages/react-dom-bindings/src/shared/sanitizeURL.js#L11-L35","documentation":"sanitizeURL does not throw during rendering; when a URL attribute (href, src, action, formAction, ...) coerces to a string matching the javascript: protocol — including obfuscated forms with control characters, spaces, and whitespace embedded between the letters — React replaces the URL with javascript:throw new Error('React has blocked a javascript: URL as a security precaution.'). The actual Error is thrown in the browser only if that sanitized URL is ever activated (link clicked, form submitted, window opened), neutralizing XSS via URL attributes.","triggerScenarios":"Server-rendering href={`javascript:${code}`}, a link whose href comes from user/CMS content like <a href={user.website}> where user.website is 'javascript:alert(1)', or formAction/action/img src values that begin with javascript: after the leading-control/space prefix the regex allows for.","commonSituations":"Markdown or rich-text renderers emitting unfiltered URLs; user profile fields rendered as links; test/security scanners probing the sanitizer; accidentally shipping javascript:void(0) hrefs that get blocked in SSR output.","solutions":["Filter URLs at the source: allowlist schemes (http, https, mailto, tel, relative) before rendering user-supplied hrefs","Use a sanitizer such as DOMPurify or isomorphic-url parsing to validate the protocol and reject non-http(s) links","Replace javascript:void(0) placeholder hrefs with href=\"#\" role/button semantics or a <button>"],"exampleFix":"// before\n<a href={user.website}>Profile</a> // user.website = \"javascript:alert(1)\"\n\n// after\nconst SAFE_URL = /^(https?:|mailto:|tel:|#|\\/|\\.\\/)/i;\nconst href = SAFE_URL.test(user.website) ? user.website : '#';\n<a href={href}>Profile</a>","handlingStrategy":"validation","validationCode":"const SAFE_HREF = /^(?:(?:https?|mailto|tel):|#|\\/|\\.\\.?\\/)/i;\nfunction safeHref(url: string | null | undefined): string {\n  if (url == null) return '#';\n  const trimmed = url.trim();\n  return SAFE_HREF.test(trimmed) ? trimmed : '#';\n}\n// <a href={safeHref(user.website)}>","typeGuard":"function isSafeUrl(url: string): boolean {\n  try {\n    const u = new URL(url, 'https://example.invalid');\n    return ['http:', 'https:', 'mailto:', 'tel:'].includes(u.protocol);\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":"// The throw fires only in the browser if a sanitized URL is activated; catch it there for telemetry:\nwindow.addEventListener('error', (e) => {\n  if (/React has blocked a javascript: URL/.test(String(e.message))) {\n    reportSecurityEvent('blocked-javascript-url', e.message);\n  }\n});","preventionTips":["Allowlist URL schemes at the data boundary for any user-supplied href/src/action","Run SSR output through a security scan/CI check that greps for 'javascript:' in emitted HTML","Prefer real buttons over javascript:void(0) link placeholders"],"tags":["security","xss","url-sanitization","javascript-url","ssr"],"backgroundTag":"javascript-url-blocked","analyzedSha":"eafeac097ba51e1eab809c07102126bd5f8e5425","analyzedAt":"2026-08-21T22:01:08.818Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}