PluginProbe
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… / 2.11.8
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… v2.11.8
3.0.0 2.11.12 2.11.11 2.11.10 2.11.9 2.11.7 2.11.8 2.11.6 2.11.5 2.11.4 2.11.3 2.11.1 2.11.2 2.11.0 2.10.5 2.10.4 2.10.3 2.10.2 2.10.1 2.10.0 2.9.9 2.9.8 2.9.6 2.9.7 2.9.5 All 88 releases
← All changes | includes/class-database-prefix.php +40 -18 2.9.92.11.8 View file →
@@ -11,10 +11,8 @@
11 11 if ( ! defined( 'ABSPATH' ) ) {
12 12 exit;
13 13 }
14 14
15 -// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
16 -
17 15 /**
18 16 * Class Vigilante_Database_Prefix
19 17 *
20 18 * Changes the WordPress database prefix safely
@@ -76,9 +74,9 @@
76 74 if ( ! is_multisite() ) {
77 75 return true;
78 76 }
79 77
80 - if ( ! is_main_site() ) {
78 + if ( ! is_main_site() || ! is_main_network() ) {
81 79 return new WP_Error(
82 80 'multisite_not_main_site',
83 81 __( 'The database prefix is shared by the whole network. Change it from the main site of the network.', 'vigilante' )
84 82 );
@@ -274,8 +272,9 @@
274 272 *
275 273 * @return array|WP_Error
276 274 */
277 275 private function get_site_prefix_map() {
276 + // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- table name comes from the validated prefix map and all values use placeholders.
278 277 if ( ! is_multisite() ) {
279 278 return array(
280 279 array(
281 280 'blog_id' => 1,
@@ -313,8 +312,9 @@
313 312 );
314 313 }
315 314
316 315 return $map;
316 + // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
317 317 }
318 318
319 319 /**
320 320 * Get all tables with the current prefix
@@ -321,8 +321,9 @@
321 321 *
322 322 * @return array Table names.
323 323 */
324 324 private function get_prefixed_tables() {
325 + // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder requires WP 6.2+, and the sniff reports inside the multiline prepare().
325 326 return $this->wpdb->get_col(
326 327 $this->wpdb->prepare(
327 328 'SHOW TABLES LIKE %s',
328 329 $this->wpdb->esc_like( $this->old_prefix ) . '%'
@@ -327,8 +328,9 @@
327 328 'SHOW TABLES LIKE %s',
328 329 $this->wpdb->esc_like( $this->old_prefix ) . '%'
329 330 )
330 331 );
332 + // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
331 333 }
332 334
333 335 /**
334 336 * Check if tables exist with a given prefix
@@ -336,8 +338,9 @@
336 338 * @param string $prefix Prefix to check.
337 339 * @return bool
338 340 */
339 341 private function prefix_tables_exist( $prefix ) {
342 + // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder requires WP 6.2+, and the sniff reports inside the multiline prepare().
340 343 $result = $this->wpdb->get_var(
341 344 $this->wpdb->prepare(
342 345 'SHOW TABLES LIKE %s',
343 346 $this->wpdb->esc_like( $prefix ) . '%'
@@ -344,8 +347,9 @@
344 347 )
345 348 );
346 349
347 350 return ! empty( $result );
351 + // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
348 352 }
349 353
350 354 /**
351 355 * Rename all tables from old prefix to new prefix
@@ -353,8 +357,9 @@
353 357 * @param array $tables List of table names.
354 358 * @return true|WP_Error
355 359 */
356 360 private function rename_tables( $tables ) {
361 + // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder requires WP 6.2+, and the sniff reports inside the multiline prepare().
357 362 $renamed = array();
358 363
359 364 foreach ( $tables as $old_name ) {
360 365 $new_name = $this->new_prefix . substr( $old_name, strlen( $this->old_prefix ) );
@@ -393,8 +398,9 @@
393 398 $renamed[ $new_name ] = $old_name;
394 399 }
395 400
396 401 return true;
402 + // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
397 403 }
398 404
399 405 /**
400 406 * Rollback table renames
@@ -401,8 +407,9 @@
401 407 *
402 408 * @param array $original_tables Original table names.
403 409 */
404 410 private function rollback_tables( $original_tables ) {
411 + // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder requires WP 6.2+, and the sniff reports inside the multiline prepare().
405 412 foreach ( $original_tables as $old_name ) {
406 413 $new_name = $this->new_prefix . substr( $old_name, strlen( $this->old_prefix ) );
407 414
408 415 // Check if new name exists (it was renamed)
@@ -417,8 +424,9 @@
417 424 )
418 425 );
419 426 }
420 427 }
428 + // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
421 429 }
422 430
423 431 /**
424 432 * Update $table_prefix in wp-config.php
@@ -432,14 +440,24 @@
432 440 if ( false === $content ) {
433 441 return new WP_Error( 'read_error', __( 'Cannot read wp-config.php.', 'vigilante' ) );
434 442 }
435 443
436 - // Back up the original file
437 - $backup_path = $this->wpconfig_path . '.vigilante-backup-' . gmdate( 'YmdHis' );
438 - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
439 - if ( ! file_put_contents( $backup_path, $content ) ) {
440 - return new WP_Error( 'backup_error', __( 'Cannot create wp-config.php backup.', 'vigilante' ) );
441 - }
444 + /*
445 + * The original file is held in $content, in memory, and that is the
446 + * whole backup this needs.
447 + *
448 + * Until 2.11.3 it also wrote a copy next to wp-config.php, named
449 + * wp-config.php.vigilante-backup-<timestamp>. On a standard install
450 + * that is the site root, the name is a predictable timestamp and it
451 + * carries no .php extension, so a server hands it over as plain text
452 + * with the database credentials and the eight salts inside. It was
453 + * deleted straight after, but a request that died in between left it
454 + * there for good, and that is exactly the moment when the owner is busy
455 + * with a site that will not load. Present since 1.2.0.
456 + *
457 + * Nothing is lost by removing it: that file was never read back. The
458 + * restore below, the only path that undoes anything, uses $content.
459 + */
442 460
443 461 // Match the $table_prefix line (handles single and double quotes, with/without spaces)
444 462 $pattern = '/(\$table_prefix\s*=\s*)([\'"]).+?\\2(\s*;)/';
445 463 $replacement = '${1}\'' . $this->new_prefix . '\'${3}';
@@ -446,10 +464,8 @@
446 464
447 465 $new_content = preg_replace( $pattern, $replacement, $content, 1, $count );
448 466
449 467 if ( 0 === $count || null === $new_content ) {
450 - // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
451 - unlink( $backup_path );
452 468 return new WP_Error( 'replace_error', __( 'Cannot find $table_prefix in wp-config.php.', 'vigilante' ) );
453 469 }
454 470
455 471 // Write updated content
@@ -456,20 +472,14 @@
456 472 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
457 473 $result = file_put_contents( $this->wpconfig_path, $new_content );
458 474
459 475 if ( false === $result ) {
460 - // Restore backup
476 + // Restore from memory, which is where the original has been all along.
461 477 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
462 478 file_put_contents( $this->wpconfig_path, $content );
463 - // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
464 - unlink( $backup_path );
465 479 return new WP_Error( 'write_error', __( 'Cannot write to wp-config.php.', 'vigilante' ) );
466 480 }
467 481
468 - // Clean up backup after successful write
469 - // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
470 - unlink( $backup_path );
471 -
472 482 $this->invalidate_wpconfig_opcode_cache();
473 483
474 484 return true;
475 485 }
@@ -674,8 +684,9 @@
674 684 * @param string $old_prefix Prefix to strip.
675 685 * @param string $new_prefix Prefix to write.
676 686 */
677 687 private function bulk_rename_options( $table, $old_prefix, $new_prefix ) {
688 + // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- %i placeholder requires WP 6.2+, and the sniff reports inside the multiline prepare(), and the table name comes from the validated prefix map and all values use placeholders.
678 689 $this->wpdb->query(
679 690 $this->wpdb->prepare(
680 691 "UPDATE `{$table}` SET option_name = CONCAT( %s, SUBSTRING( option_name, %d ) ) WHERE option_name LIKE %s",
681 692 $new_prefix,
@@ -682,8 +693,9 @@
682 693 strlen( $old_prefix ) + 1,
683 694 $this->wpdb->esc_like( $old_prefix ) . '%'
684 695 )
685 696 );
697 + // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
686 698 }
687 699
688 700 /**
689 701 * Rename every usermeta key that starts with a given prefix
@@ -695,8 +707,9 @@
695 707 * @param string $old_prefix Prefix to strip.
696 708 * @param string $new_prefix Prefix to write.
697 709 */
698 710 private function bulk_rename_usermeta( $table, $old_prefix, $new_prefix ) {
711 + // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- %i placeholder requires WP 6.2+, and the sniff reports inside the multiline prepare(), and the table name comes from the validated prefix map and all values use placeholders.
699 712 // Prefix migration has to rewrite meta_key values by definition, so the
700 713 // slow-query rule does not apply here.
701 714 // phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_key
702 715 $this->wpdb->query(
@@ -707,8 +720,9 @@
707 720 $this->wpdb->esc_like( $old_prefix ) . '%'
708 721 )
709 722 );
710 723 // phpcs:enable WordPress.DB.SlowDBQuery.slow_db_query_meta_key
724 + // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
711 725 }
712 726
713 727 /**
714 728 * Rename a single option, without ever overwriting an existing one
@@ -718,8 +732,9 @@
718 732 * @param string $new_name Wanted option name.
719 733 * @return bool Whether the option was renamed.
720 734 */
721 735 private function rename_option( $table, $old_name, $new_name ) {
736 + // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- %i placeholder requires WP 6.2+, and the sniff reports inside the multiline prepare(), and the table name comes from the validated prefix map and all values use placeholders.
722 737 if ( $old_name === $new_name || $this->option_exists( $table, $new_name ) ) {
723 738 return false;
724 739 }
725 740
@@ -737,8 +752,9 @@
737 752 array( 'option_id' => (int) $option_id ),
738 753 array( '%s' ),
739 754 array( '%d' )
740 755 );
756 + // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
741 757 }
742 758
743 759 /**
744 760 * Rename every row of a usermeta key
@@ -747,8 +763,9 @@
747 763 * @param string $old_key Current meta key.
748 764 * @param string $new_key Wanted meta key.
749 765 */
750 766 private function rename_usermeta( $table, $old_key, $new_key ) {
767 + // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- %i placeholder requires WP 6.2+, and the sniff reports inside the multiline prepare(), and the table name comes from the validated prefix map and all values use placeholders.
751 768 if ( $old_key === $new_key ) {
752 769 return;
753 770 }
754 771
@@ -762,8 +779,9 @@
762 779 $old_key
763 780 )
764 781 );
765 782 // phpcs:enable WordPress.DB.SlowDBQuery.slow_db_query_meta_key
783 + // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
766 784 }
767 785
768 786 /**
769 787 * Whether an option name exists in a given options table
@@ -772,13 +790,15 @@
772 790 * @param string $option_name Option name.
773 791 * @return bool
774 792 */
775 793 private function option_exists( $table, $option_name ) {
794 + // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- %i placeholder requires WP 6.2+, and the sniff reports inside the multiline prepare(), and the table name comes from the validated prefix map and all values use placeholders.
776 795 $found = $this->wpdb->get_var(
777 796 $this->wpdb->prepare( "SELECT option_id FROM `{$table}` WHERE option_name = %s LIMIT 1", $option_name )
778 797 );
779 798
780 799 return ! empty( $found );
800 + // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
781 801 }
782 802
783 803 /**
784 804 * Whether a table exists
@@ -786,13 +806,15 @@
786 806 * @param string $table Table name.
787 807 * @return bool
788 808 */
789 809 private function table_exists( $table ) {
810 + // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder requires WP 6.2+, and the sniff reports inside the multiline prepare().
790 811 $found = $this->wpdb->get_var(
791 812 $this->wpdb->prepare( 'SHOW TABLES LIKE %s', $this->wpdb->esc_like( $table ) )
792 813 );
793 814
794 815 return ! empty( $found );
816 + // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
795 817 }
796 818
797 819 /**
798 820 * Drop cached copies of everything the rename touched