chore(agents): implement v2 hardening and enforcement-ready QA
This commit is contained in:
87
tests/Architecture/ApiSessionIsolationContractTest.php
Normal file
87
tests/Architecture/ApiSessionIsolationContractTest.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user