test: Testsuite fuer Kernlogik, QR-Codes, Domains und Sicherheit
232 Tests, unter anderem: - Slug-Erzeugung, Kollisionsbehandlung und Validierung - URL-Validierung inklusive IDN, Prozentkodierung und abgelehnter Protokolle; Punycode gegengeprueft gegen idn_to_ascii - QR-Erzeugung in allen Groessen, mit Logo und Level-H-Automatik - Weiterleitung gegen eine echte SQLite-Datenbank samt Domain- Aufloesung ueber den Host-Header - Update einer bestehenden Installation auf mehrere Domains - Angriffsseitig: gefaelschte Proxy-Header, Host-Header-Injektion, SVG mit Skripten, XXE und externen Referenzen
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests;
|
||||
|
||||
use App\Support\Punycode;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class PunycodeTest extends TestCase
|
||||
{
|
||||
public function testAsciiHostsBleibenUnveraendert(): void
|
||||
{
|
||||
foreach (['example.com', 'sub.domain.co.uk', 'intranet', 'xn--mller-kva.de', ''] as $host) {
|
||||
self::assertSame($host, Punycode::encodeDomain($host));
|
||||
}
|
||||
}
|
||||
|
||||
public function testIsAscii(): void
|
||||
{
|
||||
self::assertTrue(Punycode::isAscii('example.com'));
|
||||
self::assertTrue(Punycode::isAscii(''));
|
||||
self::assertFalse(Punycode::isAscii('müller.de'));
|
||||
self::assertFalse(Punycode::isAscii('日本語.jp'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,array{0:string,1:string}>
|
||||
*/
|
||||
public static function domainProvider(): array
|
||||
{
|
||||
return [
|
||||
'muellerde' => ['müller.de', 'xn--mller-kva.de'],
|
||||
'buecherde' => ['bücher.de', 'xn--bcher-kva.de'],
|
||||
'muenchende' => ['münchen.de', 'xn--mnchen-3ya.de'],
|
||||
'nur ein Label' => ['münchen', 'xn--mnchen-3ya'],
|
||||
'mehrere Umlaute' => ['grüße.example.com', 'xn--gre-6ka8l.example.com'],
|
||||
'Subdomain umlautfrei' => ['www.müller.de', 'www.xn--mller-kva.de'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider domainProvider
|
||||
*/
|
||||
public function testEncodeDomain(string $unicode, string $expected): void
|
||||
{
|
||||
self::assertSame($expected, Punycode::encodeDomain($unicode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Die eigene Implementierung muss dasselbe Ergebnis liefern wie die
|
||||
* Referenz aus ext-intl. Ohne intl entfaellt die Gegenprobe.
|
||||
*/
|
||||
public function testStimmtMitIntlUeberein(): void
|
||||
{
|
||||
if (!function_exists('idn_to_ascii')) {
|
||||
self::markTestSkipped('ext-intl ist nicht verfuegbar.');
|
||||
}
|
||||
|
||||
$domains = [
|
||||
'müller.de',
|
||||
'bücher.de',
|
||||
'münchen.de',
|
||||
'grüße.example.com',
|
||||
'www.müller.de',
|
||||
'österreich.at',
|
||||
'köln.de',
|
||||
'schöne-grüße.de',
|
||||
'日本語.jp',
|
||||
'мосва.рф',
|
||||
'ελλάδα.gr',
|
||||
];
|
||||
|
||||
foreach ($domains as $domain) {
|
||||
$reference = idn_to_ascii($domain, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46);
|
||||
self::assertIsString($reference, $domain);
|
||||
|
||||
// Die eigene Implementierung direkt aufrufen, nicht den
|
||||
// intl-Weg innerhalb von encodeDomain().
|
||||
self::assertSame($reference, self::encodeWithoutIntl($domain), $domain);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruft den Rueckfallweg ueber Reflection auf, damit die Gegenprobe
|
||||
* nicht wieder bei idn_to_ascii landet.
|
||||
*/
|
||||
private static function encodeWithoutIntl(string $host): string
|
||||
{
|
||||
$method = new \ReflectionMethod(Punycode::class, 'encodeLabel');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$labels = array_map(
|
||||
static fn (string $label): string => (string) $method->invoke(null, $label),
|
||||
explode('.', $host),
|
||||
);
|
||||
|
||||
return implode('.', $labels);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user