88 lines
2.7 KiB
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;
|
|
}
|
|
}
|