mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 22:01:33 +00:00
128 lines
4.3 KiB
PHP
128 lines
4.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Unit;
|
|
|
|
use App\Http\Middleware\ContentSecurityPolicy;
|
|
use Illuminate\Config\Repository;
|
|
use Illuminate\Container\Container;
|
|
use Illuminate\Foundation\Application;
|
|
use Illuminate\Foundation\Vite;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Facade;
|
|
use Laravel\Horizon\Horizon;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class ContentSecurityPolicyTest extends TestCase
|
|
{
|
|
private Container $originalContainer;
|
|
|
|
private ?Application $originalFacadeApplication;
|
|
|
|
private string $hotFile;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->originalContainer = Container::getInstance();
|
|
$facadeApplication = Facade::getFacadeApplication();
|
|
$this->originalFacadeApplication = $facadeApplication instanceof Application ? $facadeApplication : null;
|
|
|
|
$application = new Application(dirname(__DIR__, 2));
|
|
$application->instance('config', new Repository([
|
|
'app' => ['env' => 'production'],
|
|
'captcha' => [
|
|
'provider' => null,
|
|
'turnstile' => ['enabled' => false],
|
|
],
|
|
]));
|
|
$application->instance(Vite::class, new Vite);
|
|
|
|
Container::setInstance($application);
|
|
Facade::setFacadeApplication($application);
|
|
Facade::clearResolvedInstances();
|
|
|
|
$this->hotFile = sys_get_temp_dir().'/nntmux-csp-hot-'.bin2hex(random_bytes(8));
|
|
app(Vite::class)->useHotFile($this->hotFile);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
if (is_file($this->hotFile)) {
|
|
unlink($this->hotFile);
|
|
}
|
|
|
|
Horizon::$nonceAttribute = '';
|
|
Facade::clearResolvedInstances();
|
|
Container::setInstance($this->originalContainer);
|
|
Facade::setFacadeApplication($this->originalFacadeApplication);
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
public function test_horizon_inline_assets_receive_the_csp_nonce(): void
|
|
{
|
|
$response = (new ContentSecurityPolicy)->handle(
|
|
Request::create('/horizon'),
|
|
static fn (): Response => new Response(Horizon::css().Horizon::js()),
|
|
);
|
|
|
|
$nonce = app('csp_nonce');
|
|
|
|
$this->assertStringContainsString("script-src 'self' 'nonce-{$nonce}'", (string) $response->headers->get('Content-Security-Policy'));
|
|
$this->assertStringContainsString('https://fonts.bunny.net', (string) $response->headers->get('Content-Security-Policy'));
|
|
$this->assertSame(4, substr_count((string) $response->getContent(), 'nonce="'.$nonce.'"'));
|
|
}
|
|
|
|
public function test_local_vite_hot_reload_sources_are_added_to_the_csp(): void
|
|
{
|
|
config()->set('app.env', 'local');
|
|
file_put_contents($this->hotFile, 'http://localhost:5173');
|
|
|
|
$response = (new ContentSecurityPolicy)->handle(
|
|
Request::create('/'),
|
|
static fn (): Response => new Response,
|
|
);
|
|
|
|
$csp = (string) $response->headers->get('Content-Security-Policy');
|
|
|
|
$this->assertStringContainsString("style-src-elem 'self' 'unsafe-inline'", $csp);
|
|
$this->assertStringContainsString('https://cdn.tiny.cloud http://localhost:5173', $csp);
|
|
$this->assertStringContainsString('https://sp.tinymce.com http://localhost:5173 ws://localhost:5173', $csp);
|
|
}
|
|
|
|
public function test_vite_hot_reload_sources_are_not_added_outside_local_environment(): void
|
|
{
|
|
file_put_contents($this->hotFile, 'http://localhost:5173');
|
|
|
|
$response = (new ContentSecurityPolicy)->handle(
|
|
Request::create('/'),
|
|
static fn (): Response => new Response,
|
|
);
|
|
|
|
$this->assertStringNotContainsString(
|
|
'http://localhost:5173',
|
|
(string) $response->headers->get('Content-Security-Policy'),
|
|
);
|
|
}
|
|
|
|
public function test_non_loopback_vite_hot_reload_sources_are_rejected(): void
|
|
{
|
|
config()->set('app.env', 'local');
|
|
file_put_contents($this->hotFile, 'https://assets.example.com:5173');
|
|
|
|
$response = (new ContentSecurityPolicy)->handle(
|
|
Request::create('/'),
|
|
static fn (): Response => new Response,
|
|
);
|
|
|
|
$this->assertStringNotContainsString(
|
|
'https://assets.example.com:5173',
|
|
(string) $response->headers->get('Content-Security-Policy'),
|
|
);
|
|
}
|
|
}
|