@mintlify/common
Version:
Commonly shared code within Mintlify
78 lines (77 loc) • 2.04 kB
JavaScript
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;
}
}