1<?php
2
3declare(strict_types=1);
4
5namespace ZnoteX\Tests\Security;
6
7use PHPUnit\Framework\TestCase;
8
9final class CsrfTest extends TestCase
10{
11 protected function setUp(): void
12 {
13 $_SESSION = [];
14 $_POST = [];
15 }
16
17 public function testTokenIsGeneratedOnFirstUse(): void
18 {
19 $this->assertArrayNotHasKey('acp_csrf', $_SESSION);
20 $token = \acp_csrf();
21 $this->assertNotSame('', $token);
22 $this->assertSame($token, $_SESSION['acp_csrf']);
23 }
24
25 public function testTokenIsStableAcrossCalls(): void
26 {
27 $first = \acp_csrf();
28 $second = \acp_csrf();
29 $this->assertSame($first, $second);
30 }
31
32 public function testFieldEmbedsTheCurrentToken(): void
33 {
34 $token = \acp_csrf();
35 $field = \acp_csrf_field();
36 $this->assertStringContainsString('name="csrf_token"', $field);
37 $this->assertStringContainsString(htmlspecialchars($token, ENT_QUOTES, 'UTF-8'), $field);
38 }
39
40 public function testVerifyRejectsMissingToken(): void
41 {
42 \acp_csrf();
43 $_POST = [];
44 $this->assertFalse(\acp_verify_csrf());
45 }
46
47 public function testVerifyRejectsWrongToken(): void
48 {
49 \acp_csrf();
50 $_POST['csrf_token'] = 'attacker-supplied-value';
51 $this->assertFalse(\acp_verify_csrf());
52 }
53
54 public function testVerifyRejectsNonStringToken(): void
55 {
56 \acp_csrf();
57 $_POST['csrf_token'] = ['not', 'a', 'string'];
58 $this->assertFalse(\acp_verify_csrf());
59 }
60
61 public function testVerifyAcceptsTheRealToken(): void
62 {
63 $token = \acp_csrf();
64 $_POST['csrf_token'] = $token;
65 $this->assertTrue(\acp_verify_csrf());
66 }
67
68 public function testEachSessionGetsAnIndependentToken(): void
69 {
70 $tokenA = \acp_csrf();
71 $_SESSION = [];
72 $tokenB = \acp_csrf();
73 $this->assertNotSame($tokenA, $tokenB);
74 }
75}
76