| @@ -23,9 +23,9 @@ | ||
| 23 | 23 | if ( $this->get_option( 'patchstack_license_free', 0 ) == 1 ) { |
| 24 | 24 | return; |
| 25 | 25 | } |
| 26 | 26 | |
| 27 | - add_action( 'updated_option', array( $this, 'update_option_extras' ), 10, 3 ); | |
| 27 | + add_action( 'updated_option', [ $this, 'update_option_extras' ], 10, 3 ); | |
| 28 | 28 | } |
| 29 | 29 | |
| 30 | 30 | /** |
| 31 | 31 | * If option is updated, write to .htaccess file. |
| @@ -35,11 +35,17 @@ | ||
| 35 | 35 | * @param mixed $value |
| 36 | 36 | * @return void |
| 37 | 37 | */ |
| 38 | 38 | public function update_option_extras( $option_name, $old_value, $value ) { |
| 39 | - if ( in_array( $option_name, array( 'patchstack_prevent_default_file_access', 'patchstack_basic_firewall', 'patchstack_pingback_protection', 'patchstack_block_debug_log_access', 'patchstack_block_fake_bots', 'patchstack_index_views', 'patchstack_trace_and_track', 'patchstack_proxy_comment_posting', 'patchstack_image_hotlinking', 'patchstack_firewall_custom_rules' ) ) ) { | |
| 40 | - $this->plugin->rules->post_firewall_rules(); | |
| 39 | + if ( !in_array( $option_name, [ 'patchstack_prevent_default_file_access', 'patchstack_basic_firewall', 'patchstack_pingback_protection', 'patchstack_block_debug_log_access', 'patchstack_block_fake_bots', 'patchstack_index_views', 'patchstack_trace_and_track', 'patchstack_proxy_comment_posting', 'patchstack_image_hotlinking', 'patchstack_firewall_custom_rules' ] ) ) { | |
| 40 | + return; | |
| 41 | 41 | } |
| 42 | + | |
| 43 | + if ( $old_value == $value ) { | |
| 44 | + return; | |
| 45 | + } | |
| 46 | + | |
| 47 | + $this->plugin->rules->post_firewall_rules(); | |
| 42 | 48 | } |
| 43 | 49 | |
| 44 | 50 | /** |
| 45 | 51 | * Get the turned on .htaccess firewall settings. |
| @@ -46,10 +52,10 @@ | ||
| 46 | 52 | * |
| 47 | 53 | * @return array |
| 48 | 54 | */ |
| 49 | 55 | public function get_firewall_rule_settings() { |
| 50 | - $settings = array(); | |
| 51 | - $options = array( 'patchstack_prevent_default_file_access', 'patchstack_basic_firewall', 'patchstack_block_debug_log_access', 'patchstack_block_fake_bots', 'patchstack_index_views', 'patchstack_proxy_comment_posting', 'patchstack_image_hotlinking', 'patchstack_basicscanblock' ); | |
| 56 | + $settings = []; | |
| 57 | + $options = [ 'patchstack_prevent_default_file_access', 'patchstack_basic_firewall', 'patchstack_block_debug_log_access', 'patchstack_block_fake_bots', 'patchstack_index_views', 'patchstack_proxy_comment_posting', 'patchstack_image_hotlinking', 'patchstack_basicscanblock' ]; | |
| 52 | 58 | foreach ( $options as $option ) { |
| 53 | 59 | if ( get_site_option( $option ) ) { |
| 54 | 60 | $settings[] = ( $option == 'patchstack_basicscanblock' ? 'webarx_wpscan_block' : str_replace( 'patchstack_', 'webarx_', $option ) ); |
| 55 | 61 | } |
| @@ -65,9 +71,9 @@ | ||
| 65 | 71 | */ |
| 66 | 72 | public function firewall() { |
| 67 | 73 | // Get the firewall state. |
| 68 | 74 | $sum_of_firewall = 0; |
| 69 | - foreach ( array( 'patchstack_prevent_default_file_access', 'patchstack_basic_firewall', 'patchstack_pingback_protection', 'patchstack_block_debug_log_access', 'patchstack_block_fake_bots', 'patchstack_index_views', 'patchstack_trace_and_track', 'patchstack_proxy_comment_posting', 'patchstack_image_hotlinking' ) as $option ) { | |
| 75 | + foreach ( [ 'patchstack_prevent_default_file_access', 'patchstack_basic_firewall', 'patchstack_pingback_protection', 'patchstack_block_debug_log_access', 'patchstack_block_fake_bots', 'patchstack_index_views', 'patchstack_trace_and_track', 'patchstack_proxy_comment_posting', 'patchstack_image_hotlinking' ] as $option ) { | |
| 70 | 76 | $value = get_site_option( $option, 0 ); |
| 71 | 77 | $sum_of_firewall += empty( $value ) ? 0 : 1; |
| 72 | 78 | } |
| 73 | 79 | |
| @@ -72,9 +78,9 @@ | ||
| 72 | 78 | } |
| 73 | 79 | |
| 74 | 80 | // Update the options. |
| 75 | 81 | $onoff = $sum_of_firewall > 1 ? 0 : 1; |
| 76 | - foreach ( array( 'patchstack_prevent_default_file_access', 'patchstack_basic_firewall', 'patchstack_block_debug_log_access', 'patchstack_index_views', 'patchstack_proxy_comment_posting' ) as $option ) { | |
| 82 | + foreach ( [ 'patchstack_prevent_default_file_access', 'patchstack_basic_firewall', 'patchstack_block_debug_log_access', 'patchstack_index_views', 'patchstack_proxy_comment_posting' ] as $option ) { | |
| 77 | 83 | update_site_option( $option, $onoff ); |
| 78 | 84 | } |
| 79 | 85 | update_site_option( 'patchstack_block_fake_bots', 0 ); |
| 80 | 86 | update_site_option( 'patchstack_image_hotlinking', 0 ); |
| @@ -95,9 +101,9 @@ | ||
| 95 | 101 | * @param string $rules |
| 96 | 102 | * @return void |
| 97 | 103 | */ |
| 98 | 104 | public function write_rules_to_htaccess( $rules = '' ) { |
| 99 | - if ( ! $this->is_server_supported() || get_site_option( 'patchstack_disable_htaccess', 0 ) ) { | |
| 105 | + if ( ! $this->is_server_supported() || get_site_option( 'patchstack_disable_htaccess', 0 ) || ( defined( 'PS_DISABLE_HTACCESS' ) && PS_DISABLE_HTACCESS ) ) { | |
| 100 | 106 | return false; |
| 101 | 107 | } |
| 102 | 108 | |
| 103 | 109 | // Determine if the .htaccess file exists. |
| @@ -114,9 +120,9 @@ | ||
| 114 | 120 | } |
| 115 | 121 | |
| 116 | 122 | // Get the current rules. |
| 117 | 123 | $current = $old = $fs->get_contents( ABSPATH . '.htaccess' ); |
| 118 | - $current = $this->delete_all_between( '# Patchstack Firewall Start', "# Patchstack Firewall End\r\n", $current ); | |
| 124 | + $current = $this->delete_all_between( '# Patchstack Firewall Start', '# Patchstack Firewall End', $current ); | |
| 119 | 125 | |
| 120 | 126 | // If no rules, then we delete the old ones. |
| 121 | 127 | if ( $rules != '' ) { |
| 122 | 128 | $current = "# Patchstack Firewall Start\r\n<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\n" . $rules . "\r\n</IfModule>\r\n# Patchstack Firewall End\r\n" . $current; |
| @@ -142,9 +148,9 @@ | ||
| 142 | 148 | * @param string $rules |
| 143 | 149 | * @return boolean |
| 144 | 150 | */ |
| 145 | 151 | public function write_to_htaccess( $rules = '' ) { |
| 146 | - if ( ! $this->is_server_supported() || get_site_option( 'patchstack_disable_htaccess', 0 ) ) { | |
| 152 | + if ( ! $this->is_server_supported() || get_site_option( 'patchstack_disable_htaccess', 0 ) || ( defined( 'PS_DISABLE_HTACCESS' ) && PS_DISABLE_HTACCESS ) ) { | |
| 147 | 153 | return false; |
| 148 | 154 | } |
| 149 | 155 | |
| 150 | 156 | require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; |
| @@ -153,8 +159,22 @@ | ||
| 153 | 159 | if ( ! $fs->exists( ABSPATH . '.htaccess' ) ) { |
| 154 | 160 | $fs->touch( ABSPATH . '.htaccess' ); |
| 155 | 161 | } |
| 156 | 162 | |
| 163 | + // Some rule adjustments. | |
| 164 | + $rewrites = [ | |
| 165 | + 'RedirectMatch 409 .(htaccess|htpasswd|errordocs|logs)$' => 'RedirectMatch 403 \.(htaccess|htpasswd|errordocs|logs)$', | |
| 166 | + "\n RewriteCond %{HTTP_COOKIE} !^.*wordpress_logged_in.*$ [NC]" => '', | |
| 167 | + "\n RewriteCond %{REMOTE_ADDR} !=18.221.197.243" => '', | |
| 168 | + '^wp-includes/[^/]+.php$' => '^wp-includes/.*\.php$', | |
| 169 | + 'RewriteRule ^debug*.*log$ index.php?webarx_fpage=502 [L,QSA]' => 'RewriteRule debug\.log$ index.php?webarx_fpage=502 [L,QSA]', | |
| 170 | + '*.*' => '\.' | |
| 171 | + ]; | |
| 172 | + | |
| 173 | + foreach ($rewrites as $find => $replace) { | |
| 174 | + $rules = str_replace($find, $replace, $rules); | |
| 175 | + } | |
| 176 | + | |
| 157 | 177 | return $this->plugin->htaccess->self_check( $rules ); |
| 158 | 178 | } |
| 159 | 179 | |
| 160 | 180 | /** |
| @@ -162,10 +182,10 @@ | ||
| 162 | 182 | * |
| 163 | 183 | * @return boolean |
| 164 | 184 | */ |
| 165 | 185 | public function is_server_supported() { |
| 166 | - $server = strtolower( filter_var( $_SERVER['SERVER_SOFTWARE'], FILTER_SANITIZE_STRING ) ); | |
| 167 | - foreach ( array( 'apache', 'nginx', 'litespeed' ) as $webserver ) { | |
| 186 | + $server = strtolower( $_SERVER['SERVER_SOFTWARE'] ); | |
| 187 | + foreach ( [ 'apache', 'nginx', 'litespeed' ] as $webserver ) { | |
| 168 | 188 | if ( strstr( $server, $webserver ) ) { |
| 169 | 189 | return true; |
| 170 | 190 | } |
| 171 | 191 | } |
| @@ -325,8 +345,16 @@ | ||
| 325 | 345 | if ( $begin_pos === false || $end_pos === false ) { |
| 326 | 346 | return $string; |
| 327 | 347 | } |
| 328 | 348 | |
| 329 | - $delete = substr( $string, $begin_pos, ( $end_pos + strlen( $end ) ) - $begin_pos ); | |
| 349 | + $end_pos += strlen( $end ); | |
| 350 | + | |
| 351 | + // Consume any trailing newline characters so the block is removed cleanly | |
| 352 | + // regardless of CRLF/LF line endings and repeated writes don't stack blank lines. | |
| 353 | + while ( isset( $string[ $end_pos ] ) && ( $string[ $end_pos ] === "\r" || $string[ $end_pos ] === "\n" ) ) { | |
| 354 | + $end_pos++; | |
| 355 | + } | |
| 356 | + | |
| 357 | + $delete = substr( $string, $begin_pos, $end_pos - $begin_pos ); | |
| 330 | 358 | return str_replace( $delete, '', $string ); |
| 331 | 359 | } |
| 332 | 360 | } |