mirror of
https://github.com/linuxserver/Heimdall.git
synced 2026-08-07 07:16:13 +00:00
881533baa5
Heimdall trusted the incoming X-Forwarded-Host header for URL generation, so a spoofed value poisoned the page base href, asset() URLs and redirect targets - loading assets from and redirecting to an attacker-controlled host (CVE-2025-50578). - TrustProxies no longer trusts X-Forwarded-Host; a forged value can no longer influence getHost(), url(), asset() or redirects. X-Forwarded-For/Port/Proto handling is unchanged. - Trusted proxies are now configurable via the TRUSTED_PROXIES env var (comma-separated CIDRs/IPs, "*" to trust all), defaulting to the previous private ranges. - Added an opt-in TRUSTED_HOSTS allow-list: when set, only the listed hosts are served and any other Host header is rejected. Unset keeps the historic behaviour of serving arbitrary hosts, so existing installs are unaffected. Resolves #1451
109 lines
3.4 KiB
PHP
109 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Http\Middleware\TrustHosts;
|
|
use Illuminate\Contracts\Http\Kernel;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
|
|
use Tests\TestCase;
|
|
|
|
class TrustHostsTest extends TestCase
|
|
{
|
|
/**
|
|
* Remove any TRUSTED_HOSTS override and reset Symfony's static trusted host
|
|
* state so tests do not leak into one another.
|
|
*/
|
|
protected function tearDown(): void
|
|
{
|
|
putenv('TRUSTED_HOSTS');
|
|
unset($_ENV['TRUSTED_HOSTS'], $_SERVER['TRUSTED_HOSTS']);
|
|
|
|
Request::setTrustedHosts([]);
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
private function setTrustedHostsEnv(string $value): void
|
|
{
|
|
putenv('TRUSTED_HOSTS='.$value);
|
|
$_ENV['TRUSTED_HOSTS'] = $value;
|
|
$_SERVER['TRUSTED_HOSTS'] = $value;
|
|
}
|
|
|
|
private function makeMiddleware(): TrustHosts
|
|
{
|
|
return $this->app->make(TrustHosts::class);
|
|
}
|
|
|
|
public function test_hosts_is_empty_when_env_unset(): void
|
|
{
|
|
putenv('TRUSTED_HOSTS');
|
|
unset($_ENV['TRUSTED_HOSTS'], $_SERVER['TRUSTED_HOSTS']);
|
|
|
|
$this->assertSame([], $this->makeMiddleware()->hosts());
|
|
}
|
|
|
|
public function test_arbitrary_host_is_accepted_when_env_unset(): void
|
|
{
|
|
putenv('TRUSTED_HOSTS');
|
|
unset($_ENV['TRUSTED_HOSTS'], $_SERVER['TRUSTED_HOSTS']);
|
|
|
|
// No trusted host patterns configured -> getHost() must not throw.
|
|
Request::setTrustedHosts(array_filter($this->makeMiddleware()->hosts()));
|
|
|
|
$request = Request::create('http://anything.example/', 'GET');
|
|
|
|
$this->assertSame('anything.example', $request->getHost());
|
|
}
|
|
|
|
public function test_hosts_contains_pattern_matching_configured_host(): void
|
|
{
|
|
$this->setTrustedHostsEnv('example.com');
|
|
|
|
$hosts = $this->makeMiddleware()->hosts();
|
|
|
|
$this->assertNotEmpty($hosts);
|
|
$this->assertCount(1, $hosts);
|
|
// Symfony wraps each pattern as {pattern}i before matching.
|
|
$this->assertSame(1, preg_match('{'.$hosts[0].'}i', 'example.com'));
|
|
$this->assertSame(0, preg_match('{'.$hosts[0].'}i', 'evil.com'));
|
|
}
|
|
|
|
public function test_configured_host_is_accepted_and_others_rejected(): void
|
|
{
|
|
$this->setTrustedHostsEnv('example.com');
|
|
|
|
Request::setTrustedHosts($this->makeMiddleware()->hosts());
|
|
|
|
$accepted = Request::create('http://example.com/', 'GET');
|
|
$this->assertSame('example.com', $accepted->getHost());
|
|
|
|
$this->expectException(SuspiciousOperationException::class);
|
|
|
|
Request::create('http://evil.com/', 'GET')->getHost();
|
|
}
|
|
|
|
public function test_multiple_hosts_can_be_configured(): void
|
|
{
|
|
$this->setTrustedHostsEnv('example.com, dash.example.org');
|
|
|
|
$hosts = $this->makeMiddleware()->hosts();
|
|
|
|
$this->assertCount(2, $hosts);
|
|
|
|
Request::setTrustedHosts($hosts);
|
|
|
|
$this->assertSame('example.com', Request::create('http://example.com/', 'GET')->getHost());
|
|
$this->assertSame('dash.example.org', Request::create('http://dash.example.org/', 'GET')->getHost());
|
|
}
|
|
|
|
public function test_custom_trust_hosts_middleware_is_registered_globally(): void
|
|
{
|
|
$globalMiddleware = $this->app->make(Kernel::class)->getGlobalMiddleware();
|
|
|
|
$this->assertContains(TrustHosts::class, $globalMiddleware);
|
|
$this->assertNotContains(\Illuminate\Http\Middleware\TrustHosts::class, $globalMiddleware);
|
|
}
|
|
}
|