Files
opensourcepos/tests/Config/AppTest.php
Ollama 7cb1d95da7 Fix: Host Header Injection vulnerability (GHSA-jchf-7hr6-h4f3)
Security: Prevent Host Header Injection attacks by validating HTTP_HOST
against a whitelist of allowed hostnames before constructing the baseURL.

Changes:
- Add getValidHost() method to validate HTTP_HOST against allowedHostnames
- If allowedHostnames is empty, log warning and fall back to 'localhost'
- If host not in whitelist, log warning and use first allowed hostname
- Update .env.example with allowedHostnames documentation
- Add security configuration section to INSTALL.md
- Add unit tests for host validation

This addresses the security advisory where the application constructed
baseURL from the attacker-controllable HTTP_HOST header, allowing:
- Login form phishing via manipulated form actions
- Cache poisoning via poisoned asset URLs

Fixes GHSA-jchf-7hr6-h4f3
2026-03-14 15:34:21 +00:00

126 lines
3.7 KiB
PHP

<?php
namespace Tests\Config;
use CodeIgniter\Test\CIUnitTestCase;
use Config\App;
class AppTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
}
protected function tearDown(): void
{
parent::tearDown();
}
public function testGetValidHostReturnsHostWhenValid(): void
{
$app = new class extends App {
public array $allowedHostnames = ['example.com', 'www.example.com'];
public function __construct() {}
};
$reflection = new \ReflectionClass($app);
$method = $reflection->getMethod('getValidHost');
$method->setAccessible(true);
$_SERVER['HTTP_HOST'] = 'example.com';
$host = $method->invoke($app);
$this->assertEquals('example.com', $host);
$_SERVER['HTTP_HOST'] = 'www.example.com';
$host = $method->invoke($app);
$this->assertEquals('www.example.com', $host);
}
public function testGetValidHostReturnsFallbackForInvalidHost(): void
{
$app = new class extends App {
public array $allowedHostnames = ['example.com', 'www.example.com'];
public function __construct() {}
};
$reflection = new \ReflectionClass($app);
$method = $reflection->getMethod('getValidHost');
$method->setAccessible(true);
$_SERVER['HTTP_HOST'] = 'malicious.com';
$host = $method->invoke($app);
$this->assertEquals('example.com', $host);
$_SERVER['HTTP_HOST'] = 'evil.org';
$host = $method->invoke($app);
$this->assertEquals('example.com', $host);
}
public function testGetValidHostReturnsLocalhostWhenNoWhitelist(): void
{
$app = new class extends App {
public array $allowedHostnames = [];
public function __construct() {}
};
$reflection = new \ReflectionClass($app);
$method = $reflection->getMethod('getValidHost');
$method->setAccessible(true);
$_SERVER['HTTP_HOST'] = 'malicious.com';
$host = $method->invoke($app);
$this->assertEquals('localhost', $host);
$_SERVER['HTTP_HOST'] = 'example.com';
$host = $method->invoke($app);
$this->assertEquals('localhost', $host);
}
public function testGetValidHostHandlesMissingHttpHost(): void
{
$app = new class extends App {
public array $allowedHostnames = ['example.com'];
public function __construct() {}
};
$reflection = new \ReflectionClass($app);
$method = $reflection->getMethod('getValidHost');
$method->setAccessible(true);
unset($_SERVER['HTTP_HOST']);
$host = $method->invoke($app);
$this->assertEquals('example.com', $host);
}
public function testBaseURLContainsValidHost(): void
{
$_SERVER['HTTP_HOST'] = 'example.com';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['HTTPS'] = null;
$app = new class extends App {
public array $allowedHostnames = ['example.com'];
};
$this->assertStringContainsString('example.com', $app->baseURL);
}
public function testBaseURLUsesFallbackHostWhenInvalidHostProvided(): void
{
$_SERVER['HTTP_HOST'] = 'malicious.com';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['HTTPS'] = null;
$app = new class extends App {
public array $allowedHostnames = ['example.com'];
};
$this->assertStringContainsString('example.com', $app->baseURL);
$this->assertStringNotContainsString('malicious.com', $app->baseURL);
}
}