import 'package:flutter_test/flutter_test.dart'; import 'package:melo_app/utils/sanitize.dart'; void main() { group('sanitizeDateiname — Path-Traversal', () { test('entfernt Pfad-Komponenten (Slash)', () { expect(sanitizeDateiname('../../etc/passwd'), 'passwd'); expect(sanitizeDateiname('a/b/c.mp3'), 'c.mp3'); }); test('entfernt Pfad-Komponenten (Backslash)', () { expect(sanitizeDateiname(r'..\..\windows\system32'), 'system32'); expect(sanitizeDateiname(r'a\b.mp3'), 'b.mp3'); }); test('kompletter ".."-Name → unknown', () { expect(sanitizeDateiname('..'), 'unknown'); expect(sanitizeDateiname('...'), 'unknown'); }); test('Leerzeichen VOR den Punkten umgeht die Punkt-Entfernung nicht', () { // Regression: " .." wurde früher erst nach der Punkt-Prüfung getrimmt // und ergab ".." als Dateinamen (Pfad-Traversal). expect(sanitizeDateiname(' ..'), 'unknown'); expect(sanitizeDateiname(' ... '), 'unknown'); }); test('mehrfache ".."-Sequenzen nach Sonderzeichen-Entfernung → unknown', () { // "..:.." → Sonderzeichen ':' weg → "...." → führende Punkte weg → leer expect(sanitizeDateiname('..:..'), 'unknown'); expect(sanitizeDateiname('..!..'), 'unknown'); }); test('führende Punkte (versteckte Dateien) werden entfernt', () { expect(sanitizeDateiname('.hidden'), 'hidden'); expect(sanitizeDateiname('..hidden'), 'hidden'); }); }); group('sanitizeDateiname — Windows-reservierte Namen', () { test('CON/NUL/PRN/AUX exakt → unknown', () { expect(sanitizeDateiname('CON'), 'unknown'); expect(sanitizeDateiname('NUL'), 'unknown'); expect(sanitizeDateiname('PRN'), 'unknown'); expect(sanitizeDateiname('AUX'), 'unknown'); }); test('COM1-9 und LPT1-9 exakt → unknown', () { for (var i = 1; i <= 9; i++) { expect(sanitizeDateiname('COM$i'), 'unknown'); expect(sanitizeDateiname('LPT$i'), 'unknown'); } }); test('reservierte Namen auch MIT Endung → unknown', () { expect(sanitizeDateiname('CON.txt'), 'unknown'); expect(sanitizeDateiname('NUL.mp3'), 'unknown'); expect(sanitizeDateiname('AUX.melo'), 'unknown'); expect(sanitizeDateiname('COM1.mp3'), 'unknown'); expect(sanitizeDateiname('LPT9.lrc'), 'unknown'); }); test('case-insensitiv', () { expect(sanitizeDateiname('con'), 'unknown'); expect(sanitizeDateiname('nul'), 'unknown'); expect(sanitizeDateiname('CoM4'), 'unknown'); expect(sanitizeDateiname('aux'), 'unknown'); }); test('COM10/LPT10 sind NICHT reserviert (nur 1-9)', () { expect(sanitizeDateiname('COM10'), 'COM10'); expect(sanitizeDateiname('LPT10'), 'LPT10'); }); test('Namen, die nur mit reservierten Wörtern anfangen, bleiben', () { expect(sanitizeDateiname('console'), 'console'); expect(sanitizeDateiname('constante'), 'constante'); expect(sanitizeDateiname('LPT1 - Drucker'), 'LPT1 - Drucker'); }); }); group('sanitizeDateiname — Zeichen & Normalisierung', () { test('entfernt NULL-Bytes und Steuerzeichen', () { expect(sanitizeDateiname('song\u0000.mp3'), 'song.mp3'); expect(sanitizeDateiname('a\u0001b\u001Fc'), 'abc'); }); test('entfernt Sonderzeichen, behält Wort-Zeichen/Leerzeichen/Punkte/-', () { expect(sanitizeDateiname('Mein Song - Remix (2026)!.mp3'), 'Mein Song - Remix 2026.mp3'); }); test('trailing Punkte/Leerzeichen werden entfernt (Windows)', () { expect(sanitizeDateiname('song..'), 'song'); expect(sanitizeDateiname('song. '), 'song'); }); test('leere/unlesbare Eingabe → unknown', () { expect(sanitizeDateiname(''), 'unknown'); expect(sanitizeDateiname('///'), 'unknown'); expect(sanitizeDateiname('...'), 'unknown'); expect(sanitizeDateiname(' '), 'unknown'); }); test('kürzt auf 200 Zeichen', () { final lang = 'a' * 300; expect(sanitizeDateiname(lang).length, 200); }); test('normaler Dateiname bleibt unverändert', () { expect(sanitizeDateiname('Künstler - Titel 2026.mp3'), 'Künstler - Titel 2026.mp3'); }); }); }