Files
breadcrumb-the-shire/tests/Architecture/ApiSessionIsolationContractTest.php

88 lines
2.7 KiB
PHP

<?php
namespace MintyPHP\Tests\Architecture;
use PHPUnit\Framework\TestCase;
/**
* GR-SEC-007: API requests must not start session/cookie/remember-me web flows.
*/
final class ApiSessionIsolationContractTest extends TestCase
{
use ProjectFileAssertionSupport;
public function testApiActionsDoNotUseSessionOrWebGuardFlows(): void
{
$apiFiles = $this->collectFilesByExtension('pages/api/v1', ['php']);
self::assertNotSame([], $apiFiles, 'Expected API actions under pages/api/v1.');
$patterns = [
'/\bSession::/' => 'Session::*',
'/\bGuard::/' => 'Guard::*',
'/\bsetcookie\s*\(/i' => 'setcookie(...)',
'/\bsession_start\s*\(/i' => 'session_start(...)',
'/remember[-_ ]?me/i' => 'remember-me',
'/session[_ ]?timeout/i' => 'session-timeout',
];
$violations = [];
foreach ($apiFiles as $relativePath) {
$content = $this->readProjectFile($relativePath);
foreach ($patterns as $regex => $label) {
if (preg_match($regex, $content)) {
$violations[] = "{$relativePath} uses {$label}";
}
}
}
sort($violations);
self::assertSame(
[],
$violations,
"API action files contain web-session flow references (GR-SEC-007):\n" . implode("\n", $violations)
);
}
public function testApiBootstrapDoesNotStartSessionOrRememberMeFlow(): void
{
$content = $this->readProjectFile('lib/Http/ApiBootstrap.php');
self::assertStringNotContainsString('Session::start(', $content);
self::assertStringNotContainsString('RememberMe', $content);
self::assertStringNotContainsString('CookieStore', $content);
self::assertStringNotContainsString('session timeout', strtolower($content));
}
/**
* @param list<string> $extensions
* @return list<string>
*/
private function collectFilesByExtension(string $relativeDir, array $extensions): array
{
$root = $this->projectRootPath();
$dir = $root . '/' . $relativeDir;
if (!is_dir($dir)) {
return [];
}
$result = [];
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS)
);
foreach ($iterator as $file) {
if (!$file->isFile()) {
continue;
}
if (!in_array(strtolower($file->getExtension()), $extensions, true)) {
continue;
}
$result[] = str_replace($root . '/', '', $file->getPathname());
}
sort($result);
return $result;
}
}