1313
1414namespace CodeIgniter \Filters ;
1515
16+ use CodeIgniter \Config \Factories ;
17+ use CodeIgniter \Config \Services ;
1618use CodeIgniter \HTTP \CLIRequest ;
1719use CodeIgniter \HTTP \IncomingRequest ;
20+ use CodeIgniter \HTTP \RedirectResponse ;
1821use CodeIgniter \HTTP \Response ;
22+ use CodeIgniter \Security \Exceptions \SecurityException ;
1923use CodeIgniter \Test \CIUnitTestCase ;
24+ use CodeIgniter \Test \Mock \MockSecurity ;
25+ use Config \Security as SecurityConfig ;
2026use PHPUnit \Framework \Attributes \BackupGlobals ;
2127use PHPUnit \Framework \Attributes \Group ;
2228
@@ -37,6 +43,14 @@ protected function setUp(): void
3743 $ this ->config = new \Config \Filters ();
3844 }
3945
46+ protected function tearDown (): void
47+ {
48+ parent ::tearDown ();
49+
50+ $ this ->resetServices ();
51+ Factories::reset ('config ' );
52+ }
53+
4054 public function testDoNotCheckCliRequest (): void
4155 {
4256 $ this ->config ->globals = [
@@ -73,4 +87,99 @@ public function testPassGetRequest(): void
7387 // GET request is not protected, so no SecurityException will be thrown.
7488 $ this ->assertSame ($ this ->request , $ request );
7589 }
90+
91+ public function testBeforeAddsVaryHeaderForFetchMetadataVerification (): void
92+ {
93+ $ filter = new CSRF ();
94+ $ request = single_service ('incomingrequest ' , null )
95+ ->withMethod ('POST ' )
96+ ->setHeader ('Sec-Fetch-Site ' , 'same-origin ' );
97+
98+ $ filter ->before ($ request );
99+
100+ $ this ->assertSame ('Sec-Fetch-Site ' , service ('response ' )->getHeaderLine ('Vary ' ));
101+ }
102+
103+ public function testBeforeAddsVaryHeaderWhenFetchMetadataFallsBackToToken (): void
104+ {
105+ service ('superglobals ' )
106+ ->setServer ('REQUEST_METHOD ' , 'POST ' )
107+ ->setPost ('csrf_test_name ' , '8b9218a55906f9dcc1dc263dce7f005a ' )
108+ ->setCookie ('csrf_cookie_name ' , '8b9218a55906f9dcc1dc263dce7f005a ' );
109+
110+ $ filter = new CSRF ();
111+ $ request = single_service ('incomingrequest ' , null )
112+ ->withMethod ('POST ' );
113+
114+ $ filter ->before ($ request );
115+
116+ $ this ->assertSame ('Sec-Fetch-Site ' , service ('response ' )->getHeaderLine ('Vary ' ));
117+ }
118+
119+ public function testBeforeAppendsVaryHeaderForFetchMetadataVerification (): void
120+ {
121+ $ filter = new CSRF ();
122+ $ request = single_service ('incomingrequest ' , null )
123+ ->withMethod ('POST ' )
124+ ->setHeader ('Sec-Fetch-Site ' , 'same-origin ' );
125+ service ('response ' )->setHeader ('Vary ' , 'Accept-Language ' );
126+
127+ $ filter ->before ($ request );
128+
129+ $ this ->assertSame ('Accept-Language, Sec-Fetch-Site ' , service ('response ' )->getHeaderLine ('Vary ' ));
130+ }
131+
132+ public function testBeforeAddsVaryHeaderToRedirectResponseForFetchMetadataVerification (): void
133+ {
134+ $ config = new SecurityConfig ();
135+ $ config ->redirect = true ;
136+ Factories::injectMock ('config ' , 'Security ' , $ config );
137+
138+ $ filter = new CSRF ();
139+ $ request = single_service ('incomingrequest ' , null )
140+ ->withMethod ('POST ' )
141+ ->setHeader ('Sec-Fetch-Site ' , 'cross-site ' );
142+
143+ $ response = $ filter ->before ($ request );
144+
145+ $ this ->assertInstanceOf (RedirectResponse::class, $ response );
146+ $ this ->assertSame ('Sec-Fetch-Site ' , $ response ->getHeaderLine ('Vary ' ));
147+ }
148+
149+ public function testBeforeThrowsExceptionForRejectedFetchMetadataVerification (): void
150+ {
151+ $ filter = new CSRF ();
152+ $ request = single_service ('incomingrequest ' , null )
153+ ->withMethod ('POST ' )
154+ ->setHeader ('Sec-Fetch-Site ' , 'cross-site ' );
155+
156+ try {
157+ $ filter ->before ($ request );
158+
159+ $ this ->fail ('Expected SecurityException was not thrown. ' );
160+ } catch (SecurityException ) {
161+ $ this ->assertSame ('Sec-Fetch-Site ' , service ('response ' )->getHeaderLine ('Vary ' ));
162+ }
163+ }
164+
165+ public function testBeforeUsesSecurityServiceConfigForVaryHeader (): void
166+ {
167+ service ('superglobals ' )
168+ ->setServer ('REQUEST_METHOD ' , 'POST ' )
169+ ->setPost ('csrf_test_name ' , '8b9218a55906f9dcc1dc263dce7f005a ' )
170+ ->setCookie ('csrf_cookie_name ' , '8b9218a55906f9dcc1dc263dce7f005a ' );
171+
172+ $ config = new SecurityConfig ();
173+ $ config ->csrfFetchMetadata = false ;
174+ Services::injectMock ('security ' , new MockSecurity ($ config ));
175+
176+ $ filter = new CSRF ();
177+ $ request = single_service ('incomingrequest ' , null )
178+ ->withMethod ('POST ' )
179+ ->setHeader ('Sec-Fetch-Site ' , 'same-origin ' );
180+
181+ $ filter ->before ($ request );
182+
183+ $ this ->assertSame ('' , service ('response ' )->getHeaderLine ('Vary ' ));
184+ }
76185}
0 commit comments