dd-timer/tests/regression/security-hardening.test.ts
POL Mickaël 8af626dd66 sécurité: audit commercialisation — hardening + 366 tests (24 E2E)
Sandbox Electron, HTTPS ntfy, validation import structurelle,
suppression executeJavaScript, nettoyage memory leaks, try/catch
sur tous les appels electronAPI. 27 nouveaux tests de sécurité
et validation. README mis à jour avec changelog et couverture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 07:18:28 +02:00

79 lines
2.5 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { esc } from '@presentation/helpers/format';
describe('Sécurité — Hardening', () => {
describe('esc() — sanitisation XSS', () => {
it('échappe les balises HTML', () => {
expect(esc('<script>alert("xss")</script>')).toBe('&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;');
});
it('échappe les attributs HTML', () => {
expect(esc('" onmouseover="alert(1)"')).toBe('&quot; onmouseover=&quot;alert(1)&quot;');
});
it('échappe les ampersands', () => {
expect(esc('a&b')).toBe('a&amp;b');
});
it('échappe les chevrons', () => {
expect(esc('<img>')).toBe('&lt;img&gt;');
});
it('gère les nombres', () => {
expect(esc(42)).toBe('42');
});
it('gère les chaînes vides', () => {
expect(esc('')).toBe('');
});
it('préserve le texte sans caractères spéciaux', () => {
expect(esc('Enclos normal')).toBe('Enclos normal');
});
it('gère les caractères accentués', () => {
expect(esc('Dragodinde Émeraude')).toBe('Dragodinde Émeraude');
});
});
describe('Validation URL ntfy — HTTPS requis', () => {
it('URL HTTPS est valide', () => {
const url = 'https://ntfy.example.com/topic';
const parsed = new URL(url);
expect(parsed.protocol).toBe('https:');
});
it('URL HTTP est rejetée', () => {
const url = 'http://ntfy.example.com/topic';
const parsed = new URL(url);
expect(parsed.protocol).not.toBe('https:');
});
it('URL invalide lève une erreur', () => {
expect(() => new URL('not-a-url')).toThrow();
});
});
describe('Validation backup — format métadonnées', () => {
it('backup valide contient app, version, exportedAt, data', () => {
const backup = {
app: 'minuteur-dragodinde',
version: '1.1.6',
exportedAt: new Date().toISOString(),
data: { enclos: [] },
};
expect(backup.app).toBe('minuteur-dragodinde');
expect(typeof backup.version).toBe('string');
expect(typeof backup.exportedAt).toBe('string');
expect(backup.data).toBeDefined();
});
it('data: null est détecté comme invalide', () => {
const backup = { app: 'minuteur-dragodinde', data: null };
// typeof null === 'object' mais data === null doit être rejeté
expect(backup.data === null).toBe(true);
expect(!!(backup.data && typeof backup.data === 'object' && backup.data !== null)).toBe(false);
});
});
});