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>
79 lines
2.5 KiB
TypeScript
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('<script>alert("xss")</script>');
|
|
});
|
|
|
|
it('échappe les attributs HTML', () => {
|
|
expect(esc('" onmouseover="alert(1)"')).toBe('" onmouseover="alert(1)"');
|
|
});
|
|
|
|
it('échappe les ampersands', () => {
|
|
expect(esc('a&b')).toBe('a&b');
|
|
});
|
|
|
|
it('échappe les chevrons', () => {
|
|
expect(esc('<img>')).toBe('<img>');
|
|
});
|
|
|
|
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);
|
|
});
|
|
});
|
|
});
|