Changeset 64369
- Timestamp:
- 10/15/2026 10:32:10 PM (29 hours ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/tests/phpunit/tests/error-protection/wpRecoveryModeCookieService.php
r60729 r64369 7 7 8 8 /** 9 * Whether the recovery mode cookie was set before the test. 10 * 11 * @var bool 12 */ 13 private $had_cookie; 14 15 /** 16 * Original value of the recovery mode cookie. 17 * 18 * @var mixed 19 */ 20 private $orig_cookie; 21 22 public function set_up() { 23 parent::set_up(); 24 25 $this->had_cookie = array_key_exists( RECOVERY_MODE_COOKIE, $_COOKIE ); 26 $this->orig_cookie = $this->had_cookie ? $_COOKIE[ RECOVERY_MODE_COOKIE ] : null; 27 28 unset( $_COOKIE[ RECOVERY_MODE_COOKIE ] ); 29 } 30 31 public function tear_down() { 32 if ( $this->had_cookie ) { 33 $_COOKIE[ RECOVERY_MODE_COOKIE ] = $this->orig_cookie; 34 } else { 35 unset( $_COOKIE[ RECOVERY_MODE_COOKIE ] ); 36 } 37 38 parent::tear_down(); 39 } 40 41 /** 42 * Builds a signed recovery mode cookie value. 43 * 44 * @param int $created_at Timestamp the cookie was created at. 45 * @param string $random Random part of the cookie, used to derive the session ID. 46 * @return string Cookie value. 47 */ 48 private function build_cookie( $created_at, $random ) { 49 $service = new WP_Recovery_Mode_Cookie_Service(); 50 $reflection = new ReflectionMethod( $service, 'recovery_mode_hash' ); 51 if ( PHP_VERSION_ID < 80100 ) { 52 $reflection->setAccessible( true ); 53 } 54 55 $to_sign = sprintf( 'recovery_mode|%s|%s', $created_at, $random ); 56 57 return base64_encode( sprintf( '%s|%s', $to_sign, $reflection->invoke( $service, $to_sign ) ) ); 58 } 59 60 /** 9 61 * @ticket 46130 10 62 * … … 105 157 $this->assertTrue( $service->validate_cookie( $reflection->invoke( $service ) ) ); 106 158 } 159 160 /** 161 * @ticket 65819 162 * 163 * @covers WP_Recovery_Mode_Cookie_Service::is_cookie_set 164 * 165 * @dataProvider data_is_cookie_set 166 * 167 * @param mixed $cookie Cookie value, or null to leave the cookie unset. 168 * @param bool $expected Whether the cookie counts as set. 169 */ 170 public function test_is_cookie_set( $cookie, $expected ) { 171 if ( null !== $cookie ) { 172 $_COOKIE[ RECOVERY_MODE_COOKIE ] = $cookie; 173 } 174 175 $service = new WP_Recovery_Mode_Cookie_Service(); 176 177 $this->assertSame( $expected, $service->is_cookie_set() ); 178 } 179 180 /** 181 * Data provider. 182 * 183 * @return array[] 184 */ 185 public static function data_is_cookie_set() { 186 return array( 187 'cookie not set' => array( null, false ), 188 'empty string' => array( '', false ), 189 'zero string' => array( '0', false ), 190 'cookie value' => array( 'cmVjb3ZlcnlfbW9kZQ==', true ), 191 ); 192 } 193 194 /** 195 * @ticket 65819 196 * 197 * @covers WP_Recovery_Mode_Cookie_Service::validate_cookie 198 * 199 * @dataProvider data_missing_cookies 200 * 201 * @param mixed $cookie Cookie value, or null to leave the cookie unset. 202 */ 203 public function test_validate_cookie_returns_wp_error_if_no_cookie_is_present( $cookie ) { 204 if ( null !== $cookie ) { 205 $_COOKIE[ RECOVERY_MODE_COOKIE ] = $cookie; 206 } 207 208 $service = new WP_Recovery_Mode_Cookie_Service(); 209 210 $error = $service->validate_cookie(); 211 $this->assertWPError( $error ); 212 $this->assertSame( 'no_cookie', $error->get_error_code() ); 213 } 214 215 /** 216 * Data provider. 217 * 218 * @return array[] 219 */ 220 public static function data_missing_cookies() { 221 return array( 222 'cookie not set' => array( null ), 223 'empty string' => array( '' ), 224 'zero string' => array( '0' ), 225 ); 226 } 227 228 /** 229 * @ticket 65819 230 * 231 * @covers WP_Recovery_Mode_Cookie_Service::validate_cookie 232 */ 233 public function test_validate_cookie_reads_cookie_from_request() { 234 $_COOKIE[ RECOVERY_MODE_COOKIE ] = $this->build_cookie( time(), 'abcdefghij0123456789' ); 235 236 $service = new WP_Recovery_Mode_Cookie_Service(); 237 238 $this->assertTrue( $service->validate_cookie() ); 239 } 240 241 /** 242 * @ticket 65819 243 * 244 * @covers WP_Recovery_Mode_Cookie_Service::validate_cookie 245 */ 246 public function test_validate_cookie_prefers_cookie_argument_over_request() { 247 $_COOKIE[ RECOVERY_MODE_COOKIE ] = $this->build_cookie( time(), 'abcdefghij0123456789' ); 248 249 $service = new WP_Recovery_Mode_Cookie_Service(); 250 251 $error = $service->validate_cookie( 'gibberish' ); 252 $this->assertWPError( $error ); 253 $this->assertSame( 'invalid_format', $error->get_error_code() ); 254 } 255 256 /** 257 * @ticket 65819 258 * 259 * @covers WP_Recovery_Mode_Cookie_Service::validate_cookie 260 * 261 * @dataProvider data_filtered_cookie_lengths 262 * 263 * @param int $age Age of the cookie in seconds. 264 * @param int $cookie_length Filtered cookie length in seconds. 265 * @param string|true $expected Expected error code, or true if the cookie is valid. 266 */ 267 public function test_validate_cookie_uses_filtered_cookie_length( $age, $cookie_length, $expected ) { 268 $filter = new MockAction(); 269 add_filter( 'recovery_mode_cookie_length', array( $filter, 'filter' ) ); 270 add_filter( 271 'recovery_mode_cookie_length', 272 static function () use ( $cookie_length ) { 273 return $cookie_length; 274 }, 275 20 276 ); 277 278 $service = new WP_Recovery_Mode_Cookie_Service(); 279 $result = $service->validate_cookie( $this->build_cookie( time() - $age, 'abcdefghij0123456789' ) ); 280 281 if ( true === $expected ) { 282 $this->assertTrue( $result ); 283 } else { 284 $this->assertWPError( $result ); 285 $this->assertSame( $expected, $result->get_error_code() ); 286 } 287 288 $this->assertSame( array( array( 604800 ) ), $filter->get_args(), 'The filter should receive the default length of one week.' ); 289 } 290 291 /** 292 * Data provider. 293 * 294 * @return array[] 295 */ 296 public static function data_filtered_cookie_lengths() { 297 return array( 298 'shorter length expires a recent cookie' => array( 2 * HOUR_IN_SECONDS, HOUR_IN_SECONDS, 'expired' ), 299 'zero length expires any older cookie' => array( MINUTE_IN_SECONDS, 0, 'expired' ), 300 'longer length keeps an old cookie' => array( 2 * WEEK_IN_SECONDS, 3 * WEEK_IN_SECONDS, true ), 301 ); 302 } 303 304 /** 305 * @ticket 65819 306 * 307 * @covers WP_Recovery_Mode_Cookie_Service::get_session_id_from_cookie 308 */ 309 public function test_get_session_id_from_cookie_returns_hash_of_random_part() { 310 $service = new WP_Recovery_Mode_Cookie_Service(); 311 $cookie = base64_encode( 'recovery_mode|1234567890|abcdefghij0123456789|signature' ); 312 313 $this->assertSame( '6256c24ff916241741c75edc27548d4ba4124a29', $service->get_session_id_from_cookie( $cookie ) ); 314 } 315 316 /** 317 * @ticket 65819 318 * 319 * @covers WP_Recovery_Mode_Cookie_Service::get_session_id_from_cookie 320 */ 321 public function test_get_session_id_from_cookie_reads_cookie_from_request() { 322 $_COOKIE[ RECOVERY_MODE_COOKIE ] = base64_encode( 'recovery_mode|1234567890|abcdefghij0123456789|signature' ); 323 324 $service = new WP_Recovery_Mode_Cookie_Service(); 325 326 $this->assertSame( '6256c24ff916241741c75edc27548d4ba4124a29', $service->get_session_id_from_cookie() ); 327 } 328 329 /** 330 * @ticket 65819 331 * 332 * @covers WP_Recovery_Mode_Cookie_Service::get_session_id_from_cookie 333 * 334 * @dataProvider data_missing_cookies 335 * 336 * @param mixed $cookie Cookie value, or null to leave the cookie unset. 337 */ 338 public function test_get_session_id_from_cookie_returns_wp_error_if_no_cookie_is_present( $cookie ) { 339 if ( null !== $cookie ) { 340 $_COOKIE[ RECOVERY_MODE_COOKIE ] = $cookie; 341 } 342 343 $service = new WP_Recovery_Mode_Cookie_Service(); 344 345 $error = $service->get_session_id_from_cookie(); 346 $this->assertWPError( $error ); 347 $this->assertSame( 'no_cookie', $error->get_error_code() ); 348 } 349 350 /** 351 * @ticket 65819 352 * 353 * @covers WP_Recovery_Mode_Cookie_Service::get_session_id_from_cookie 354 * 355 * @dataProvider data_cookies_with_invalid_format 356 * 357 * @param string $cookie Cookie value. 358 */ 359 public function test_get_session_id_from_cookie_returns_wp_error_if_invalid_format( $cookie ) { 360 $service = new WP_Recovery_Mode_Cookie_Service(); 361 362 $error = $service->get_session_id_from_cookie( $cookie ); 363 $this->assertWPError( $error ); 364 $this->assertSame( 'invalid_format', $error->get_error_code() ); 365 } 366 367 /** 368 * Data provider. 369 * 370 * @return array[] 371 */ 372 public static function data_cookies_with_invalid_format() { 373 return array( 374 'not base64 encoded' => array( 'gibberish' ), 375 'too few parts' => array( base64_encode( 'recovery_mode|1234567890|abcdefghij0123456789' ) ), 376 'too many parts' => array( base64_encode( 'recovery_mode|1234567890|abcdefghij0123456789|signature|extra' ) ), 377 ); 378 } 379 380 /** 381 * @ticket 65819 382 * 383 * @covers WP_Recovery_Mode_Cookie_Service::recovery_mode_hash 384 */ 385 public function test_recovery_mode_hash_uses_stored_key_and_salt_with_default_secret_keys() { 386 if ( 'put your unique phrase here' !== AUTH_KEY || 'put your unique phrase here' !== AUTH_SALT ) { 387 $this->markTestSkipped( 'This test requires the default AUTH_KEY and AUTH_SALT values.' ); 388 } 389 390 update_site_option( 'recovery_mode_auth_key', 'test-auth-key' ); 391 update_site_option( 'recovery_mode_auth_salt', 'test-auth-salt' ); 392 393 $service = new WP_Recovery_Mode_Cookie_Service(); 394 $reflection = new ReflectionMethod( $service, 'recovery_mode_hash' ); 395 if ( PHP_VERSION_ID < 80100 ) { 396 $reflection->setAccessible( true ); 397 } 398 399 $this->assertSame( 400 '3b83c8f3e016b4ed284de9e6300874a516e896a3', 401 $reflection->invoke( $service, 'recovery_mode|1234567890|abcdefghij0123456789' ) 402 ); 403 } 404 405 /** 406 * @ticket 65819 407 * 408 * @covers WP_Recovery_Mode_Cookie_Service::recovery_mode_hash 409 */ 410 public function test_recovery_mode_hash_generates_and_reuses_key_and_salt_with_default_secret_keys() { 411 if ( 'put your unique phrase here' !== AUTH_KEY || 'put your unique phrase here' !== AUTH_SALT ) { 412 $this->markTestSkipped( 'This test requires the default AUTH_KEY and AUTH_SALT values.' ); 413 } 414 415 delete_site_option( 'recovery_mode_auth_key' ); 416 delete_site_option( 'recovery_mode_auth_salt' ); 417 418 $service = new WP_Recovery_Mode_Cookie_Service(); 419 $reflection = new ReflectionMethod( $service, 'recovery_mode_hash' ); 420 if ( PHP_VERSION_ID < 80100 ) { 421 $reflection->setAccessible( true ); 422 } 423 424 $hash = $reflection->invoke( $service, 'recovery_mode|1234567890|abcdefghij0123456789' ); 425 $key = get_site_option( 'recovery_mode_auth_key' ); 426 $salt = get_site_option( 'recovery_mode_auth_salt' ); 427 428 $this->assertIsString( $key, 'A key should be generated and stored.' ); 429 $this->assertSame( 64, strlen( $key ), 'The stored key should be 64 characters long.' ); 430 $this->assertIsString( $salt, 'A salt should be generated and stored.' ); 431 $this->assertSame( 64, strlen( $salt ), 'The stored salt should be 64 characters long.' ); 432 $this->assertNotSame( $key, $salt, 'The key and salt should differ.' ); 433 434 $this->assertSame( $hash, $reflection->invoke( $service, 'recovery_mode|1234567890|abcdefghij0123456789' ), 'The same data should produce the same hash.' ); 435 $this->assertSame( $key, get_site_option( 'recovery_mode_auth_key' ), 'The stored key should be reused.' ); 436 $this->assertSame( $salt, get_site_option( 'recovery_mode_auth_salt' ), 'The stored salt should be reused.' ); 437 } 107 438 }
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)