` in the root .htaccess applies to every
* directory below it, so the log rule covers wp-content/debug.log too.
*/
class CoreFileRules {
const MARKER = 'GemSecurity Core Files';
/** @return string[] */
public static function rules() {
$deny = [
' ',
' Require all denied',
' ',
' ',
' Order allow,deny',
' Deny from all',
' ',
];
return array_merge(
[ '' ], $deny, [ '' ],
[ '' ], $deny, [ '' ],
[
'',
' RewriteEngine On',
' RewriteRule ^wp-admin/includes/ - [F,L]',
'',
]
);
}
/**
* Write or remove the block.
*
* @param bool $enabled
* @param string|null $file Defaults to the root .htaccess; overridable for tests.
* @return bool Whether the file now matches $enabled.
*/
public static function sync( $enabled, $file = null ) {
$file = $file ?: \GemSecurity\Paths::root() . '.htaccess';
if ( ! $enabled && ! file_exists( $file ) ) {
return true;
}
$writable = file_exists( $file ) ? wp_is_writable( $file ) : wp_is_writable( dirname( $file ) );
if ( ! $writable ) {
return false;
}
if ( ! $enabled ) {
return self::remove_block( $file, self::MARKER );
}
if ( ! function_exists( 'insert_with_markers' ) ) {
require_once ABSPATH . 'wp-admin/includes/misc.php';
}
return (bool) insert_with_markers( $file, self::MARKER, self::rules() );
}
/**
* Remove a marked block entirely. insert_with_markers() with no lines
* leaves the BEGIN/END comments behind.
*
* @param string $file
* @param string $marker
* @return bool
*/
public static function remove_block( $file, $marker ) {
$body = (string) file_get_contents( $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- local file.
$rest = preg_replace( '/\n?# BEGIN ' . preg_quote( $marker, '/' ) . '\n.*?# END ' . preg_quote( $marker, '/' ) . '\n?/s', "\n", $body );
if ( null === $rest || $rest === $body ) {
return true; // Nothing of ours in there.
}
return false !== file_put_contents( $file, ltrim( $rest, "\n" ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- .htaccess edit, same as insert_with_markers().
}
/**
* Whether the block is in place.
*
* @param string|null $file
* @return bool
*/
public static function installed( $file = null ) {
$file = $file ?: \GemSecurity\Paths::root() . '.htaccess';
return is_readable( $file ) && false !== strpos( (string) file_get_contents( $file ), '# BEGIN ' . self::MARKER ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- local file.
}
}