{"version":3,"file":"markdown-url.cjs","names":[],"sources":["../../../src/components/Markdown/markdown-url.ts"],"sourcesContent":["/**\n * URL vetting for links and images in rendered Markdown.\n *\n * This is the security boundary of the whole component. Markdown that arrives from\n * a user — a comment, a ticket description, a review — reaches the DOM as a real\n * `href`, and `javascript:alert(1)` in a link is script execution on click. So the\n * scheme is checked against an allowlist rather than a blocklist: a blocklist has\n * to enumerate `javascript:`, `JaVaScRiPt:`, `java\\tscript:`, `&#106;avascript:`\n * and whatever comes next, and misses the one nobody thought of.\n */\n\n/** Schemes a link may use. Anything else is dropped. */\nconst LINK_SCHEMES = new Set([\"http:\", \"https:\", \"mailto:\", \"tel:\", \"sms:\"]);\n\n/**\n * Image `data:` types that are safe to inline.\n *\n * `svg+xml` is deliberately absent: an SVG is a document, it can carry `<script>`\n * and event handlers, and browsers execute them when it loads from a `data:` URL in\n * some contexts. The raster formats cannot carry code.\n */\nconst IMAGE_DATA_TYPES = /^data:image\\/(png|jpe?g|gif|webp|avif|bmp);base64,[a-z0-9+/=\\s]+$/i;\n\n/**\n * Strip the characters a scheme can be smuggled through.\n *\n * `java\\tscript:` and `java\\nscript:` are both parsed as `javascript:` by browsers,\n * and a leading control character defeats a naive `startsWith` check.\n */\nfunction normalize(raw: string): string {\n    // eslint-disable-next-line no-control-regex -- stripping control characters is the point\n    return raw.replace(/[\\u0000-\\u0020\\u007F]/g, \"\").trim();\n}\n\n/**\n * The href to render, or `null` when the URL must not be linked.\n *\n * Relative URLs (`/docs`, `./x`, `#anchor`, `foo/bar`) are kept: they cannot carry\n * a scheme, so they cannot execute anything, and dropping them would break every\n * in-app link.\n *\n * @param raw - URL as written in the Markdown.\n * @returns A safe href, or `null`.\n */\nexport function safeLinkUrl(raw: string): string | null {\n    const url = normalize(raw);\n    if (!url) return null;\n    if (/^[a-z][a-z0-9+.-]*:/i.test(url)) {\n        const scheme = url.slice(0, url.indexOf(\":\") + 1).toLowerCase();\n        return LINK_SCHEMES.has(scheme) ? url : null;\n    }\n    // Protocol-relative (`//host/path`) inherits the page scheme — safe, and real.\n    return url;\n}\n\n/**\n * The `src` to render for an image, or `null`.\n *\n * @param raw - URL as written in the Markdown.\n * @returns A safe src, or `null`.\n */\nexport function safeImageUrl(raw: string): string | null {\n    const url = normalize(raw);\n    if (!url) return null;\n    if (IMAGE_DATA_TYPES.test(url)) return url;\n    if (/^data:/i.test(url)) return null;\n    return safeLinkUrl(url);\n}\n"],"mappings":"AAYA,IAAM,EAAe,IAAI,IAAI,CAAC,QAAS,SAAU,UAAW,OAAQ,MAAM,CAAC,EASrE,EAAmB,qEAQzB,SAAS,EAAU,EAAqB,CAEpC,OAAO,EAAI,QAAQ,yBAA0B,EAAE,CAAC,CAAC,KAAK,CAC1D,CAYA,SAAgB,EAAY,EAA4B,CACpD,IAAM,EAAM,EAAU,CAAG,EACzB,GAAI,CAAC,EAAK,OAAO,KACjB,GAAI,uBAAuB,KAAK,CAAG,EAAG,CAClC,IAAM,EAAS,EAAI,MAAM,EAAG,EAAI,QAAQ,GAAG,EAAI,CAAC,CAAC,CAAC,YAAY,EAC9D,OAAO,EAAa,IAAI,CAAM,EAAI,EAAM,IAC5C,CAEA,OAAO,CACX,CAQA,SAAgB,EAAa,EAA4B,CACrD,IAAM,EAAM,EAAU,CAAG,EAIzB,OAHK,EACD,EAAiB,KAAK,CAAG,EAAU,EACnC,UAAU,KAAK,CAAG,EAAU,KACzB,EAAY,CAAG,EAHL,IAIrB"}