dev-lamp
Version:
Your friendly lighthouse performance companion - 100% local
93 lines • 3.54 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.UrlValidator = void 0;
class UrlValidator {
static ALLOWED_PROTOCOLS = ['http:', 'https:'];
static MAX_URL_LENGTH = 2048;
static DANGEROUS_PATTERNS = [
/javascript:/i,
/data:/i,
/vbscript:/i,
/file:/i,
/about:/i,
/chrome:/i,
/chrome-extension:/i
];
static validate(urlString) {
// Check URL length
if (!urlString || urlString.length === 0) {
return { valid: false, error: 'URL is required' };
}
if (urlString.length > this.MAX_URL_LENGTH) {
return { valid: false, error: `URL exceeds maximum length of ${this.MAX_URL_LENGTH} characters` };
}
// Trim whitespace
const trimmedUrl = urlString.trim();
// Check for dangerous patterns
for (const pattern of this.DANGEROUS_PATTERNS) {
if (pattern.test(trimmedUrl)) {
return { valid: false, error: 'URL contains potentially dangerous protocol' };
}
}
try {
const url = new URL(trimmedUrl);
// Check protocol
if (!this.ALLOWED_PROTOCOLS.includes(url.protocol)) {
return {
valid: false,
error: `Invalid protocol. Only ${this.ALLOWED_PROTOCOLS.join(', ')} are allowed`
};
}
// Check for local file paths disguised as URLs
if (url.pathname.includes('../') || url.pathname.includes('..\\')) {
return { valid: false, error: 'URL contains path traversal patterns' };
}
// Check hostname
if (!url.hostname || url.hostname.length === 0) {
return { valid: false, error: 'URL must have a valid hostname' };
}
// Validate hostname format
const hostnameRegex = /^([a-z0-9]+(-[a-z0-9]+)*\.)*[a-z0-9]+(-[a-z0-9]+)*$|^localhost$|^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$|^\[[\da-fA-F:]+\]$/i;
if (!hostnameRegex.test(url.hostname)) {
return { valid: false, error: 'Invalid hostname format' };
}
// Check port if specified
if (url.port) {
const port = parseInt(url.port, 10);
if (isNaN(port) || port < 1 || port > 65535) {
return { valid: false, error: 'Invalid port number (must be 1-65535)' };
}
}
// Return sanitized URL
return {
valid: true,
sanitized: url.toString()
};
}
catch (error) {
return { valid: false, error: 'Invalid URL format' };
}
}
static isLocalUrl(urlString) {
try {
const url = new URL(urlString);
const localPatterns = [
'localhost',
'127.0.0.1',
'0.0.0.0',
'::1',
'[::1]'
];
return localPatterns.includes(url.hostname) ||
url.hostname.endsWith('.local') ||
/^192\.168\.\d{1,3}\.\d{1,3}$/.test(url.hostname) ||
/^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(url.hostname) ||
/^172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}$/.test(url.hostname);
}
catch {
return false;
}
}
}
exports.UrlValidator = UrlValidator;
//# sourceMappingURL=url-validator.js.map