PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.8.0
Defender Security – Malware Scanner, Login Security & Firewall v5.8.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / component / security-tweaks / servers / class-apache.php

class-apache.php in Defender Security – Malware Scanner, Login Security & Firewall 5.8.0, at src/component/security-tweaks/servers/class-apache.php

653 lines 19.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Responsible for managing server configurations related to security tweaks .
4 *
5 * @package WP_Defender\Component\Security_Tweaks\Servers
6 */
7
8 namespace WP_Defender\Component\Security_Tweaks\Servers;
9
10 use WP_Error;
11 use WP_Filesystem_Base;
12
13 /**
14 * Provides methods to apply and revert security rules on servers.
15 */
16 class Apache {
17
18 public const CACHE_APACHE_VERSION = 'defender_apache_version';
19
20 /**
21 * Exclude file paths.
22 *
23 * @var array
24 */
25 public $new_htaccess_config = array();
26
27 /**
28 * The htaccess inside wp-content.
29 *
30 * @var string
31 */
32 public $contentdir_path = null;
33
34 /**
35 * The htaccess path inside wp-includes.
36 *
37 * @var null
38 */
39 public $includedir_path = null;
40
41 /**
42 * Service type.
43 *
44 * @var string
45 */
46 private $type = null;
47
48 /**
49 * Constructor for class.
50 *
51 * @param string $type The type of the security tweak.
52 */
53 public function __construct( $type ) {
54 $this->type = $type;
55 }
56
57 /**
58 * Check whether the issue has been resolved or not.
59 *
60 * @return bool
61 */
62 public function check() {
63 $url = '';
64
65 if ( 'prevent-php-executed' === $this->type ) {
66 $dir = wp_upload_dir();
67 $url = $dir['baseurl'] . '/wp-defender/index.php';
68 }
69
70 if ( 'protect-information' === $this->type ) {
71 $url = defender_asset_url( '/languages/' . WP_DEFENDER_POT_FILENAME );
72 }
73
74 return Server::ping_test_failed( $url );
75 }
76
77 /**
78 * Retrieves a WP_Error object based on the provided file path.
79 *
80 * @param string $file_path The path to the file.
81 *
82 * @return WP_Error Returns a WP_Error indicating the file is not writable.
83 */
84 public function get_error_by_file_path( $file_path ) {
85 return new WP_Error(
86 'defender_file_not_writable',
87 sprintf(
88 /* translators: %s - file path */
89 esc_html__( 'The file %s is not writable', 'defender-security' ),
90 $file_path
91 )
92 );
93 }
94
95 /**
96 * Process the rule.
97 *
98 * @return bool|WP_Error
99 */
100 public function process() {
101 global $wp_filesystem;
102 // Initialize the WP filesystem, no more using 'file-put-contents' function.
103 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
104 require_once ABSPATH . '/wp-admin/includes/file.php';
105 WP_Filesystem();
106 }
107 if ( 'protect-information' === $this->type ) {
108 $ht_access_path = ABSPATH . '.htaccess';
109 // Create a htaccess-file if it doesn't exist.
110 if ( ! is_file( $ht_access_path )
111 && false === (bool) file_put_contents( $ht_access_path, '', LOCK_EX ) // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
112 ) {
113 return $this->get_error_by_file_path( $ht_access_path );
114 }
115 // If the htaccess-file is existed.
116 if ( ! $wp_filesystem->is_writable( $ht_access_path ) ) {
117 return $this->get_error_by_file_path( $ht_access_path );
118 }
119
120 $ht_access_config = file( $ht_access_path );
121 $ht_access_config = array_map( 'trim', $ht_access_config );
122 $rules = $this->get_rules();
123 $contains_search = array_diff( array_map( 'trim', $rules ), $ht_access_config );
124
125 if ( count( $contains_search ) < count( $rules ) ) {
126 // Search the wrapper block.
127 $ht_access_content = $wp_filesystem->get_contents( $ht_access_path );
128 preg_match( '/## WP Defender(.*?)## WP Defender - End ##/s', $ht_access_content, $matches );
129
130 if ( count( $matches ) ) {
131 // Remove the whole parts as its partial done.
132 $ht_access_content = str_replace( $matches[0], '', $ht_access_content );
133 $ht_access_config = explode( PHP_EOL, $ht_access_content );
134 $ht_access_config = array_merge( $ht_access_config, $rules );
135
136 return (bool) file_put_contents( $ht_access_path, implode( PHP_EOL, $ht_access_config ), LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
137 }
138 }
139
140 if ( 0 === count( $contains_search ) || ( count( $contains_search ) === count( $rules ) ) ) {
141 $ht_access_config = array_merge( $ht_access_config, $rules );
142
143 return (bool) file_put_contents( $ht_access_path, implode( PHP_EOL, $ht_access_config ), LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
144 }
145 }
146
147 if ( 'prevent-php-executed' === $this->type ) {
148 if ( ! in_array( Server::get_current_server(), array( 'apache', 'litespeed' ), true ) ) {
149 return new WP_Error(
150 'defender_unable_to_apply_rules',
151 esc_html__(
152 'The rules can\'t be applied. This can be either because your host doesn\'t allow editing the file, or you\'ve selected the wrong server type.',
153 'defender-security'
154 )
155 );
156 }
157
158 $response = $this->protect_content_directory();
159
160 if ( is_wp_error( $response ) ) {
161 return $response;
162 }
163
164 $response = $this->protect_includes_directory();
165
166 if ( is_wp_error( $response ) ) {
167 return $response;
168 }
169
170 $response = $this->protect_uploads_directory();
171
172 if ( is_wp_error( $response ) ) {
173 return $response;
174 }
175
176 $settings = array(
177 'new_htaccess_config' => $this->get_new_htaccess_config(),
178 );
179
180 update_site_option( "defender_security_tweeks_{$this->type}", $settings );
181
182 return true;
183 }
184 }
185
186 /**
187 * Revert the rules.
188 *
189 * @return bool|WP_Error
190 */
191 public function revert() {
192 global $wp_filesystem;
193 // Initialize the WP filesystem, no more using 'file-put-contents' function.
194 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
195 require_once ABSPATH . '/wp-admin/includes/file.php';
196 WP_Filesystem();
197 }
198 if ( 'protect-information' === $this->type ) {
199 $ht_access_path = ABSPATH . '.htaccess';
200 // Quick exit if the file does not exist.
201 if ( ! is_file( $ht_access_path ) ) {
202 return true;
203 }
204
205 if ( ! $wp_filesystem->is_writable( $ht_access_path ) ) {
206 return $this->get_error_by_file_path( $ht_access_path );
207 }
208
209 $ht_access_config = $wp_filesystem->get_contents( $ht_access_path );
210 $rules = $this->get_rules();
211
212 preg_match_all( '/## WP Defender(.*?)## WP Defender - End ##/s', $ht_access_config, $matches );
213
214 if ( is_array( $matches ) && count( $matches ) > 0 ) {
215 $ht_access_config = str_replace( implode( '', $matches[0] ), '', $ht_access_config );
216 } else {
217 $ht_access_config = str_replace( implode( '', $rules ), '', $ht_access_config );
218 }
219
220 $ht_access_config = trim( $ht_access_config );
221
222 return (bool) file_put_contents( $ht_access_path, $ht_access_config, LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
223 }
224
225 if ( 'prevent-php-executed' === $this->type ) {
226 // Content-folder.
227 $response = $this->unprotect_content_directory();
228 if ( is_wp_error( $response ) ) {
229 return wp_send_json_error(
230 array( 'message' => $response->get_error_message() )
231 );
232 }
233 // Includes-folder.
234 $response = $this->unprotect_includes_directory();
235 if ( is_wp_error( $response ) ) {
236 return wp_send_json_error(
237 array( 'message' => $response->get_error_message() )
238 );
239 }
240 // Uploads-folder.
241 $response = $this->unprotect_upload_directory();
242 if ( is_wp_error( $response ) ) {
243 return wp_send_json_error(
244 array( 'message' => $response->get_error_message() )
245 );
246 }
247
248 return delete_site_option( "defender_security_tweeks_{$this->type}" );
249 }
250 }
251
252 /**
253 * Get Apache rule depending on the version.
254 *
255 * @return array
256 */
257 public function get_rules() {
258 $rules = array(
259 PHP_EOL . '## WP Defender - Prevent information disclosure ##' . PHP_EOL,
260 '<FilesMatch "\.(md|exe|sh|bak|inc|pot|po|mo|log|sql)$">' . PHP_EOL .
261 'Require all denied' . PHP_EOL .
262 '</FilesMatch>' . PHP_EOL,
263 '<Files robots.txt>' . PHP_EOL .
264 'Require all granted' . PHP_EOL .
265 '</Files>' . PHP_EOL,
266 '<Files ads.txt>' . PHP_EOL .
267 'Require all granted' . PHP_EOL .
268 '</Files>' . PHP_EOL,
269 '## WP Defender - End ##',
270 );
271
272 if ( version_compare( $this->get_version(), '2.4', '<' ) ) {
273 $rules = array(
274 PHP_EOL . '## WP Defender - Prevent information disclosure ##' . PHP_EOL,
275 '<FilesMatch "\.(md|exe|sh|bak|inc|pot|po|mo|log|sql)$">' . PHP_EOL .
276 'Order allow,deny' . PHP_EOL .
277 'Deny from all' . PHP_EOL .
278 '</FilesMatch>' . PHP_EOL,
279 '<Files robots.txt>' . PHP_EOL .
280 'Allow from all' . PHP_EOL .
281 '</Files>' . PHP_EOL,
282 '<Files ads.txt>' . PHP_EOL .
283 'Allow from all' . PHP_EOL .
284 '</Files>' . PHP_EOL,
285 '## WP Defender - End ##',
286 );
287 }
288
289 return $rules;
290 }
291
292 /**
293 * Get Apache rule depending on the version for instruction on browser.
294 *
295 * @return string
296 */
297 public function get_rules_for_instruction() {
298 $rules = '';
299
300 if ( 'prevent-php-executed' === $this->type ) {
301 $rules = '## WP Defender - Protect PHP Executed ##' . PHP_EOL;
302 $rules .= PHP_EOL;
303 $rules .= '<Files *.php>' . PHP_EOL;
304 $rules .= 'Require all denied' . PHP_EOL;
305 $rules .= '</Files>' . PHP_EOL;
306 $rules .= PHP_EOL;
307 $rules .= '## WP Defender - End ##' . PHP_EOL;
308
309 if ( 'apache' === Server::get_current_server() && version_compare( $this->get_version(), '2.4', '<' ) ) {
310 $rules = '## WP Defender - Protect PHP Executed ##' . PHP_EOL;
311 $rules .= PHP_EOL;
312 $rules .= '<Files *.php>' . PHP_EOL;
313 $rules .= 'Order allow,deny' . PHP_EOL;
314 $rules .= 'Deny from all' . PHP_EOL;
315 $rules .= '</Files>' . PHP_EOL;
316 $rules .= PHP_EOL;
317 $rules .= '## WP Defender - End ##' . PHP_EOL;
318 } elseif ( 'litespeed' === Server::get_current_server() ) {
319 $rules = '## WP Defender - Protect PHP Executed ##' . PHP_EOL;
320 $rules .= 'RewriteEngine On' . PHP_EOL;
321 $rules .= 'RewriteRule ^.*\.php$ - [F,L,NC]' . PHP_EOL;
322 $rules .= '## WP Defender - End ##' . PHP_EOL;
323 }
324 }
325
326 if ( 'protect-information' === $this->type ) {
327 $rules = '## WP Defender - Prevent information disclosure ##' . PHP_EOL;
328 $rules .= PHP_EOL;
329 $rules .= '<FilesMatch "\.(md|exe|sh|bak|inc|pot|po|mo|log|sql)$">' . PHP_EOL;
330 $rules .= 'Require all denied' . PHP_EOL;
331 $rules .= '</FilesMatch>' . PHP_EOL;
332 $rules .= PHP_EOL;
333 $rules .= '<Files robots.txt>' . PHP_EOL;
334 $rules .= 'Require all granted' . PHP_EOL;
335 $rules .= '</Files>' . PHP_EOL;
336 $rules .= PHP_EOL;
337 $rules .= '<Files ads.txt>' . PHP_EOL;
338 $rules .= 'Require all granted' . PHP_EOL;
339 $rules .= '</Files>' . PHP_EOL;
340 $rules .= PHP_EOL;
341 $rules .= '## WP Defender - End ##';
342
343 if ( version_compare( $this->get_version(), '2.4', '<' ) ) {
344 $rules = '## WP Defender - Prevent information disclosure ##' . PHP_EOL;
345 $rules .= PHP_EOL;
346 $rules .= '<FilesMatch "\.(md|exe|sh|bak|inc|pot|po|mo|log|sql)$">' . PHP_EOL;
347 $rules .= 'Order allow,deny' . PHP_EOL;
348 $rules .= 'Deny from all' . PHP_EOL;
349 $rules .= '</FilesMatch>' . PHP_EOL;
350 $rules .= PHP_EOL;
351 $rules .= '<Files robots.txt>' . PHP_EOL;
352 $rules .= 'Allow from all' . PHP_EOL;
353 $rules .= '</Files>' . PHP_EOL;
354 $rules .= PHP_EOL;
355 $rules .= '<Files ads.txt>' . PHP_EOL;
356 $rules .= 'Allow from all' . PHP_EOL;
357 $rules .= '</Files>' . PHP_EOL;
358 $rules .= PHP_EOL;
359 $rules .= '## WP Defender - End ##';
360 }
361 }
362
363 return $rules;
364 }
365
366 /**
367 * Determine the Apache version.
368 * Most web servers have apache_get_version disabled, so we just get a simple curl of the headers.
369 *
370 * @return string
371 */
372 public function get_version() {
373 if ( ! function_exists( 'apache_get_version' ) ) {
374 // The default support is 2.2.
375 $version = '2.2';
376 $url = home_url();
377 $apache_version = get_site_transient( self::CACHE_APACHE_VERSION );
378
379 if ( ! is_array( $apache_version ) ) {
380 $apache_version = array();
381 }
382
383 if ( isset( $apache_version[ $url ] ) && is_string( $apache_version[ $url ] ) && '' !== $apache_version[ $url ] ) {
384 return strtolower( $apache_version[ $url ] );
385 }
386 // The default support is 2.2.
387 $apache_version[ $url ] = $version;
388
389 $server = defender_get_data_from_request( 'SERVER_SOFTWARE', 's' );
390 if ( isset( $server ) ) {
391 $server = explode( ' ', $server );
392 if ( is_array( $server ) && count( $server ) > 1 ) {
393 $server = $server[0];
394 $server = explode( '/', $server );
395 if ( is_array( $server ) && count( $server ) > 1 ) {
396 $version = $server[1];
397 $apache_version[ $url ] = $version;
398 }
399 }
400 }
401
402 set_site_transient( self::CACHE_APACHE_VERSION, $apache_version, 3600 );
403 } else {
404 $version = apache_get_version();
405 $version = explode( '/', $version );
406 $version = isset( $version[1] ) && is_string( $version[1] ) && '' !== $version[1] ? $version[1] : $version[0];
407 }
408
409 return $version;
410 }
411
412 /**
413 * Protect content directory.
414 *
415 * @return void|WP_Error
416 */
417 public function protect_content_directory() {
418 global $wp_filesystem;
419 // Initialize the WP filesystem, no more using 'file-put-contents' function.
420 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
421 require_once ABSPATH . '/wp-admin/includes/file.php';
422 WP_Filesystem();
423 }
424 $ht_access_path = $this->contentdir_path;
425
426 if ( ! is_string( $ht_access_path ) || '' === $ht_access_path ) {
427 $ht_access_path = WP_CONTENT_DIR . '/.htaccess';
428 }
429 // Create a htaccess-file if it doesn't exist.
430 if ( ! is_file( $ht_access_path )
431 && false === (bool) file_put_contents( $ht_access_path, '', LOCK_EX ) // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
432 ) {
433 return $this->get_error_by_file_path( $ht_access_path );
434 }
435 // If the htaccess-file is existed.
436 if ( ! $wp_filesystem->is_writable( $ht_access_path ) ) {
437 return $this->get_error_by_file_path( $ht_access_path );
438 }
439
440 $exists_rules = $this->cleanup_old_rules( $wp_filesystem->get_contents( $ht_access_path ) );
441 $rule = array(
442 '## WP Defender - Protect PHP Executed ##',
443 '<Files *.php>',
444 $this->generate_htaccess_rule( false ),
445 '</Files>',
446 );
447
448 $rule[] = '## WP Defender - End ##';
449 file_put_contents( $ht_access_path, $exists_rules . implode( PHP_EOL, $rule ), LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
450 }
451
452 /**
453 * Protect includes directory.
454 *
455 * @return void|WP_Error
456 */
457 public function protect_includes_directory() {
458 global $wp_filesystem;
459 // Initialize the WP filesystem, no more using 'file-put-contents' function.
460 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
461 require_once ABSPATH . '/wp-admin/includes/file.php';
462 WP_Filesystem();
463 }
464 $ht_access_path = $this->includedir_path;
465
466 if ( ! is_string( $ht_access_path ) || '' === $ht_access_path ) {
467 $ht_access_path = ABSPATH . WPINC . '/.htaccess';
468 }
469 // Create a htaccess-file if it doesn't exist.
470 if ( ! is_file( $ht_access_path )
471 && false === (bool) file_put_contents( $ht_access_path, '', LOCK_EX ) // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
472 ) {
473 return $this->get_error_by_file_path( $ht_access_path );
474 }
475 // If the htaccess-file is existed.
476 if ( ! $wp_filesystem->is_writable( $ht_access_path ) ) {
477 return $this->get_error_by_file_path( $ht_access_path );
478 }
479
480 $exists_rules = $this->cleanup_old_rules( $wp_filesystem->get_contents( $ht_access_path ) );
481
482 $rule = array(
483 '## WP Defender - Protect PHP Executed ##',
484 '<Files *.php>',
485 $this->generate_htaccess_rule( false ),
486 '</Files>',
487 '<Files wp-tinymce.php>',
488 $this->generate_htaccess_rule( true ),
489 '</Files>',
490 '<Files ms-files.php>',
491 $this->generate_htaccess_rule( true ),
492 '</Files>',
493 '## WP Defender - End ##',
494 );
495
496 file_put_contents( $ht_access_path, $exists_rules . implode( PHP_EOL, $rule ), LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
497 }
498
499 /**
500 * Generates the correct Apache rules for allow/deny based on the server version.
501 *
502 * @param bool $allow Specifies whether to generate allow or deny rules.
503 *
504 * @return string Returns the generated Apache rules as a string.
505 */
506 protected function generate_htaccess_rule( $allow = true ) {
507 if ( version_compare( $this->get_version(), 2.4, '>=' ) ) {
508 if ( $allow ) {
509 return 'Require all granted';
510 } else {
511 return 'Require all denied';
512 }
513 } elseif ( $allow ) {
514 return 'Allow from all';
515 } else {
516 return 'Order allow,deny' . PHP_EOL .
517 'Deny from all';
518 }
519 }
520
521 /**
522 * Protect uploads directory.
523 * This only when user provide a custom uploads.
524 *
525 * @return void|WP_Error
526 */
527 public function protect_uploads_directory() {
528 if ( defined( 'UPLOADS' ) ) {
529 $this->contentdir_path = ABSPATH . UPLOADS . '/.htaccess';
530
531 return $this->protect_content_directory();
532 }
533 }
534
535 /**
536 * Unprotect content directory.
537 *
538 * @return void|WP_Error
539 */
540 public function unprotect_content_directory() {
541 global $wp_filesystem;
542 // Initialize the WP filesystem, no more using 'file-put-contents' function.
543 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
544 require_once ABSPATH . '/wp-admin/includes/file.php';
545 WP_Filesystem();
546 }
547 $ht_access_path = $this->contentdir_path;
548
549 if ( ! is_string( $ht_access_path ) || '' === $ht_access_path ) {
550 $ht_access_path = WP_CONTENT_DIR . '/.htaccess';
551 }
552 // Quick exit if the file does not exist.
553 if ( ! is_file( $ht_access_path ) ) {
554 return;
555 }
556 // If the htaccess-file is existed.
557 if ( ! $wp_filesystem->is_writable( $ht_access_path ) ) {
558 return $this->get_error_by_file_path( $ht_access_path );
559 }
560
561 $ht_config = $this->cleanup_old_rules( $wp_filesystem->get_contents( $ht_access_path ) );
562 $ht_config = trim( $ht_config );
563
564 file_put_contents( $ht_access_path, trim( $ht_config ), LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
565 }
566
567 /**
568 * Unprotect includes-directory.
569 * Todo: can be combined with unprotect_content_directory()?
570 *
571 * @return void|WP_Error
572 */
573 public function unprotect_includes_directory() {
574 global $wp_filesystem;
575 // Initialize the WP filesystem, no more using 'file-put-contents' function.
576 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
577 require_once ABSPATH . '/wp-admin/includes/file.php';
578 WP_Filesystem();
579 }
580 $ht_access_path = $this->includedir_path;
581
582 if ( ! is_string( $ht_access_path ) || '' === $ht_access_path ) {
583 $ht_access_path = ABSPATH . WPINC . '/.htaccess';
584 }
585 // Quick exit if the file does not exist.
586 if ( ! is_file( $ht_access_path ) ) {
587 return;
588 }
589 // If the htaccess-file is existed.
590 if ( ! $wp_filesystem->is_writable( $ht_access_path ) ) {
591 return $this->get_error_by_file_path( $ht_access_path );
592 }
593
594 $ht_config = $this->cleanup_old_rules( $wp_filesystem->get_contents( $ht_access_path ) );
595 file_put_contents( $ht_access_path, trim( $ht_config ), LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
596 }
597
598 /**
599 * Unprotect upload directory.
600 *
601 * @return void
602 */
603 public function unprotect_upload_directory() {
604 if ( defined( 'UPLOADS' ) ) {
605 $this->contentdir_path = ABSPATH . UPLOADS . '/.htaccess';
606 $this->unprotect_content_directory();
607 }
608 }
609
610 /**
611 * Set the exclude file paths.
612 *
613 * @param array $config to set.
614 */
615 public function set_new_htaccess_config( $config = array() ) {
616 if ( is_array( $config ) && array() !== $config ) {
617 $this->new_htaccess_config = $config;
618 }
619 }
620
621 /**
622 * Get the new HT config.
623 *
624 * @return array - $new_htaccess_config
625 */
626 public function get_new_htaccess_config() {
627 return $this->new_htaccess_config;
628 }
629
630 /**
631 * Cleans up old rules from the existing .htaccess configuration.
632 *
633 * @param string $exists_rules The existing .htaccess rules.
634 *
635 * @return string Returns the cleaned-up .htaccess rules.
636 */
637 public function cleanup_old_rules( $exists_rules ) {
638 $pattern = '/(## WP Defender - Protect PHP Executed ##((.|\n)*)## WP Defender - End ##)/';
639
640 if ( preg_match( $pattern, $exists_rules ) ) {
641 $exists_rules = preg_replace( $pattern, '', $exists_rules );
642 }
643
644 $exists_rules = trim( $exists_rules );
645
646 if ( strlen( $exists_rules ) ) {
647 $exists_rules .= PHP_EOL;
648 }
649
650 return $exists_rules;
651 }
652 }
653