veza/apps/web/src/utils/sanitize.test.ts

163 lines
5.1 KiB
TypeScript
Raw Normal View History

/**
* Tests for Sanitize Utility
* FE-TEST-004: Test sanitize utility functions
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
sanitizeHTML,
sanitizeChatMessage,
sanitizeTextInput,
sanitizeURL,
sanitizeEmail,
validatePassword,
} from './sanitize';
// Mock DOMPurify
vi.mock('dompurify', () => ({
default: {
isSupported: true,
sanitize: vi.fn((html: string) =>
html.replace(/<script[^>]*>.*?<\/script>/gi, ''),
),
},
}));
describe('sanitize utilities', () => {
describe('sanitizeHTML', () => {
it('should remove script tags', () => {
const input = '<p>Hello</p><script>alert("xss")</script>';
const result = sanitizeHTML(input);
expect(result).not.toContain('<script>');
});
it('should escape HTML tags (sanitizeHTML escapes all HTML)', () => {
const input = '<p>Hello <strong>world</strong></p>';
const result = sanitizeHTML(input);
// sanitizeHTML escapes all HTML, so tags become entities
expect(result).toContain('&lt;');
expect(result).toContain('Hello');
});
it('should remove dangerous patterns', () => {
const input = '<p onclick="alert(1)">Hello</p>';
const result = sanitizeHTML(input);
expect(result).not.toContain('onclick');
});
});
describe('sanitizeChatMessage', () => {
it('should sanitize chat messages', () => {
const input = '<p>Hello <script>alert("xss")</script></p>';
const result = sanitizeChatMessage(input);
expect(result).not.toContain('<script>');
});
it('should preserve basic formatting', () => {
const input = '<p>Hello <strong>world</strong></p>';
const result = sanitizeChatMessage(input);
expect(result).toContain('<p>');
expect(result).toContain('<strong>');
});
});
describe('sanitizeTextInput', () => {
it('should escape HTML', () => {
const input = '<script>alert("xss")</script>';
const result = sanitizeTextInput(input);
expect(result).not.toContain('<script>');
expect(result).toContain('&lt;');
});
it('should trim input', () => {
expect(sanitizeTextInput(' hello ')).toBe('hello');
});
});
describe('sanitizeURL', () => {
it('should allow http URLs', () => {
const result = sanitizeURL('http://example.com');
// URL.toString() adds trailing slash
expect(result).toBe('http://example.com/');
});
it('should allow https URLs', () => {
const result = sanitizeURL('https://example.com');
// URL.toString() adds trailing slash
expect(result).toBe('https://example.com/');
});
it('should reject javascript URLs', () => {
const result = sanitizeURL('javascript:alert(1)');
expect(result).toBeNull();
});
it('should reject file URLs', () => {
const result = sanitizeURL('file:///etc/passwd');
expect(result).toBeNull();
});
it('should return null for invalid URLs', () => {
const result = sanitizeURL('not-a-url');
expect(result).toBeNull();
});
});
describe('sanitizeEmail', () => {
it('should validate and normalize valid email', () => {
const result = sanitizeEmail('Test@Example.COM');
expect(result).toBe('test@example.com');
});
it('should return null for invalid email', () => {
expect(sanitizeEmail('not-an-email')).toBeNull();
expect(sanitizeEmail('test@')).toBeNull();
expect(sanitizeEmail('@example.com')).toBeNull();
});
});
describe('validatePassword', () => {
it('should validate strong password', () => {
const result = validatePassword('StrongP@ssw0rd123');
expect(result.isValid).toBe(true);
expect(result.errors).toHaveLength(0);
});
it('should reject short password', () => {
const result = validatePassword('Short1!');
expect(result.isValid).toBe(false);
expect(result.errors.length).toBeGreaterThan(0);
});
it('should reject password without uppercase', () => {
const result = validatePassword('lowercase123!');
expect(result.isValid).toBe(false);
expect(result.errors.some((e) => e.includes('majuscule'))).toBe(true);
});
it('should reject password without lowercase', () => {
const result = validatePassword('UPPERCASE123!');
expect(result.isValid).toBe(false);
expect(result.errors.some((e) => e.includes('minuscule'))).toBe(true);
});
it('should reject password without number', () => {
const result = validatePassword('NoNumber!');
expect(result.isValid).toBe(false);
expect(result.errors.some((e) => e.includes('chiffre'))).toBe(true);
});
it('should reject password without special character', () => {
const result = validatePassword('NoSpecial123');
expect(result.isValid).toBe(false);
expect(result.errors.some((e) => e.includes('spécial'))).toBe(true);
});
it('should reject common weak patterns', () => {
const result = validatePassword('Password123!');
expect(result.isValid).toBe(false);
expect(result.errors.some((e) => e.includes('commun'))).toBe(true);
});
});
});