PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.2.2
Defender Security – Malware Scanner, Login Security & Firewall v6.2.2
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 6.2.2, at src/component/security-tweaks/servers/class-apache.php

693 lines 20.4 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 === 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 return true;
186 }
187
188 /**
189 * Revert the rules.
190 *
191 * @return bool|WP_Error
192 */
193 public function revert() {
194 global $wp_filesystem;
195 // Initialize the WP filesystem, no more using 'file-put-contents' function.
196 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
197 require_once ABSPATH . '/wp-admin/includes/file.php';
198 WP_Filesystem();
199 }
200 if ( 'protect-information' === $this->type ) {
201 $ht_access_path = ABSPATH . '.htaccess';
202 // Quick exit if the file does not exist.
203 if ( ! is_file( $ht_access_path ) ) {
204 return true;
205 }
206
207 if ( ! $wp_filesystem->is_writable( $ht_access_path ) ) {
208 return $this->get_error_by_file_path( $ht_access_path );
209 }
210
211 $ht_access_config = $wp_filesystem->get_contents( $ht_access_path );
212 $rules = $this->get_rules();
213
214 preg_match_all( '/## WP Defender(.*?)## WP Defender - End ##/s', $ht_access_config, $matches );
215
216 if ( is_array( $matches ) && count( $matches ) > 0 ) {
217 $ht_access_config = str_replace( implode( '', $matches[0] ), '', $ht_access_config );
218 } else {
219 $ht_access_config = str_replace( implode( '', $rules ), '', $ht_access_config );
220 }
221
222 $ht_access_config = trim( $ht_access_config );
223
224 return (bool) file_put_contents( $ht_access_path, $ht_access_config, LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
225 }
226
227 if ( 'prevent-php-executed' === $this->type ) {
228 // Content-folder.
229 $response = $this->unprotect_content_directory();
230 if ( is_wp_error( $response ) ) {
231 return wp_send_json_error(
232 array( 'message' => $response->get_error_message() )
233 );
234 }
235 // Includes-folder.
236 $response = $this->unprotect_includes_directory();
237 if ( is_wp_error( $response ) ) {
238 return wp_send_json_error(
239 array( 'message' => $response->get_error_message() )
240 );
241 }
242 // Uploads-folder.
243 $response = $this->unprotect_upload_directory();
244 if ( is_wp_error( $response ) ) {
245 return wp_send_json_error(
246 array( 'message' => $response->get_error_message() )
247 );
248 }
249
250 return delete_site_option( "defender_security_tweeks_{$this->type}" );
251 }
252
253 return true;
254 }
255
256 /**
257 * Get Apache rule depending on the version.
258 *
259 * @return array
260 */
261 public function get_rules() {
262 $rules = array(
263 PHP_EOL . '## WP Defender - Prevent information disclosure ##' . PHP_EOL,
264 '<FilesMatch "\.(md|exe|sh|bak|inc|pot|po|mo|log|sql)$">' . PHP_EOL .
265 'Require all denied' . PHP_EOL .
266 '</FilesMatch>' . PHP_EOL,
267 '<Files robots.txt>' . PHP_EOL .
268 'Require all granted' . PHP_EOL .
269 '</Files>' . PHP_EOL,
270 '<Files ads.txt>' . PHP_EOL .
271 'Require all granted' . PHP_EOL .
272 '</Files>' . PHP_EOL,
273 '## WP Defender - End ##',
274 );
275
276 if ( version_compare( $this->get_version(), '2.4', '<' ) ) {
277 $rules = array(
278 PHP_EOL . '## WP Defender - Prevent information disclosure ##' . PHP_EOL,
279 '<FilesMatch "\.(md|exe|sh|bak|inc|pot|po|mo|log|sql)$">' . PHP_EOL .
280 'Order allow,deny' . PHP_EOL .
281 'Deny from all' . PHP_EOL .
282 '</FilesMatch>' . PHP_EOL,
283 '<Files robots.txt>' . PHP_EOL .
284 'Allow from all' . PHP_EOL .
285 '</Files>' . PHP_EOL,
286 '<Files ads.txt>' . PHP_EOL .
287 'Allow from all' . PHP_EOL .
288 '</Files>' . PHP_EOL,
289 '## WP Defender - End ##',
290 );
291 }
292
293 return $rules;
294 }
295
296 /**
297 * Get LiteSpeed .htaccess rules for display in the browser, independent of the current server detection.
298 *
299 * @return string
300 */
301 public function get_litespeed_rules_for_instruction(): string {
302 if ( 'prevent-php-executed' !== $this->type ) {
303 return '';
304 }
305
306 $rules = '## WP Defender - Protect PHP Executed ##' . PHP_EOL;
307 $rules .= '<Files *.php>' . PHP_EOL;
308 $rules .= 'Require all denied' . PHP_EOL;
309 $rules .= '</Files>' . PHP_EOL;
310 $rules .= '## WP Defender - End ##' . PHP_EOL;
311
312 return $rules;
313 }
314
315 /**
316 * Get Apache rule depending on the version for instruction on browser.
317 *
318 * @return string
319 */
320 public function get_rules_for_instruction() {
321 $rules = '';
322
323 if ( 'prevent-php-executed' === $this->type ) {
324 $rules = '## WP Defender - Protect PHP Executed ##' . PHP_EOL;
325 $rules .= PHP_EOL;
326 $rules .= '<Files *.php>' . PHP_EOL;
327 $rules .= 'Require all denied' . PHP_EOL;
328 $rules .= '</Files>' . PHP_EOL;
329 $rules .= PHP_EOL;
330 $rules .= '## WP Defender - End ##' . PHP_EOL;
331
332 if ( 'apache' === Server::get_current_server() && version_compare( $this->get_version(), '2.4', '<' ) ) {
333 $rules = '## WP Defender - Protect PHP Executed ##' . PHP_EOL;
334 $rules .= PHP_EOL;
335 $rules .= '<Files *.php>' . PHP_EOL;
336 $rules .= 'Order allow,deny' . PHP_EOL;
337 $rules .= 'Deny from all' . PHP_EOL;
338 $rules .= '</Files>' . PHP_EOL;
339 $rules .= PHP_EOL;
340 $rules .= '## WP Defender - End ##' . PHP_EOL;
341 } elseif ( 'litespeed' === Server::get_current_server() ) {
342 $rules = '## WP Defender - Protect PHP Executed ##' . PHP_EOL;
343 $rules .= 'RewriteEngine On' . PHP_EOL;
344 $rules .= 'RewriteRule ^.*\.php$ - [F,L,NC]' . PHP_EOL;
345 $rules .= '## WP Defender - End ##' . PHP_EOL;
346 }
347 }
348
349 if ( 'protect-information' === $this->type ) {
350 $rules = '## WP Defender - Prevent information disclosure ##' . PHP_EOL;
351 $rules .= PHP_EOL;
352 $rules .= '<FilesMatch "\.(md|exe|sh|bak|inc|pot|po|mo|log|sql)$">' . PHP_EOL;
353 $rules .= 'Require all denied' . PHP_EOL;
354 $rules .= '</FilesMatch>' . PHP_EOL;
355 $rules .= PHP_EOL;
356 $rules .= '<Files robots.txt>' . PHP_EOL;
357 $rules .= 'Require all granted' . PHP_EOL;
358 $rules .= '</Files>' . PHP_EOL;
359 $rules .= PHP_EOL;
360 $rules .= '<Files ads.txt>' . PHP_EOL;
361 $rules .= 'Require all granted' . PHP_EOL;
362 $rules .= '</Files>' . PHP_EOL;
363 $rules .= PHP_EOL;
364 $rules .= '## WP Defender - End ##';
365
366 if ( version_compare( $this->get_version(), '2.4', '<' ) ) {
367 $rules = '## WP Defender - Prevent information disclosure ##' . PHP_EOL;
368 $rules .= PHP_EOL;
369 $rules .= '<FilesMatch "\.(md|exe|sh|bak|inc|pot|po|mo|log|sql)$">' . PHP_EOL;
370 $rules .= 'Order allow,deny' . PHP_EOL;
371 $rules .= 'Deny from all' . PHP_EOL;
372 $rules .= '</FilesMatch>' . PHP_EOL;
373 $rules .= PHP_EOL;
374 $rules .= '<Files robots.txt>' . PHP_EOL;
375 $rules .= 'Allow from all' . PHP_EOL;
376 $rules .= '</Files>' . PHP_EOL;
377 $rules .= PHP_EOL;
378 $rules .= '<Files ads.txt>' . PHP_EOL;
379 $rules .= 'Allow from all' . PHP_EOL;
380 $rules .= '</Files>' . PHP_EOL;
381 $rules .= PHP_EOL;
382 $rules .= '## WP Defender - End ##';
383 }
384 }
385
386 return $rules;
387 }
388
389 /**
390 * Determine the Apache version.
391 * Most web servers have apache_get_version disabled, so we just get a simple curl of the headers.
392 *
393 * @return string
394 */
395 public function get_version() {
396 if ( ! function_exists( 'apache_get_version' ) ) {
397 // The default support is 2.2.
398 $version = '2.2';
399 $url = home_url();
400 $apache_version = get_site_transient( self::CACHE_APACHE_VERSION );
401
402 if ( ! is_array( $apache_version ) ) {
403 $apache_version = array();
404 }
405
406 if ( isset( $apache_version[ $url ] ) && is_string( $apache_version[ $url ] ) && '' !== $apache_version[ $url ] ) {
407 return strtolower( $apache_version[ $url ] );
408 }
409 // The default support is 2.2.
410 $apache_version[ $url ] = $version;
411
412 $server = defender_get_data_from_request( 'SERVER_SOFTWARE', 's' );
413 if ( isset( $server ) ) {
414 $server = explode( ' ', $server );
415 if ( is_array( $server ) && count( $server ) > 1 ) {
416 $server = $server[0];
417 $server = explode( '/', $server );
418 if ( is_array( $server ) && count( $server ) > 1 ) {
419 $version = $server[1];
420 $apache_version[ $url ] = $version;
421 }
422 }
423 }
424
425 set_site_transient( self::CACHE_APACHE_VERSION, $apache_version, 3600 );
426 } else {
427 $version = apache_get_version();
428 $version = explode( '/', $version );
429 $version = isset( $version[1] ) && is_string( $version[1] ) && '' !== $version[1] ? $version[1] : $version[0];
430 }
431
432 return $version;
433 }
434
435 /**
436 * Protect content directory.
437 *
438 * @return void|WP_Error
439 */
440 public function protect_content_directory() {
441 global $wp_filesystem;
442 // Initialize the WP filesystem, no more using 'file-put-contents' function.
443 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
444 require_once ABSPATH . '/wp-admin/includes/file.php';
445 WP_Filesystem();
446 }
447 $ht_access_path = $this->contentdir_path;
448
449 if ( ! is_string( $ht_access_path ) || '' === $ht_access_path ) {
450 $ht_access_path = WP_CONTENT_DIR . '/.htaccess';
451 }
452 // Create a htaccess-file if it doesn't exist.
453 if ( ! is_file( $ht_access_path )
454 && false === file_put_contents( $ht_access_path, '', LOCK_EX ) // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
455 ) {
456 return $this->get_error_by_file_path( $ht_access_path );
457 }
458 // If the htaccess-file is existed.
459 if ( ! $wp_filesystem->is_writable( $ht_access_path ) ) {
460 return $this->get_error_by_file_path( $ht_access_path );
461 }
462
463 $exists_rules = $this->cleanup_old_rules( $wp_filesystem->get_contents( $ht_access_path ) );
464 $rule = array(
465 '## WP Defender - Protect PHP Executed ##',
466 '<Files *.php>',
467 $this->generate_htaccess_rule( false ),
468 '</Files>',
469 );
470
471 $rule[] = '## WP Defender - End ##';
472 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
473 }
474
475 /**
476 * Protect includes directory.
477 *
478 * @return void|WP_Error
479 */
480 public function protect_includes_directory() {
481 global $wp_filesystem;
482 // Initialize the WP filesystem, no more using 'file-put-contents' function.
483 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
484 require_once ABSPATH . '/wp-admin/includes/file.php';
485 WP_Filesystem();
486 }
487 $ht_access_path = $this->includedir_path;
488
489 if ( ! is_string( $ht_access_path ) || '' === $ht_access_path ) {
490 $ht_access_path = ABSPATH . WPINC . '/.htaccess';
491 }
492 // Create a htaccess-file if it doesn't exist.
493 if ( ! is_file( $ht_access_path )
494 && false === file_put_contents( $ht_access_path, '', LOCK_EX ) // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
495 ) {
496 return $this->get_error_by_file_path( $ht_access_path );
497 }
498 // If the htaccess-file is existed.
499 if ( ! $wp_filesystem->is_writable( $ht_access_path ) ) {
500 return $this->get_error_by_file_path( $ht_access_path );
501 }
502
503 $exists_rules = $this->cleanup_old_rules( $wp_filesystem->get_contents( $ht_access_path ) );
504
505 $rule = array(
506 '## WP Defender - Protect PHP Executed ##',
507 '<Files *.php>',
508 $this->generate_htaccess_rule( false ),
509 '</Files>',
510 '<Files wp-tinymce.php>',
511 $this->generate_htaccess_rule( true ),
512 '</Files>',
513 '<Files ms-files.php>',
514 $this->generate_htaccess_rule( true ),
515 '</Files>',
516 '## WP Defender - End ##',
517 );
518
519 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
520 }
521
522 /**
523 * Generates the correct Apache rules for allow/deny based on the server version.
524 *
525 * @param bool $allow Specifies whether to generate allow or deny rules.
526 *
527 * @return string Returns the generated Apache rules as a string.
528 */
529 protected function generate_htaccess_rule( $allow = true ) {
530 if ( version_compare( $this->get_version(), 2.4, '>=' ) ) {
531 if ( $allow ) {
532 return 'Require all granted';
533 } else {
534 return 'Require all denied';
535 }
536 } elseif ( $allow ) {
537 return 'Allow from all';
538 } else {
539 return 'Order allow,deny' . PHP_EOL .
540 'Deny from all';
541 }
542 }
543
544 /**
545 * Protect uploads directory.
546 * This only when user provide a custom uploads.
547 *
548 * @return void|WP_Error
549 */
550 public function protect_uploads_directory() {
551 $path = $this->get_custom_upload_dir();
552 if ( '' !== $path ) {
553 $this->contentdir_path = $path;
554
555 return $this->protect_content_directory();
556 }
557 }
558
559 /**
560 * Unprotect content directory.
561 *
562 * @return void|WP_Error
563 */
564 public function unprotect_content_directory() {
565 global $wp_filesystem;
566 // Initialize the WP filesystem, no more using 'file-put-contents' function.
567 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
568 require_once ABSPATH . '/wp-admin/includes/file.php';
569 WP_Filesystem();
570 }
571 $ht_access_path = $this->contentdir_path;
572
573 if ( ! is_string( $ht_access_path ) || '' === $ht_access_path ) {
574 $ht_access_path = WP_CONTENT_DIR . '/.htaccess';
575 }
576 // Quick exit if the file does not exist.
577 if ( ! is_file( $ht_access_path ) ) {
578 return;
579 }
580 // If the htaccess-file is existed.
581 if ( ! $wp_filesystem->is_writable( $ht_access_path ) ) {
582 return $this->get_error_by_file_path( $ht_access_path );
583 }
584
585 $ht_config = $this->cleanup_old_rules( $wp_filesystem->get_contents( $ht_access_path ) );
586 $ht_config = trim( $ht_config );
587
588 file_put_contents( $ht_access_path, trim( $ht_config ), LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
589 }
590
591 /**
592 * Unprotect includes-directory.
593 * Todo: can be combined with unprotect_content_directory()?
594 *
595 * @return void|WP_Error
596 */
597 public function unprotect_includes_directory() {
598 global $wp_filesystem;
599 // Initialize the WP filesystem, no more using 'file-put-contents' function.
600 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
601 require_once ABSPATH . '/wp-admin/includes/file.php';
602 WP_Filesystem();
603 }
604 $ht_access_path = $this->includedir_path;
605
606 if ( ! is_string( $ht_access_path ) || '' === $ht_access_path ) {
607 $ht_access_path = ABSPATH . WPINC . '/.htaccess';
608 }
609 // Quick exit if the file does not exist.
610 if ( ! is_file( $ht_access_path ) ) {
611 return;
612 }
613 // If the htaccess-file is existed.
614 if ( ! $wp_filesystem->is_writable( $ht_access_path ) ) {
615 return $this->get_error_by_file_path( $ht_access_path );
616 }
617
618 $ht_config = $this->cleanup_old_rules( $wp_filesystem->get_contents( $ht_access_path ) );
619 file_put_contents( $ht_access_path, trim( $ht_config ), LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
620 }
621
622 /**
623 * Unprotect upload directory.
624 *
625 * @return void|WP_Error
626 */
627 public function unprotect_upload_directory() {
628 $path = $this->get_custom_upload_dir();
629 if ( '' !== $path ) {
630 $this->contentdir_path = $path;
631
632 return $this->unprotect_content_directory();
633 }
634 }
635
636 /**
637 * Set the exclude file paths.
638 *
639 * @param array $config to set.
640 */
641 public function set_new_htaccess_config( $config = array() ) {
642 if ( is_array( $config ) && array() !== $config ) {
643 $this->new_htaccess_config = $config;
644 }
645 }
646
647 /**
648 * Get the new HT config.
649 *
650 * @return array - $new_htaccess_config
651 */
652 public function get_new_htaccess_config() {
653 return $this->new_htaccess_config;
654 }
655
656 /**
657 * Cleans up old rules from the existing .htaccess configuration.
658 *
659 * @param string $exists_rules The existing .htaccess rules.
660 *
661 * @return string Returns the cleaned-up .htaccess rules.
662 */
663 public function cleanup_old_rules( $exists_rules ) {
664 $pattern = '/(## WP Defender - Protect PHP Executed ##((.|\n)*)## WP Defender - End ##)/';
665
666 if ( preg_match( $pattern, $exists_rules ) ) {
667 $exists_rules = preg_replace( $pattern, '', $exists_rules );
668 }
669
670 $exists_rules = trim( $exists_rules );
671
672 if ( strlen( $exists_rules ) ) {
673 $exists_rules .= PHP_EOL;
674 }
675
676 return $exists_rules;
677 }
678
679 /**
680 * Get custom upload dir path.
681 *
682 * @return string
683 */
684 private function get_custom_upload_dir(): string {
685 if ( defined( 'UPLOADS' ) ) {
686 $upload_dir = wp_upload_dir( null, false );
687 return $upload_dir['basedir'] . '/.htaccess';
688 }
689
690 return '';
691 }
692 }
693