fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled
Some checks failed
Deploy Application / deploy (push) Has been cancelled
This commit is contained in:
136
tests/Unit/Framework/Http/Session/CsrfProtectionTest.php
Normal file
136
tests/Unit/Framework/Http/Session/CsrfProtectionTest.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Framework\DateTime\Clock;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\Http\Session\CsrfProtection;
|
||||
use App\Framework\Http\Session\Session;
|
||||
use App\Framework\Http\Session\SessionId;
|
||||
use App\Framework\Http\Session\ValueObjects\CsrfDataCollection;
|
||||
use App\Framework\Http\Session\ValueObjects\FormDataCollection;
|
||||
use App\Framework\Http\Session\ValueObjects\FlashMessageCollection;
|
||||
use App\Framework\Http\Session\ValueObjects\SecurityDataCollection;
|
||||
use App\Framework\Http\Session\ValueObjects\SessionData;
|
||||
use App\Framework\Http\Session\ValueObjects\ValidationErrorCollection;
|
||||
use App\Framework\Random\SecureRandomGenerator;
|
||||
use App\Framework\Security\CsrfToken;
|
||||
use App\Framework\Security\CsrfTokenGenerator;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->clock = new SystemClock();
|
||||
$this->tokenGenerator = new CsrfTokenGenerator(new SecureRandomGenerator());
|
||||
$this->sessionId = SessionId::fromString('test-session-' . uniqid());
|
||||
|
||||
$this->session = Session::fromArray(
|
||||
$this->sessionId,
|
||||
$this->clock,
|
||||
$this->tokenGenerator,
|
||||
[]
|
||||
);
|
||||
|
||||
$this->csrfProtection = $this->session->csrf;
|
||||
});
|
||||
|
||||
it('generates a new token every time', function () {
|
||||
$formId = 'test-form';
|
||||
|
||||
$token1 = $this->csrfProtection->generateToken($formId);
|
||||
$token2 = $this->csrfProtection->generateToken($formId);
|
||||
|
||||
// Tokens should be different (no reuse)
|
||||
expect($token1->toString())->not->toBe($token2->toString());
|
||||
});
|
||||
|
||||
it('generates valid 64-character hex tokens', function () {
|
||||
$token = $this->csrfProtection->generateToken('test-form');
|
||||
|
||||
expect($token->toString())->toHaveLength(64);
|
||||
expect(ctype_xdigit($token->toString()))->toBeTrue();
|
||||
});
|
||||
|
||||
it('stores multiple tokens per form', function () {
|
||||
$formId = 'test-form';
|
||||
|
||||
$token1 = $this->csrfProtection->generateToken($formId);
|
||||
$token2 = $this->csrfProtection->generateToken($formId);
|
||||
$token3 = $this->csrfProtection->generateToken($formId);
|
||||
|
||||
$count = $this->csrfProtection->getActiveTokenCount($formId);
|
||||
|
||||
// Should have 3 tokens
|
||||
expect($count)->toBe(3);
|
||||
});
|
||||
|
||||
it('validates correct token', function () {
|
||||
$formId = 'test-form';
|
||||
$token = $this->csrfProtection->generateToken($formId);
|
||||
|
||||
$result = $this->csrfProtection->validateTokenWithDebug($formId, $token);
|
||||
|
||||
expect($result['valid'])->toBeTrue();
|
||||
});
|
||||
|
||||
it('rejects invalid token', function () {
|
||||
$formId = 'test-form';
|
||||
$this->csrfProtection->generateToken($formId);
|
||||
|
||||
$invalidToken = CsrfToken::fromString(str_repeat('0', 64));
|
||||
|
||||
$result = $this->csrfProtection->validateTokenWithDebug($formId, $invalidToken);
|
||||
|
||||
expect($result['valid'])->toBeFalse();
|
||||
expect($result['debug']['reason'])->toBe('No matching token found in session');
|
||||
});
|
||||
|
||||
it('allows token reuse within resubmit window', function () {
|
||||
$formId = 'test-form';
|
||||
$token = $this->csrfProtection->generateToken($formId);
|
||||
|
||||
// First validation
|
||||
$result1 = $this->csrfProtection->validateTokenWithDebug($formId, $token);
|
||||
expect($result1['valid'])->toBeTrue();
|
||||
|
||||
// Second validation within resubmit window (should still work)
|
||||
$result2 = $this->csrfProtection->validateTokenWithDebug($formId, $token);
|
||||
expect($result2['valid'])->toBeTrue();
|
||||
});
|
||||
|
||||
it('limits tokens per form to maximum', function () {
|
||||
$formId = 'test-form';
|
||||
|
||||
// Generate more than MAX_TOKENS_PER_FORM (3)
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$this->csrfProtection->generateToken($formId);
|
||||
}
|
||||
|
||||
$count = $this->csrfProtection->getActiveTokenCount($formId);
|
||||
|
||||
// Should be limited to 3
|
||||
expect($count)->toBeLessThanOrEqual(3);
|
||||
});
|
||||
|
||||
it('handles multiple forms independently', function () {
|
||||
$formId1 = 'form-1';
|
||||
$formId2 = 'form-2';
|
||||
|
||||
$token1 = $this->csrfProtection->generateToken($formId1);
|
||||
$token2 = $this->csrfProtection->generateToken($formId2);
|
||||
|
||||
// Tokens should be different
|
||||
expect($token1->toString())->not->toBe($token2->toString());
|
||||
|
||||
// Each form should have its own token
|
||||
expect($this->csrfProtection->getActiveTokenCount($formId1))->toBe(1);
|
||||
expect($this->csrfProtection->getActiveTokenCount($formId2))->toBe(1);
|
||||
|
||||
// Validation should work independently
|
||||
expect($this->csrfProtection->validateToken($formId1, $token1))->toBeTrue();
|
||||
expect($this->csrfProtection->validateToken($formId2, $token2))->toBeTrue();
|
||||
|
||||
// Cross-validation should fail
|
||||
expect($this->csrfProtection->validateToken($formId1, $token2))->toBeFalse();
|
||||
expect($this->csrfProtection->validateToken($formId2, $token1))->toBeFalse();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user