{
$output[] = $value;
} else {
$output[ $key ] = $value;
}
}
return $output;
}
/**
* Get's a value from an array or object.
*
* @param array|object $item The item to retrieve the value from.
* @param string $field The field or method name to use.
* @param null $default The default value to return if no value is found.
*
* @return mixed|null
*/
public static function get( $item, $field, $default = null ) {
if ( is_array( $item ) ) {
return isset( $item[ $field ] ) ? $item[ $field ] : $default;
}
if ( is_object( $item ) ) {
if ( is_callable( [ $item, $field ] ) ) {
return $item->{$field}();
}
return isset( $item->{$field} ) ? $item->{$field} : $default;
}
return $default;
}
/**
* Finds the first item in a list matching the given predicate.
*
* @param iterable $list
* @param callable $predicate
*
* @return mixed|null
*/
public static function find_where( iterable $list, callable $predicate ) {
foreach ( $list as $item ) {
if ( $predicate( $item ) ) {
return $item;
}
}
return null;
}
/**
* Array unique implementation that allows for non-scalar values.
*
* Will compare elements using `serialize()`.
*
* Keys are preserved. If a numeric array is given, the array will be re-indexed.
*
* @param array $array
* @param bool $stabilize If true, stabilizes the values first according to JSON semantics.
*
* @return array
*/
public static function non_scalar_array_unique( $array, $stabilize = false ) {
$is_numeric = wp_is_numeric_array( $array );
$hashes = array();
foreach ( $array as $key => $value ) {
if ( $stabilize ) {
$value = rest_stabilize_value( $value );
}
$hash = serialize( $value );
if ( isset( $hashes[ $hash ] ) ) {
unset( $array[ $key ] );
} else {
$hashes[ $hash ] = 1;
}
}
if ( $is_numeric ) {
return array_values( $array );
}
return $array;
}
/**
* Parse a complex header that has attributes like quality values.
*
* @param string $header
*
* @return array[]
* @example Parsing the Accept-Language header.
*
* "en-US,en;q=0.9,de;q=0.8" transforms to:
*
* [
* 'en-US' => [],
* 'en' => [ 'q' => 0.9 ],
* 'de' => [ 'q' => 0.8' ],
* ]
*
*/
public static function parse_header_with_attributes( $header ) {
$parsed = array();
$list = explode( ',', $header );
foreach ( $list as $value ) {
$attrs = array();
$parts = explode( ';', trim( $value ) );
$main = trim( $parts[0], ' <>' );
foreach ( $parts as $part ) {
if ( false === strpos( $part, '=' ) ) {
continue;
}
list( $key, $value ) = array_map( 'trim', explode( '=', $part, 2 ) );
$attrs[ $key ] = trim( $value, '" ' );
}
$parsed[ $main ] = $attrs;
}
return $parsed;
}
/**
* Is a particular function allowed to be called.
*
* Checks disabled functions and the function blacklist.
*
* @param string $func
*
* @return bool
*/
public static function is_func_allowed( $func ) {
static $cache = array();
static $disabled;
static $suhosin;
if ( isset( $cache[ $func ] ) ) {
return $cache[ $func ];
}
if ( $disabled === null ) {
$disabled = preg_split( '/\s*,\s*/', (string) ini_get( 'disable_functions' ) );
}
if ( $suhosin === null ) {
$suhosin = preg_split( '/\s*,\s*/', (string) ini_get( 'suhosin.executor.func.blacklist' ) );
}
if ( ! is_callable( $func ) ) {
return $cache[ $func ] = false;
}
if ( in_array( $func, $disabled, true ) ) {
return $cache[ $func ] = false;
}
if ( in_array( $func, $suhosin, true ) ) {
return $cache[ $func ] = false;
}
return $cache[ $func ] = true;
}
/**
* Get whatever backup plugin is being used on this site.
*
* @return string
*/
public static function get_backup_plugin() {
$possible = array(
'backupbuddy/backupbuddy.php',
'updraftplus/updraftplus.php',
'backwpup/backwpup.php',
'xcloner-backup-and-restore/xcloner.php',
'duplicator/duplicator.php',
'backup/backup.php',
'wp-db-backup/wp-db-backup.php',
'backupwordpress/backupwordpress.php',
'blogvault-real-time-backup/blogvault.php',
'wp-all-backup/wp-all-backup.php',
'vaultpress/vaultpress.php',
);
/**
* Filter the list of possible backup plugins.
*
* @param string[] List of Backup Plugin __FILE__.
*/
$possible = apply_filters( 'itsec_possible_backup_plugins', $possible );
if ( ! function_exists( 'is_plugin_active' ) ) {
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
}
if ( ! function_exists( 'is_plugin_active' ) ) {
return '';
}
foreach ( $possible as $file ) {
if ( is_plugin_active( $file ) ) {
return $file;
}
}
return '';
}
/**
* Generate a random token.
*
* @return string Hex token.
*/
public static function generate_token() {
$length = 64;
try {
$random = bin2hex( random_bytes( $length / 2 ) );
} catch ( Exception $e ) {
$unpacked = unpack( 'H*', wp_generate_password( $length / 2, true, true ) );
$random = reset( $unpacked );
}
return $random;
}
/**
* Generate a hash of the token for storage.
*
* @param string $token
*
* @return false|string
*/
public static function hash_token( $token ) {
return hash_hmac( self::get_hash_algo(), $token, wp_salt() );
}
/**
* Check if the provided token matches the stored hashed token.
*
* @param string $provided_token
* @param string $hashed_token
*
* @return bool
*/
public static function verify_token( $provided_token, $hashed_token ) {
if ( ! $hashed_token || ! $provided_token ) {
return false;
}
return hash_equals( $hashed_token, self::hash_token( $provided_token ) );
}
/**
* Get the hash algorithm to use.
*
* PHP can be compiled without the hash extension and the supported hash algos can be variable. WordPress shims
* support for md5 and sha1 hashes with hash_hmac.
*
* @return string
*/
public static function get_hash_algo() {
if ( ! function_exists( 'hash_algos' ) ) {
return 'sha1';
}
$algos = hash_algos();
if ( in_array( 'sha256', $algos, true ) ) {
return 'sha256';
}
return 'sha1';
}
public static function get_url_from_file( $file, $auto_ssl = true, $prevent_recursion = false ) {
$file = str_replace( '\\', '/', $file );
$url = '';
$upload_dir = ITSEC_Core::get_wp_upload_dir();
$upload_dir['basedir'] = str_replace( '\\', '/', $upload_dir['basedir'] );
if ( is_array( $upload_dir ) && ( false === $upload_dir['error'] ) ) {
if ( 0 === strpos( $file, $upload_dir['basedir'] ) ) {
$url = str_replace( $upload_dir['basedir'], $upload_dir['baseurl'], $file );
} elseif ( false !== strpos( $file, 'wp-content/uploads' ) ) {
$path_pattern = 'wp-content/uploads';
$url_base = $upload_dir['baseurl'];
if ( is_multisite() && ! ( is_main_network() && is_main_site() && defined( 'MULTISITE' ) ) ) {
if ( defined( 'MULTISITE' ) ) {
$mu_path = '/sites/' . get_current_blog_id();
} else {
$mu_path = '/' . get_current_blog_id();
}
if ( false === strpos( $file, "$path_pattern$mu_path" ) ) {
$url_base = substr( $url_base, 0, - strlen( $mu_path ) );
} else {
$path_pattern .= $mu_path;
}
}
$url = $url_base . substr( $file, strpos( $file, $path_pattern ) + strlen( $path_pattern ) );
}
}
if ( empty( $url ) ) {
if ( ! isset( $GLOBALS['__itsec_cache_wp_content_dir'] ) ) {
$GLOBALS['__itsec_cache_wp_content_dir'] = rtrim( str_replace( '\\', '/', WP_CONTENT_DIR ), '/' );
}
if ( ! isset( $GLOBALS['__itsec_cache_abspath'] ) ) {
$GLOBALS['__itsec_cache_abspath'] = rtrim( str_replace( '\\', '/', ABSPATH ), '/' );
}
if ( 0 === strpos( $file, $GLOBALS['__itsec_cache_wp_content_dir'] ) ) {
$url = WP_CONTENT_URL . str_replace( '\\', '/', preg_replace( '/^' . preg_quote( $GLOBALS['__itsec_cache_wp_content_dir'], '/' ) . '/', '', $file ) );
} elseif ( 0 === strpos( $file, $GLOBALS['__itsec_cache_abspath'] ) ) {
$url = get_option( 'siteurl' ) . str_replace( '\\', '/', preg_replace( '/^' . preg_quote( $GLOBALS['__itsec_cache_abspath'], '/' ) . '/', '', $file ) );
}
}
if ( empty( $url ) && ! $prevent_recursion ) {
$url = self::get_url_from_file( realpath( $file ), $auto_ssl, true );
}
if ( empty( $url ) ) {
return '';
}
if ( $auto_ssl ) {
$url = self::fix_url( $url );
}
return $url;
}
public static function get_file_from_url( $url ) {
$url = preg_replace( '/^https/', 'http', $url );
$url = preg_replace( '/\?.*$/', '', $url );
$file = '';
$upload_dir = ITSEC_Core::get_wp_upload_dir();
if ( is_array( $upload_dir ) && ( false === $upload_dir['error'] ) ) {
if ( 0 === strpos( $url, $upload_dir['baseurl'] ) ) {
$file = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $url );
} elseif ( false !== strpos( $url, 'wp-content/uploads' ) ) {
$path_pattern = 'wp-content/uploads';
$file_base = $upload_dir['basedir'];
if ( is_multisite() && ! ( is_main_network() && is_main_site() && defined( 'MULTISITE' ) ) ) {
if ( defined( 'MULTISITE' ) ) {
$mu_path = '/sites/' . get_current_blog_id();
} else {
$mu_path = '/' . get_current_blog_id();
}
if ( false === strpos( $url, "$path_pattern$mu_path" ) ) {
$file_base = substr( $file_base, 0, - strlen( $mu_path ) );
} else {
$path_pattern .= $mu_path;
}
}
$file = $file_base . substr( $url, strpos( $url, $path_pattern ) + strlen( $path_pattern ) );
}
}
if ( empty( $file ) ) {
if ( ! isset( $GLOBALS['__itsec_cache_wp_content_url'] ) ) {
$GLOBALS['__itsec_cache_wp_content_url'] = preg_replace( '/^https/', 'http', WP_CONTENT_URL );
}
if ( ! isset( $GLOBALS['__itsec_cache_siteurl'] ) ) {
$GLOBALS['__itsec_cache_siteurl'] = preg_replace( '/^https/', 'http', get_option( 'siteurl' ) );
}
if ( 0 === strpos( $url, $GLOBALS['__itsec_cache_wp_content_url'] ) ) {
$file = rtrim( WP_CONTENT_DIR, '\\\/' ) . preg_replace( '/^' . preg_quote( $GLOBALS['__itsec_cache_wp_content_url'], '/' ) . '/', '', $url );
} elseif ( 0 === strpos( $url, $GLOBALS['__itsec_cache_siteurl'] ) ) {
$file = rtrim( ABSPATH, '\\\/' ) . preg_replace( '/^' . preg_quote( $GLOBALS['__itsec_cache_siteurl'], '/' ) . '/', '', $url );
}
}
return $file;
}
public static function fix_url( $url ) {
if ( is_ssl() ) {
$url = preg_replace( '|^http://|', 'https://', $url );
} else {
$url = preg_replace( '|^https://|', 'http://', $url );
}
return $url;
}
/**
* Set a cookie.
*
* @param string $name
* @param string $value
* @param array $args
*/
public static function set_cookie( $name, $value, $args = array() ) {
$args = wp_parse_args( array(
'length' => 0,
'http_only' => true,
), $args );
$expires = $args['length'] ? ITSEC_Core::get_current_time_gmt() + $args['length'] : 0;
setcookie( $name, $value, $expires, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), $args['http_only'] );
}
/**
* Clear a cookie.
*
* @param string $name
*/
public static function clear_cookie( $name ) {
setcookie( $name, ' ', ITSEC_Core::get_current_time_gmt() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, false );
}
/**
* Is the current request a loopback request.
*
* @return bool
*/
public static function is_loopback_request() {
return in_array( self::get_ip(), ITSEC_Modules::get_setting( 'global', 'server_ips' ), true );
}
/**
* Version of {@see wp_slash()} that won't cast numbers to strings.
*
* @param array|string $value
*
* @return array|string
*/
public static function slash( $value ) {
if ( is_array( $value ) ) {
foreach ( $value as $k => $v ) {
if ( is_array( $v ) ) {
$value[ $k ] = self::slash( $v );
} elseif ( is_string( $v ) ) {
$value[ $k ] = addslashes( $v );
}
}
} elseif ( is_string( $value ) ) {
$value = addslashes( $value );
}
return $value;
}
/**
* Format as a ISO 8601 date.
*
* @param int|string|\DateTimeInterface $date Epoch or strtotime compatible date.
*
* @return string|false
*/
public static function to_rest_date( $date = 0 ) {
if ( ! $date ) {
$date = ITSEC_Core::get_current_time_gmt();
} elseif ( $date instanceof \DateTimeInterface ) {
$date = $date->getTimestamp();
} elseif ( ! is_int( $date ) ) {
$date = strtotime( $date );
}
return gmdate( 'Y-m-d\TH:i:sP', $date );
}
/**
* Flatten an array.
*
* @param array $array
*
* @return array
*/
public static function flatten( $array ) {
if ( ! is_array( $array ) ) {
return array( $array );
}
$merge = array();
foreach ( $array as $value ) {
$merge[] = self::flatten( $value );
}
return $merge ? call_user_func_array( 'array_merge', $merge ) : array();
}
/**
* Preload REST API requests.
*
* @param array $requests
*
* @return array
*/
public static function preload_rest_requests( $requests, string $page = '' ) {
if ( $page ) {
/**
* Filters the list of API requests to preload.
*
* @param array $requests
* @param string $page
*/
$requests = apply_filters( 'itsec_preload_requests', $requests, 'tools' );
}
$preload = array();
foreach ( $requests as $key => $config ) {
if ( is_string( $config ) ) {
$key = $config;
$config = array( 'route' => $config );
}
$request = new WP_REST_Request(
isset( $config['method'] ) ? $config['method'] : 'GET',
$config['route']
);
if ( ! empty( $config['query'] ) ) {
$request->set_query_params( $config['query'] );
}
$response = rest_do_request( $request );
if ( $response->get_status() >= 200 && $response->get_status() < 300 ) {
rest_send_allow_header( $response, rest_get_server(), $request );
if ( is_int( $key ) ) {
$key = $config['route'];
if ( ! empty( $config['query'] ) ) {
$key = add_query_arg( $config['query'], $key );
}
if ( ! empty( $config['embed'] ) ) {
$key = add_query_arg( '_embed', '1', $key );
}
}
$preload[ $key ] = array(
'body' => rest_get_server()->response_to_data( $response, ! empty( $config['embed'] ) ),
'headers' => $response->get_headers()
);
}
}
return $preload;
}
/**
* Preloads a REST API request directly into a data store.
*
* This can be useful when we need the data to be immediately
* available when the app renders. Typical preloading still has
* a fractional delay, as it goes through an async fetch stack.
*
* @param string $store The data store handle.
* @param string $action The data store action.
* @param string $route The REST API route to fetch.
* @param array $query Query parameters for the REST API route.
*
* @return bool
*/
public static function preload_request_for_data_store( string $store, string $action, string $route, array $query = [] ): bool {
$request = new WP_REST_Request( 'GET', $route );
$request->set_query_params( $query );
$response = rest_do_request( $request );
if ( $response->is_error() ) {
return false;
}
$data = rest_get_server()->response_to_data( $response, ! empty( $query['_embed'] ) );
return wp_add_inline_script( 'itsec-packages-data', sprintf(
"wp.data.dispatch( '%s' ).%s( %s )",
$store,
$action,
wp_json_encode( $data )
) );
}
/**
* Check if the given string starts with the given needle.
*
* @param string $haystack
* @param string $needle
*
* @return bool
*/
public static function str_starts_with( $haystack, $needle ) {
return 0 === strpos( $haystack, $needle );
}
public static function str_ends_with( $haystack, $needle ) {
return '' === $needle || substr_compare( $haystack, $needle, - strlen( $needle ) ) === 0;
}
/**
* Load a library class definition.
*
* @param string $name
*/
public static function load( $name ) {
require_once( dirname( __FILE__ ) . "/lib/class-itsec-lib-{$name}.php" );
}
/**
* Combine multiple WP_Error instances.
*
* @param WP_Error|null ...$errors
*
* @return WP_Error
*/
public static function combine_wp_error( ...$errors ) {
$combined = new WP_Error();
self::add_to_wp_error( $combined, ...$errors );
return $combined;
}
/**
* Add the subsequent WP Error data to the first WP Error instance.
*
* @param WP_Error $add_to
* @param WP_Error|null ...$errors
*/
public static function add_to_wp_error( WP_Error $add_to, ...$errors ) {
foreach ( $errors as $error ) {
if ( $error ) {
foreach ( $error->get_error_codes() as $code ) {
foreach ( $error->get_error_messages( $code ) as $message ) {
$add_to->add( $code, $message );
}
$data = $error->get_error_data( $code );
if ( null !== $data ) {
$add_to->add_data( $data, $code );
}
}
}
}
}
/**
* Render a file with only the given vars in context.
*
* @param string $file
* @param array $context
* @param bool $echo
*
* @return string|void
*/
public static function render( $file, $context = array(), $echo = true ) {
$__echo = $echo;
$__file = $file;
extract( $context, EXTR_OVERWRITE );
unset( $file, $context, $echo );
if ( ! $__echo ) {
ob_start();
}
require( $__file );
if ( ! $__echo ) {
return ob_get_clean() ?: '';
}
}
/**
* Utility to mark this page as not cacheable.
*/
public static function no_cache() {
nocache_headers();
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
define( 'DONOTCACHEPAGE', true );
}
}
/**
* Get the WordPress branch version.
*
* @return string
* @example 5.2.4 => 5.2
*
*/
public static function get_wp_branch() {
$version = get_bloginfo( 'version' );
list( $major, $minor ) = explode( '.', $version );
return $major . '.' . $minor;
}
/**
* Are two lists equal ignoring order.
*
* @param array $a
* @param array $b
* @param callable|null $cmp
*
* @return bool
*/
public static function equal_sets( array $a, array $b, callable $cmp = null ) {
if ( $cmp ) {
usort( $a, $cmp );
usort( $b, $cmp );
} else {
sort( $a );
sort( $b );
}
return $a === $b;
}
/**
* Convert the return val from {@see ITSEC_Modules::set_settings()} to a WP_Error object.
*
* @param array $updated
*
* @return WP_Error|null
*/
public static function updated_settings_to_wp_error( $updated ) {
if ( is_wp_error( $updated ) ) {
return $updated;
}
if ( $updated['saved'] ) {
return null;
}
if ( $updated['errors'] ) {
$error = self::combine_wp_error( ...$updated['errors'] );
} else {
$error = new \WP_Error( 'itsec.settings.set-failed', __( 'Failed to update settings.', 'better-wp-security' ), [ 'status' => \WP_Http::BAD_REQUEST ] );
}
return $error;
}
/**
* Sanitize the list of roles.
*
* @param string[] $roles
*
* @return array
*/
public static function sanitize_roles( $roles ) {
return array_filter( $roles, static function ( $role ) {
return (bool) get_role( $role );
} );
}
/**
* Get a snapshot of $_SERVER properties.
*
* @return array
*/
public static function get_server_snapshot() {
$whitelist = [
'REQUEST_TIME',
'REQUEST_TIME_FLOAT',
'REQUEST_METHOD',
'HTTPS',
'REQUEST_SCHEME',
'SERVER_PROTOCOL',
'SCRIPT_FILENAME',
];
return array_filter( $_SERVER, static function ( $key ) use ( $whitelist ) {
if ( $key === 'HTTP_COOKIE' ) {
return false;
}
if ( self::str_starts_with( $key, 'HTTP_' ) ) {
return true;
}
if ( self::str_starts_with( $key, 'CONTENT_' ) ) {
return true;
}
return in_array( $key, $whitelist, true );
}, ARRAY_FILTER_USE_KEY );
}
/**
* Version of {@see is_super_admin()} that operates on a `WP_User` instance.
*
* This bypasses an issue where {@see is_super_admin()} cannot be used during the `determine_current_user` filter since
* `is_super_admin` has a side effect of querying for the current user, causing an infinite loop.
*
* @param WP_User $user
*
* @return bool
*/
public static function is_super_admin( WP_User $user ) {
if ( ! $user->exists() ) {
return false;
}
if ( is_multisite() ) {
$super_admins = get_super_admins();
if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins ) ) {
return true;
}
} else {
if ( $user->has_cap( 'delete_users' ) ) {
return true;
}
}
return false;
}
/**
* Performs a {@see dbDelta()} but reports any errors encountered.
*
* @param string $delta
*
* @return WP_Error
*/
public static function db_delta_with_error_handling( $delta ) {
global $wpdb, $EZSQL_ERROR;
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
$err_count = is_array( $EZSQL_ERROR ) ? count( $EZSQL_ERROR ) : 0;
$showed_errors = $wpdb->show_errors( false );
dbDelta( $delta );
if ( $showed_errors ) {
$wpdb->show_errors();
}
$wp_error = new WP_Error();
if ( is_array( $EZSQL_ERROR ) ) {
for ( $i = $err_count, $i_max = count( $EZSQL_ERROR ); $i < $i_max; $i ++ ) {
$error = $EZSQL_ERROR[ $i ];
if ( empty( $error['error_str'] ) || empty( $error['query'] ) || 0 === strpos( $error['query'], 'DESCRIBE ' ) ) {
continue;
}
$wp_error->add( 'db_delta_error', $error['error_str'] );
}
}
return $wp_error;
}
/**
* Get info used to help evaluate requirements according to
* {@see ITSEC_Lib::evaluate_requirements()}.
*
* @return array[]
*/
public static function get_requirements_info(): array {
return [
'load' => ITSEC_Core::is_loading_early() ? 'early' : 'normal',
'server' => [
'php' => explode( '-', PHP_VERSION )[0],
'extensions' => [
'OpenSSL' => self::is_func_allowed( 'openssl_verify' ),
],
],
];
}
/**
* Evaluate whether this site passes the given requirements.
*
* @param array $requirements The requirements list. Formatted according to the schema.
* @param bool $add_messages Whether to add error messages.
*
* @return WP_Error
*/
public static function evaluate_requirements( array $requirements, bool $add_messages = true ) {
$schema = [
'type' => 'object',
'additionalProperties' => false,
'properties' => [
'version' => [
'type' => 'object',
'additionalProperties' => false,
'properties' => [
'pro' => [
'type' => 'string',
'required' => true,
],
'free' => [
'type' => 'string',
'required' => true,
],
],
],
'ssl' => [
'type' => 'boolean',
],
'feature-flags' => [
'type' => 'array',
'items' => [
'type' => 'string',
],
],
'multisite' => [
'type' => 'string',
'enum' => [ 'enabled', 'disabled' ],
],
'server' => [
'type' => 'object',
'properties' => [
'php' => [
'type' => 'string',
],
'extensions' => [
'type' => 'array',
'items' => [
'type' => 'string',
'enum' => [ 'OpenSSL' ],
],
],
],
],
'load' => [
'type' => 'string',
'enum' => [ 'normal', 'early' ],
],
'ip' => [
'type' => 'boolean',
],
],
];
if ( ITSEC_Core::is_development() ) {
$valid_requirements = rest_validate_value_from_schema( $requirements, $schema );
if ( is_wp_error( $valid_requirements ) ) {
return $valid_requirements;
}
}
$error = new WP_Error();
foreach ( $requirements as $kind => $requirement ) {
switch ( $kind ) {
case 'version':
$key = ITSEC_Core::is_pro() ? 'pro' : 'free';
$version = $requirement[ $key ];
if ( version_compare( ITSEC_Core::get_plugin_version(), $version, '<' ) ) {
if ( $add_messages ) {
$error->add(
'version',
sprintf( __( 'You must be running at least version %s of Solid Security.', 'better-wp-security' ), $version )
);
} else {
$error->add( 'version', '' );
}
}
break;
case 'ssl':
if ( $requirement !== is_ssl() ) {
if ( $add_messages ) {
$error->add(
'ssl',
$requirement ? __( 'Your site must support SSL.', 'better-wp-security' ) : __( 'Your site must not support SSL.', 'better-wp-security' )
);
} else {
$error->add( 'ssl', '' );
}
}
break;
case 'feature-flags':
foreach ( $requirement as $flag ) {
if ( ! ITSEC_Lib_Feature_Flags::is_enabled( $flag ) ) {
if ( $add_messages ) {
$error->add(
'feature-flags',
sprintf(
__( 'The \'%s\' feature flag must be enabled.', 'better-wp-security' ),
( ITSEC_Lib_Feature_Flags::get_flag_config( $flag )['title'] ?? $flag ) ?: $flag
)
);
} else {
$error->add( 'feature-flags', '' );
}
}
}
break;
case 'multisite':
if ( $requirement === 'enabled' && ! is_multisite() ) {
if ( $add_messages ) {
$error->add(
'multisite',
__( 'Multisite must be enabled.', 'better-wp-security' )
);
} else {
$error->add( 'multisite', '' );
}
} elseif ( $requirement === 'disabled' && is_multisite() ) {
if ( $add_messages ) {
$error->add(
'multisite',
__( 'Multisite is not supported.', 'better-wp-security' )
);
} else {
$error->add( 'multisite', '' );
}
}
break;
case 'server':
$info = self::get_requirements_info();
if ( isset( $requirement['php'] ) && version_compare( $info['server']['php'], $requirement['php'], '<' ) ) {
$error->add( 'server', sprintf( __( 'You must be running PHP version %s or later.', 'better-wp-security' ), $requirement['php'] ) );
}
$missing = array_filter( $requirement['extensions'] ?? [], function ( $extension ) use ( $info ) {
return empty( $info['server']['extensions'][ $extension ] );
} );
if ( $missing ) {
if ( ! $add_messages ) {
$error->add( 'server', '' );
break;
}
if ( count( $missing ) === 1 ) {
$message = sprintf( __( 'The %s PHP extension is required.', 'better-wp-security' ), ITSEC_Lib::first( $missing ) );
} else {
$message = wp_sprintf(
_n(
'The following PHP extension is required: %l.',
'The following PHP extensions are required: %l.',
count( $missing ),
'better-wp-security'
),
$missing
);
}
$error->add( 'server', $message );
}
break;
case 'load':
if ( $requirement === 'normal' && ITSEC_Core::is_loading_early() ) {
if ( $add_messages ) {
$error->add( 'load', __( 'Loading Solid Security via an MU-Plugin is not supported.', 'better-wp-security' ) );
} else {
$error->add( 'load', '' );
}
} elseif ( $requirement === 'early' && ! ITSEC_Core::is_loading_early() ) {
if ( $add_messages ) {
$error->add( 'load', __( 'Loading Solid Security without an MU-Plugin is not supported.', 'better-wp-security' ) );
} else {
$error->add( 'load', '' );
}
}
break;
case 'ip':
if ( ! ITSEC_Lib_IP_Detector::is_configured() ) {
if ( $add_messages ) {
$error->add( 'ip', __( 'You must select an IP Detection method in Global Settings.', 'better-wp-security' ) );
} else {
$error->add( 'ip', '' );
}
}
break;
}
}
/**
* Fires when a requirements error is encountered.
*
* @param WP_Error $error
*/
do_action( 'itsec_requirements_error', $error );
return $error;
}
/**
* Converts a JSON Schema to a WP-CLI synopsis.
*
* @param array $schema
*
* @return array
*/
public static function convert_schema_to_cli_synopsis( array $schema ) {
$synopsis = [];
$required = isset( $schema['required'] ) ? $schema['required'] : [];
if ( isset( $schema['properties'] ) ) {
foreach ( $schema['properties'] as $property => $config ) {
$param = [
'name' => $property,
];
if ( 'boolean' === $config['type'] ) {
$param['type'] = 'flag';
} else {
$param['type'] = 'assoc';
}
if ( array_key_exists( 'default', $config ) ) {
$param['default'] = $config['default'];
}
if ( isset( $config['enum'] ) ) {
$param['options'] = $config['enum'];
}
if ( ( ! isset( $config['required'] ) || true !== $config['required'] ) && ! in_array( $property, $required, true ) ) {
$param['optional'] = true;
}
if ( isset( $config['description'] ) ) {
$param['description'] = $config['description'];
}
$synopsis[] = $param;
}
}
if ( ! empty( $schema['additionalProperties'] ) ) {
$synopsis[] = [
'type' => 'generic',
];
}
return $synopsis;
}
/**
* Decode a string with URL-safe Base64.
*
* @param string $input A Base64 encoded string
*
* @return string A decoded string
*/
public static function url_safe_b64_decode( $input ) {
$remainder = strlen( $input ) % 4;
if ( $remainder ) {
$padlen = 4 - $remainder;
$input .= str_repeat( '=', $padlen );
}
return base64_decode( strtr( $input, '-_', '+/' ) );
}
/**
* Encode a string with URL-safe Base64.
*
* @param string $input The string you want encoded
*
* @return string The base64 encode of what you passed in
*/
public static function url_safe_b64_encode( $input ) {
return str_replace( '=', '', strtr( base64_encode( $input ), '+/', '-_' ) );
}
/**
* Compares the WordPress version with the given version.
*
* @param string $version The version to compare with.
* @param string $operator The operator.
* @param bool $allow_dev Whether to treat dev versions as stable.
*
* @return bool
*/
public static function wp_version_compare( $version, $operator, $allow_dev = true ) {
global $wp_version;
if ( $allow_dev ) {
list( $wp_version ) = explode( '-', $wp_version );
}
return version_compare( $wp_version, $version, $operator );
}
/**
* Checks if the WordPress version is at least the given version.
*
* @param string $version The version to check WP for.
* @param bool $allow_dev Whether to treat dev versions as stable.
*
* @return bool
*/
public static function is_wp_version_at_least( $version, $allow_dev = true ) {
return static::wp_version_compare( $version, '>=', $allow_dev );
}
/**
* Gets the WordPress login URL.
*
* @param string $action A particular login action to use.
* @param string $redirect Where to redirect the user to after login.
* @param string $scheme The scheme to use. Accepts `login_post` for form submissions.
*
* @return string
*/
public static function get_login_url( $action = '', $redirect = '', $scheme = 'login' ) {
if ( 'login_post' === $scheme || ( $action && 'login' !== $action ) ) {
$url = 'wp-login.php';
if ( $action ) {
$url = add_query_arg( 'action', urlencode( $action ), $url );
}
if ( $redirect ) {
$url = add_query_arg( 'redirect_to', urlencode( $redirect ), $url );
}
$url = site_url( $url, $scheme );
} else {
$url = wp_login_url( $redirect );
if ( $action ) {
$url = add_query_arg( 'action', urlencode( $action ), $url );
}
}
if ( function_exists( 'is_wpe' ) && is_wpe() ) {
$url = add_query_arg( 'wpe-login', 'true', $url );
}
return apply_filters( 'itsec_login_url', $url, $action, $redirect, $scheme );
}
/**
* Extends a service definition, ignoring if the service has been frozen.
*
* @param \iThemesSecurity\Strauss\Pimple\Container $c
* @param string $id
* @param callable $extend
*
* @return bool
*/
public static function extend_if_able( \iThemesSecurity\Strauss\Pimple\Container $c, string $id, callable $extend ): bool {
try {
$c->extend( $id, $extend );
return true;
} catch ( \iThemesSecurity\Strauss\Pimple\Exception\FrozenServiceException $e ) {
return false;
}
}
/**
* Resolve JSON Schema refs.
*
* @param array $schema
*
* @return array
*/
public static function resolve_schema_refs( array $schema ): array {
if ( isset( $schema['definitions'] ) ) {
array_walk( $schema, [ static::class, 'resolve_ref' ], $schema['definitions'] );
}
return $schema;
}
/**
* Resolves $ref entries at any point in the config.
*
* Currently, only a simplified form of JSON Pointers are supported where `/` is the only
* allowed control character.
*
* Additionally, the `$ref` keyword must start with `#/definitions`.
*
* @param mixed $value The incoming value.
* @param string $key The array key.
* @param array $definitions The shared definitions.
*/
private static function resolve_ref( &$value, $key, $definitions ) {
if ( ! is_array( $value ) ) {
return;
}
if ( isset( $value['$ref'] ) ) {
$ref = str_replace( '#/definitions/', '', $value['$ref'] );
$value = \ITSEC_Lib::array_get( $definitions, $ref, null, '/' );
return;
}
array_walk( $value, [ static::class, 'resolve_ref' ], $definitions );
}
/**
* Generates a v4 UUID using a CSPRNG.
*
* @return string
*/
public static function generate_uuid4(): string {
return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
wp_rand( 0, 0xffff ),
wp_rand( 0, 0xffff ),
wp_rand( 0, 0xffff ),
wp_rand( 0, 0x0fff ) | 0x4000,
wp_rand( 0, 0x3fff ) | 0x8000,
wp_rand( 0, 0xffff ),
wp_rand( 0, 0xffff ),
wp_rand( 0, 0xffff )
);
}
/**
* Clears the WordPress auth cookies.
*
* This function is safe to call before plugins have been loaded.
* But the request MUST exist after calling it.
*
* @return void
*/
public static function clear_auth_cookie() {
if ( ! function_exists( 'wp_clear_auth_cookie' ) ) {
if ( is_multisite() ) {
ms_cookie_constants();
}
// Define constants after multisite is loaded.
wp_cookie_constants();
require_once ABSPATH . 'wp-includes/pluggable.php';
}
wp_clear_auth_cookie();
}
public static function recursively_json_serialize( $value ) {
if ( $value instanceof JsonSerializable ) {
return $value->jsonSerialize();
}
if ( is_array( $value ) ) {
foreach ( $value as $k => $v ) {
$value[ $k ] = self::recursively_json_serialize( $v );
}
}
return $value;
}
}
Fatal error: Uncaught Error: Class "ITSEC_Lib" not found in /home/mediaco1/public_html/wp-content/plugins/better-wp-security/core/rest.php:28
Stack trace:
#0 /home/mediaco1/public_html/wp-content/plugins/better-wp-security/core/core.php(135): ITSEC_REST->run()
#1 /home/mediaco1/public_html/wp-content/plugins/better-wp-security/better-wp-security.php(54): ITSEC_Core->init('/home/mediaco1/...', 'Solid Security ...')
#2 /home/mediaco1/public_html/wp-settings.php(545): include_once('/home/mediaco1/...')
#3 /home/mediaco1/public_html/wp-config.php(108): require_once('/home/mediaco1/...')
#4 /home/mediaco1/public_html/wp-load.php(50): require_once('/home/mediaco1/...')
#5 /home/mediaco1/public_html/wp-blog-header.php(13): require_once('/home/mediaco1/...')
#6 /home/mediaco1/public_html/index.php(17): require('/home/mediaco1/...')
#7 {main}
thrown in /home/mediaco1/public_html/wp-content/plugins/better-wp-security/core/rest.php on line 28