export class URLValidator {
private static readonly ALLOWED_PROTOCOLS = ['http:', 'https:'];
private static readonly BLOCKED_DOMAINS = [
'localhost',
'127.0.0.1',
'0.0.0.0',
'::1'
];
static validate(urlString: string): { isValid: boolean; error?: string; url?: URL } {
if (!urlString || typeof urlString !== 'string' || urlString.trim() === '') {
return {
isValid: false,
error: 'URL cannot be empty.'
};
}
let urlToParse = urlString.trim();
try {
const url = new URL(urlToParse);
// Check protocol
if (!this.ALLOWED_PROTOCOLS.includes(url.protocol)) {
return {
isValid: false,
error: `Protocol '${url.protocol}' is not allowed. Only HTTP and HTTPS are supported.`
};
}
// Check if it's a blocked local domain (optional security check)
if (this.BLOCKED_DOMAINS.includes(url.hostname)) {
return {
isValid: false,
error: `Cannot test localhost or local addresses for security reasons. Please use a public URL.`
};
}
// Check for suspicious patterns
if (url.hostname.includes('..') || url.pathname.includes('..')) {
return {
isValid: false,
error: 'URL contains potentially malicious patterns.'
};
}
return {
isValid: true,
url
};
} catch (error) {
return {
isValid: false,
error: `Invalid URL format: ${error instanceof Error ? error.message : 'Unknown error'}`
};
}
}
static sanitizeUrl(urlString: string): string {
const validation = this.validate(urlString);
if (!validation.isValid || !validation.url) {
throw new Error(validation.error || 'Invalid URL');
}
return validation.url.href;
}
}
export interface TestConfiguration {
wcagLevel: 'A' | 'AA' | 'AAA';
wcagVersion: '2.1' | '2.2';
browser: 'chromium' | 'firefox' | 'webkit';
timeout: number;
includeScreenshot: boolean;
viewport?: {
width: number;
height: number;
};
}
export class ConfigValidator {
static validateTestConfiguration(config: Partial<TestConfiguration>): TestConfiguration {
const defaults: TestConfiguration = {
wcagLevel: 'AA',
wcagVersion: '2.1',
browser: 'chromium',
timeout: 30000,
includeScreenshot: false,
viewport: { width: 1920, height: 1080 }
};
const validated: TestConfiguration = { ...defaults, ...config };
// Validate WCAG level
if (!['A', 'AA', 'AAA'].includes(validated.wcagLevel)) {
throw new Error(`Invalid WCAG level: ${validated.wcagLevel}. Must be A, AA, or AAA.`);
}
// Validate WCAG version
if (!['2.1', '2.2'].includes(validated.wcagVersion)) {
throw new Error(`Invalid WCAG version: ${validated.wcagVersion}. Must be 2.1 or 2.2.`);
}
// Validate browser
if (!['chromium', 'firefox', 'webkit'].includes(validated.browser)) {
throw new Error(`Invalid browser: ${validated.browser}. Must be chromium, firefox, or webkit.`);
}
// Validate timeout
if (validated.timeout < 1000 || validated.timeout > 300000) {
throw new Error('Timeout must be between 1 second and 5 minutes.');
}
// Validate viewport
if (validated.viewport) {
if (validated.viewport.width < 320 || validated.viewport.width > 4000) {
throw new Error('Viewport width must be between 320 and 4000 pixels.');
}
if (validated.viewport.height < 200 || validated.viewport.height > 4000) {
throw new Error('Viewport height must be between 200 and 4000 pixels.');
}
}
return validated;
}
}