UNPKG

@mintlify/common

Version:

Commonly shared code within Mintlify

78 lines (77 loc) 2.04 kB
const dangerousProtocols = ['javascript:', 'data:', 'vbscript:', 'file:', 'about:', 'blob:']; function hasDangerousPatterns(str) { const lowerCased = str.toLowerCase(); for (const protocol of dangerousProtocols) { if (lowerCased.startsWith(protocol)) { return true; } } if (str.startsWith('//')) { return true; } if (str.includes('\t') || str.includes('\n') || str.includes('\r') || str.includes('\x00')) { return true; } if (str.includes('&#')) { return true; } return false; } export function validateRedirectUrl(options) { const { redirectUrl, currentOrigin, allowedOrigins, allowDifferentOrigin } = options; if (!redirectUrl) { return true; } if (typeof redirectUrl !== 'string') { return null; } const trimmed = redirectUrl.trim(); if (trimmed.length === 0) { return true; } if (hasDangerousPatterns(trimmed)) { return null; } let decoded = trimmed; for (let i = 0; i < 3; i++) { try { const nextDecoded = decodeURIComponent(decoded); if (nextDecoded === decoded) { break; } decoded = nextDecoded; if (hasDangerousPatterns(decoded)) { return null; } } catch (_a) { return null; } } if (trimmed.startsWith('/')) { return trimmed; } if (!trimmed.includes('://')) { return trimmed; } try { const url = new URL(trimmed); if (url.protocol !== 'https:') { return null; } if (allowDifferentOrigin) { return trimmed; } const urlOrigin = url.origin; if (urlOrigin === currentOrigin) { return trimmed; } if (allowedOrigins && allowedOrigins.includes(urlOrigin)) { return trimmed; } return null; } catch (_b) { return null; } }