���� JFIF    �� �        "" $(4,$&1'-=-157:::#+?D?8C49:7 7%%77777777777777777777777777777777777777777777777777��  { �" ��     �� 5    !1AQa"q�2��BR��#b�������  ��  ��   ? ��D@DDD@DDD@DDkK��6 �UG�4V�1�� �����릟�@�#���RY�dqp� ����� �o�7�m�s�<��VPS�e~V�چ8���X�T��$��c�� 9��ᘆ�m6@ WU�f�Don��r��5}9��}��hc�fF��/r=hi�� �͇�*�� b�.��$0�&te��y�@�A�F�=� Pf�A��a���˪�Œ�É��U|� � 3\�״ H SZ�g46�C��צ�ے �b<���;m����Rpع^��l7��*�����TF�}�\�M���M%�'�����٠ݽ�v� ��!-�����?�N!La��A+[`#���M����'�~oR�?��v^)��=��h����A��X�.���˃����^Ə��ܯsO"B�c>; �e�4��5�k��/CB��.  �J?��;�҈�������������������~�<�VZ�ꭼ2/)Í”jC���ע�V�G�!���!�F������\�� Kj�R�oc�h���:Þ I��1"2�q×°8��Р@ז���_C0�ր��A��lQ��@纼�!7��F�� �]�sZ B�62r�v�z~�K�7�c��5�.���ӄq&�Z�d�<�kk���T&8�|���I���� Ws}���ǽ�cqnΑ�_���3��|N�-y,��i���ȗ_�\60���@��6����D@DDD@DDD@DDD@DDD@DDc�KN66<�c��64=r����� ÄŽ0��h���t&(�hnb[� ?��^��\��â|�,�/h�\��R��5�? �0�!צ܉-����G����٬��Q�zA���1�����V��� �:R���`�$��ik��H����D4�����#dk����� h�}����7���w%�������*o8wG�LycuT�.���ܯ7��I��u^���)��/c�,s�Nq�ۺ�;�ך�YH2���.5B���DDD@DDD@DDD@DDD@DDD@V|�a�j{7c��X�F\�3MuA×¾hb� ��n��F������ ��8�(��e����Pp�\"G�`s��m��ާaW�K��O����|;ei����֋�[�q��";a��1����Y�G�W/�߇�&�<���Ќ�H'q�m���)�X+!���=�m�ۚ丷~6a^X�)���,�>#&6G���Y��{����"" """ """ """ """ ""��at\/�a�8 �yp%�lhl�n����)���i�t��B�������������?��modskinlienminh.com - WSOX ENC PK!08kk hardening.phpnu[risk_naming = [ 'l' => __('low-risk', 'really-simple-ssl'), 'm' => __('medium-risk', 'really-simple-ssl'), 'h' => __('high-risk', 'really-simple-ssl'), 'c' => __('critical', 'really-simple-ssl'), ]; self::$_this = $this; } function hardening_data( array $response, string $action, $data ): array { if ( ! rsssl_user_can_manage() ) { return $response; } if ($action === 'hardening_data') { $response = $this->get_stats( $data ); } return $response; } static function this() { return self::$_this; } /* Public Section 2: DataGathering */ /** * @param $data * * @return array */ public function get_stats($data): array { if ( ! rsssl_user_can_manage() ) { return []; } $vulEnabled = rsssl_get_option('enable_vulnerability_scanner'); //now we fetch all plugins that have an update available. $stats = [ 'updates' => $this->getAllUpdatesCount(), 'lastChecked' => time(), 'riskNaming' => $this->risk_naming, 'vulEnabled' => $vulEnabled, ]; $repsonse = [ "request_success" => true, 'data' => apply_filters('rsssl_vulnerability_data', $stats), ]; return $repsonse; } /** * Gets the count of all available updates for core, plugins, and themes. * * @return int The count of all available updates. */ public function getAllUpdatesCount(): int { $updatesData = wp_get_update_data(); // Checks if the 'counts' key exists in the array and it's an array itself. if (isset($updatesData['counts']) && is_array($updatesData['counts'])) { //we only want core, plugins and themes. $updatesCounts = array_slice($updatesData['counts'], 0, 3); return array_sum($updatesCounts); } // Fallback return in case there's no 'counts' key or it's not an array. return 0; } } PK!(E0 cron.phpnu[ 5 * MINUTE_IN_SECONDS, // seconds 'display' => __('Once every 5 minutes') ); $schedules['rsssl_daily'] = array( 'interval' => DAY_IN_SECONDS, 'display' => __( 'Once every day' ) ); $schedules['rsssl_every_three_hours'] = array( 'interval' => 3 * HOUR_IN_SECONDS, 'display' => __( 'Every three hours' ) ); $schedules['rsssl_weekly'] = array( 'interval' => WEEK_IN_SECONDS, 'display' => __( 'Once every week' ) ); return $schedules; } register_deactivation_hook( rsssl_file, 'rsssl_clear_scheduled_hooks' ); function rsssl_clear_scheduled_hooks() { wp_clear_scheduled_hook( 'rsssl_every_day_hook' ); wp_clear_scheduled_hook( 'rsssl_every_week_hook' ); wp_clear_scheduled_hook( 'rsssl_every_five_minutes_hook' ); wp_clear_scheduled_hook( 'rsssl_every_three_hours' ); wp_clear_scheduled_hook( 'rsssl_ssl_process_hook' ); } /** * Multisite cron */ add_action('plugins_loaded', 'rsssl_multisite_schedule_cron', 15); function rsssl_multisite_schedule_cron() { if ( get_site_option('rsssl_ssl_activation_active') ) { if ( !wp_next_scheduled('rsssl_ssl_process_hook') ) { wp_schedule_event(time(), 'rsssl_one_minute', 'rsssl_ssl_process_hook'); } } else { wp_clear_scheduled_hook('rsssl_ssl_process_hook'); } add_action( 'rsssl_ssl_process_hook', array( RSSSL()->multisite, 'run_ssl_process' ) ); } PK!O security.phpnu[includes(); if ( rsssl_admin_logged_in() ) { self::$instance->firewall_manager = new rsssl_firewall_manager(); self::$instance->hardening = new rsssl_hardening(); } } return self::$instance; } private function includes() { $path = rsssl_path.'security/'; require_once( $path . 'integrations.php' ); require_once( $path . 'hardening.php' ); require_once( $path . 'cron.php' ); /** * Load only on back-end */ if ( rsssl_admin_logged_in() ) { require_once( $path . 'functions.php' ); require_once( $path . 'deactivate-integration.php' ); require_once( $path . 'firewall-manager.php' ); require_once( $path . 'tests.php' ); require_once( $path . 'notices.php' ); require_once( $path . 'sync-settings.php' ); } } } function RSSSL_SECURITY() { return REALLY_SIMPLE_SECURITY::instance(); } add_action('plugins_loaded', 'RSSSL_SECURITY', 9);PK!]vKKdeactivate-integration.phpnu[ $plugin_data ) { if ( isset($plugin_data['has_deactivation']) && $plugin_data['has_deactivation'] && isset($plugin_data['option_id']) && $plugin_data['option_id'] === $field_id ) { //add to deactivated list $current_list = get_option('rsssl_deactivate_list', []); if ( !in_array($plugin, $current_list)) { $current_list[] = $plugin; update_option('rsssl_deactivate_list', $current_list, false); } } } } } add_action( "rsssl_after_save_field", "rsssl_handle_integration_deactivation", 10, 4 ); /** * Remove a plugin from the deactivation list if deactivation procedure was completed * @param string $plugin * * @return void */ function rsssl_remove_from_deactivation_list($plugin){ if (!rsssl_user_can_manage()) { return; } $deactivate_list = get_option('rsssl_deactivate_list', []); if ( in_array($plugin, $deactivate_list )) { $index = array_search($plugin, $deactivate_list); unset($deactivate_list[$index]); update_option('rsssl_deactivate_list', $deactivate_list, false ); } }PK!6'' notices.phpnu[/', '--br--', $code, 1); $code_arr = explode('--br--', $code); $code_arr = array_map('esc_html', $code_arr); $code = implode('
', $code_arr); return '
' . $code . '
'; } function rsssl_general_security_notices( $notices ) { $code = rsssl_parse_htaccess_to_html( get_site_option('rsssl_htaccess_rules','') ); $uploads_code = rsssl_parse_htaccess_to_html( get_site_option('rsssl_uploads_htaccess_rules','') ); $notices['application-passwords'] = array( 'callback' => 'rsssl_wp_is_application_passwords_available', 'score' => 5, 'output' => array( 'true' => array( 'msg' => __("Disable application passwords.", "really-simple-ssl"), 'icon' => 'premium', 'url' => 'https://really-simple-ssl.com/definition/what-are-application-passwords/', 'dismissible' => true, 'highlight_field_id' => 'disable_application_passwords', ), ), ); $notices['htaccess_status'] = array( 'callback' => 'rsssl_htaccess_status', 'score' => 5, 'output' => array( 'not-writable' => array( 'title' => __(".htaccess not writable", "really-simple-ssl"), 'msg' => __("An option that requires the .htaccess file is enabled, but the file is not writable.", "really-simple-ssl").' '.__("Please add the following lines to your .htaccess, or set it to writable:", "really-simple-ssl").$code, 'icon' => 'warning', 'dismissible' => true, 'plusone' => true, 'url' => 'https://really-simple-ssl.com/manual/editing-htaccess/', ), 'not-exists' => array( 'title' => __(".htaccess does not exist", "really-simple-ssl"), 'msg' => __("An option that requires the .htaccess file is enabled, but the file does not exist.", "really-simple-ssl").' '.__("Please add the following lines to your .htaccess, or set it to writable:", "really-simple-ssl").$code, 'icon' => 'warning', 'dismissible' => true, 'plusone' => true, 'url' => 'https://really-simple-ssl.com/manual/editing-htaccess/', ), ), 'show_with_options' => [ 'disable_indexing', 'redirect' ] ); $notices['htaccess_status_uploads'] = array( 'callback' => 'rsssl_uploads_htaccess_status', 'score' => 5, 'output' => array( 'not-writable' => array( 'title' => __(".htaccess in uploads not writable", "really-simple-ssl"), 'msg' => __("An option that requires the .htaccess file in the uploads directory is enabled, but the file is not writable.", "really-simple-ssl").' '.__("Please add the following lines to your .htaccess, or set it to writable:", "really-simple-ssl").$uploads_code, 'icon' => 'warning', 'dismissible' => true, 'plusone' => true, 'url' => 'https://really-simple-ssl.com/manual/editing-htaccess/', ), ), 'show_with_options' => [ 'block_code_execution_uploads', ] ); $notices['block_display_is_login_enabled'] = array( 'condition' => ['NOT option_block_display_is_login'], 'callback' => '_true_', 'score' => 5, 'output' => array( 'true' => array( 'highlight_field_id' => 'block_display_is_login', 'msg' => __("It is currently possible to create an administrator user with the same login and display name.", "really-simple-ssl"), 'icon' => 'open', 'dismissible' => true, ), ), ); $notices['display_name_is_login_exists'] = array( 'condition' => ['rsssl_get_users_where_display_name_is_login'], 'callback' => '_true_', 'score' => 5, 'output' => array( 'true' => array( 'url' => 'https://really-simple-ssl.com/manual/login-and-display-names-should-be-different-for-wordpress/', 'msg' => __("We have detected administrator roles where the login and display names are the same.", "really-simple-ssl") . " " . rsssl_list_users_where_display_name_is_login_name() . "", 'icon' => 'open', 'dismissible' => true, ), ), ); $notices['debug_log'] = array( 'condition' => ['rsssl_debug_log_file_exists_in_default_location'], 'callback' => 'rsssl_is_debugging_enabled', 'score' => 5, 'output' => array( 'true' => array( 'highlight_field_id' => 'change_debug_log_location', 'title' => __("Debugging", "really-simple-ssl"), 'msg' => __("Your site logs information to a public debugging file.", "really-simple-ssl"), 'url' => 'https://really-simple-ssl.com/instructions/about-hardening-features/', 'icon' => 'premium', 'dismissible' => true, ), ), 'show_with_options' => [ 'change_debug_log_location', ], ); $notices['user_id_one'] = array( 'condition' => ['NOT option_disable_user_enumeration'], 'callback' => '_true_', 'score' => 5, 'output' => array( 'true' => array( 'msg' => __("Your site is vulnerable to user enumeration attacks.", "really-simple-ssl"), 'icon' => 'warning', 'title' => __('Prevent user enumeration','really-simple-ssl'), 'url' => 'https://really-simple-ssl.com/what-are-user-enumeration-attacks/', 'dismissible' => true, 'highlight_field_id' => 'disable_user_enumeration', ), ), 'show_with_options' => [ 'disable_user_enumeration', ], ); $notices['username_admin_exists'] = array( 'condition' => ['rsssl_has_admin_user'], 'callback' => '_true_', 'score' => 5, 'output' => array( 'true' => array( 'highlight_field_id' => 'rename_admin_user', 'title' => __("Username", "really-simple-ssl"), 'msg' => __("Your site registered a user with the name 'admin'.", "really-simple-ssl"), 'icon' => 'warning', 'dismissible' => true, ), ), 'show_with_options' => [ 'rename_admin_user', ], ); $notices['new_username_empty'] = array( 'condition' => ['rsssl_has_admin_user', 'option_rename_admin_user', 'NOT rsssl_new_username_valid'], 'callback' => '_true_', 'score' => 5, 'output' => array( 'true' => array( 'highlight_field_id' => 'rename_admin_user', 'title' => __("Username", "really-simple-ssl"), 'msg' => __("Rename admin user enabled: Please choose a new username of at least 3 characters, which is not in use yet.", "really-simple-ssl"), 'icon' => 'warning', 'dismissible' => true, ), ), 'show_with_options' => [ 'new_admin_user_login', ], ); $notices['code-execution-uploads-allowed'] = array( 'callback' => 'rsssl_code_execution_allowed', 'score' => 5, 'output' => array( 'true' => array( 'highlight_field_id' => 'block_code_execution_uploads', 'msg' => __("Code execution is allowed in the public 'Uploads' folder.", "really-simple-ssl"), 'icon' => 'open', 'dismissible' => true, ), ), ); $notices['db-prefix-notice'] = array( 'callback' => 'rsssl_is_default_wp_prefix', 'score' => 5, 'output' => array( 'false' => array( 'msg' => __("Your database prefix is renamed and randomized. Awesome!", "really-simple-ssl"), 'icon' => 'success', 'dismissible' => true, ), 'true' => array( 'msg' => __("Your database prefix is set to the default 'wp_'.", "really-simple-ssl"), 'icon' => 'premium', 'dismissible' => true, 'url' => 'https://really-simple-ssl.com/instructions/about-hardening-features/' ), ), ); $notices['vulnerabilities'] = array( 'condition' => ['NOT option_enable_vulnerability_scanner'], 'callback' => '_true_', 'score' => 5, 'output' => array( 'true' => array( 'msg' => __("Really Simple SSL has a new feature! Introducing Vulnerability Detection, enable it now.", "really-simple-ssl"), 'icon' => 'open', 'dismissible' => true, 'url' => 'https://really-simple-ssl.com/vulnerability-detection/', 'highlight_field_id' => 'enable_vulnerability_scanner', 'plusone' => true, ), ), ); // $notices['xmlrpc'] = array( // 'callback' => 'rsssl_xmlrpc_allowed', // 'score' => 10, // 'output' => array( // 'true' => array( // 'highlight_field_id' => 'xmlrpc', // 'msg' => __("XMLRPC is enabled on your site.", "really-simple-ssl"), // 'icon' => 'warning', // 'plusone' => true, // ), // ), // 'show_with_options' => [ // 'xmlrpc', // ], // ); $notices['file-editing'] = array( 'callback' => 'rsssl_file_editing_allowed', 'score' => 5, 'output' => array( 'true' => array( 'highlight_field_id' => 'disable_file_editing', 'msg' => __("The built-in file editors are accessible to others.", "really-simple-ssl"), // 'url' => 'https://wordpress.org/support/article/editing-wp-config-php/#disable-the-plugin-and-theme-editor', 'icon' => 'open', 'dismissible' => true, ), ), ); $notices['registration'] = array( 'callback' => 'rsssl_user_registration_allowed', 'score' => 5, 'output' => array( 'true' => array( 'highlight_field_id' => 'disable_anyone_can_register', 'msg' => __("Anyone can register an account on your site. Consider disabling this option in the WordPress general settings.", "really-simple-ssl"), 'icon' => 'open', 'plusone' => false, 'dismissible' => true, ), ), ); $notices['hide-wp-version'] = array( 'callback' => 'rsssl_src_contains_wp_version', 'score' => 5, 'output' => array( 'true' => array( 'highlight_field_id' => 'hide_wordpress_version', 'msg' => __("Your WordPress version is visible to others.", "really-simple-ssl"), 'icon' => 'open', 'dismissible' => true, ), ), ); // $notices['login-url-not-working'] = array( // 'callback' => 'NOT rsssl_new_login_url_working', // 'score' => 5, // 'output' => array( // 'true' => array( // 'msg' => __("Your new login URL does not seem to work. Still using /wp-admin and /wp-login.php.", "really-simple-ssl"), // 'url' => 'https://really-simple-ss.com/', // 'icon' => 'warning', // 'dismissible' => true, // ), // ), // ); return $notices; } add_filter('rsssl_notices', 'rsssl_general_security_notices'); PK! ?SBB functions.phpnu[admin->mixed_content_fixer_detected(); } //expire in five minutes $headers = get_transient('rsssl_can_use_curl_headers_check'); set_transient('rsssl_can_use_curl_headers_check', $headers, 5 * MINUTE_IN_SECONDS); //no change if ( $field_value === $prev_value ) { return; } if ( $field_id === 'disable_http_methods' ) { delete_option( 'rsssl_http_methods_allowed' ); rsssl_http_methods_allowed(); } if ( $field_id === 'xmlrpc' ) { delete_transient( 'rsssl_xmlrpc_allowed' ); rsssl_xmlrpc_allowed(); } if ( $field_id === 'disable_indexing' ) { delete_transient( 'rsssl_directory_indexing_status' ); rsssl_directory_indexing_allowed(); } if ( $field_id === 'block_code_execution_uploads' ) { delete_transient( 'rsssl_code_execution_allowed_status' ); rsssl_code_execution_allowed(); } if ( $field_id === 'hide_wordpress_version' ) { delete_option( 'rsssl_wp_version_detected' ); rsssl_src_contains_wp_version(); } if ( $field_id === 'rename_admin_user' ) { delete_transient('rsssl_admin_user_count'); rsssl_has_admin_user(); } } add_action( "rsssl_after_save_field", 'rsssl_maybe_clear_transients', 100, 4 ); } if ( !function_exists('rsssl_remove_htaccess_security_edits') ) { /** * Clean up on deactivation * * @return void */ function rsssl_remove_htaccess_security_edits() { if ( ! rsssl_user_can_manage() ) { return; } if ( ! rsssl_uses_htaccess() ) { return; } $htaccess_file = RSSSL()->admin->htaccess_file(); if ( ! file_exists( $htaccess_file ) ) { return; } $start = "\n" . '#Begin Really Simple Security'; $end = '#End Really Simple Security' . "\n"; $pattern = '/'.$start.'(.*?)'.$end.'/is'; /** * htaccess in uploads dir */ $upload_dir = wp_get_upload_dir(); $htaccess_file_uploads = trailingslashit( $upload_dir['basedir']).'.htaccess'; $content_htaccess_uploads = file_exists($htaccess_file_uploads ) ? file_get_contents($htaccess_file_uploads) : ''; if (preg_match($pattern, $content_htaccess_uploads) && is_writable( $htaccess_file_uploads )) { $content_htaccess_uploads = preg_replace($pattern, "", $content_htaccess_uploads); file_put_contents( $htaccess_file_uploads, $content_htaccess_uploads ); } /** * htaccess in root dir */ $htaccess_file = RSSSL()->admin->htaccess_file(); $content_htaccess = file_get_contents($htaccess_file); //remove old style rules $pattern_1 = "/#\s?BEGIN\s?rlrssslReallySimpleSSL.*?#\s?END\s?rlrssslReallySimpleSSL/s"; $pattern_2 = "/#\s?BEGIN\s?Really Simple SSL Redirect.*?#\s?END\s?Really Simple SSL Redirect/s"; $content_htaccess = preg_replace([$pattern_1, $pattern_2], "", $content_htaccess); if (preg_match($pattern, $content_htaccess) && is_writable( $htaccess_file ) ) { $content_htaccess = preg_replace($pattern, "", $content_htaccess); file_put_contents( $htaccess_file, $content_htaccess ); } } } /** * Wrap the security headers */ if ( ! function_exists('rsssl_wrap_htaccess' ) ) { function rsssl_wrap_htaccess() { if ( !rsssl_user_can_manage() ) { return; } if ( ! rsssl_uses_htaccess() ) { return; } if ( rsssl_get_option('do_not_edit_htaccess') ) { return; } if ( !rsssl_is_logged_in_rest() && !RSSSL()->admin->is_settings_page() && current_filter() !== 'rocket_activation' && current_filter() !== 'rocket_deactivation' ) { return; } if ( get_site_option('rsssl_htaccess_error') ) { delete_site_option( 'rsssl_htaccess_error' ); delete_site_option( 'rsssl_htaccess_rules' ); } if ( get_site_option('rsssl_uploads_htaccess_error') ) { delete_site_option( 'rsssl_uploads_htaccess_error' ); delete_site_option( 'rsssl_uploads_htaccess_rules' ); } if ( get_option('rsssl_updating_htaccess') ) { return; } update_option('rsssl_updating_htaccess', true, false ); $start = '#Begin Really Simple Security'; $end = "\n" . '#End Really Simple Security' . "\n"; $pattern_content = '/'.$start.'(.*?)'.$end.'/is'; $pattern = '/'.$start.'.*?'.$end.'/is'; /** * htaccess in uploads dir */ $rules_uploads = apply_filters( 'rsssl_htaccess_security_rules_uploads', []); $upload_dir = wp_get_upload_dir(); $htaccess_file_uploads = trailingslashit( $upload_dir['basedir']).'.htaccess'; if ( ! file_exists( $htaccess_file_uploads ) && count($rules_uploads)>0 ) { if ( is_writable(trailingslashit( $upload_dir['basedir'])) ) { file_put_contents($htaccess_file_uploads, ''); } else { update_site_option( 'rsssl_uploads_htaccess_error', 'not-writable' ); $rules_uploads_result = implode( '', array_column( $rules_uploads, 'rules' ) ); update_site_option( 'rsssl_uploads_htaccess_rules', $rules_uploads_result ); } } if ( file_exists( $htaccess_file_uploads ) ) { $content_htaccess_uploads = file_exists( $htaccess_file_uploads ) ? file_get_contents( $htaccess_file_uploads ) : ''; preg_match( $pattern_content, $content_htaccess_uploads, $matches ); if ( ( ! empty( $matches[1] ) && empty( $rules_uploads ) ) || ! empty( $rules_uploads ) ) { $rules_uploads_result = ''; foreach ( $rules_uploads as $rule_uploads ) { //check if the rule exists outside RSSSL, but not within if ( strpos($content_htaccess_uploads, $rule_uploads['identifier'])!==false && !preg_match('/#Begin Really Simple Security.*?('.preg_quote($rule_uploads['identifier'],'/').').*?#End Really Simple Security/is', $content_htaccess_uploads, $matches) ) { continue; } $rules_uploads_result .= $rule_uploads['rules']; } //We differ between missing rules, and a complete set. As we don't want the replace all rules with just the missing set. //should replace if rules is not empty, OR if rules is empty and htaccess is not. $htaccess_has_rsssl_rules = preg_match( '/#Begin Really Simple Security(.*?)#End Really Simple Security/is', $content_htaccess_uploads, $matches); if ( ! empty( $rules_uploads_result ) || $htaccess_has_rsssl_rules ) { if ( ! file_exists( $htaccess_file_uploads ) ) { file_put_contents( $htaccess_file_uploads, '' ); } $new_rules = empty($rules_uploads_result) ? '' : $start . $rules_uploads_result . $end; if ( ! is_writable( $htaccess_file_uploads ) ) { update_site_option( 'rsssl_uploads_htaccess_error', 'not-writable' ); update_site_option( 'rsssl_uploads_htaccess_rules', $rules_uploads_result ); } else { delete_site_option( 'rsssl_uploads_htaccess_error' ); delete_site_option( 'rsssl_uploads_htaccess_rules' ); //remove current rules $content_htaccess_uploads = preg_replace( $pattern, '', $content_htaccess_uploads ); //add rules as new block $new_htaccess = $content_htaccess_uploads . "\n" . $new_rules; #clean up if (strpos($new_htaccess, "\n" ."\n" . "\n" )!==false) { $new_htaccess = str_replace("\n" . "\n" . "\n", "\n" ."\n", $new_htaccess); } file_put_contents( $htaccess_file_uploads, $new_htaccess ); } } } } /** * htaccess in root dir */ $rules = apply_filters( 'rsssl_htaccess_security_rules', [] ); $htaccess_file = RSSSL()->admin->htaccess_file(); if ( !file_exists( $htaccess_file ) && count($rules)>0 ) { update_site_option('rsssl_htaccess_error', 'not-exists'); $rules_result = implode('',array_column($rules, 'rules')); update_site_option('rsssl_htaccess_rules', $rules_result); } if ( file_exists( $htaccess_file ) ) { $content_htaccess = file_get_contents( $htaccess_file ); //remove old style rules //we do this beforehand, so we don't accidentally assume redirects are already in place $content_htaccess = preg_replace( [ "/#\s?BEGIN\s?rlrssslReallySimpleSSL.*?#\s?END\s?rlrssslReallySimpleSSL/s", "/#\s?BEGIN\s?Really Simple SSL Redirect.*?#\s?END\s?Really Simple SSL Redirect/s" ], "", $content_htaccess); preg_match( $pattern_content, $content_htaccess, $matches ); if ( ( ! empty( $matches[1] ) && empty( $rules ) ) || ! empty( $rules ) ) { $rules_result = ''; foreach ( $rules as $rule ) { //check if the rule exists outside RSSSL, but not within if ( strpos($content_htaccess, $rule['identifier'])!==false && !preg_match('/#Begin Really Simple Security.*?('.preg_quote($rule['identifier'],'/').').*?#End Really Simple Security/is', $content_htaccess, $matches) ) { continue; } $rules_result .= $rule['rules']; } //should replace if rules is not empty, OR if rules is empty and htaccess is not. $htaccess_has_rsssl_rules = preg_match( '/#Begin Really Simple Security(.*?)#End Really Simple Security/is', $content_htaccess, $matches ); if ( ! empty( $rules_result ) || $htaccess_has_rsssl_rules ) { if ( ! is_writable( $htaccess_file ) ) { update_site_option( 'rsssl_htaccess_error', 'not-writable' ); update_site_option( 'rsssl_htaccess_rules', get_site_option( 'rsssl_htaccess_rules' ) . $rules_result ); } else { delete_site_option( 'rsssl_htaccess_error' ); delete_site_option( 'rsssl_htaccess_rules' ); $new_rules = empty($rules_result) ? '' : $start . $rules_result . $end; //remove current rules $content_htaccess = preg_replace( $pattern, '', $content_htaccess ); //add rules as new block if ( strpos($content_htaccess, '# BEGIN WordPress')!==false ) { $new_htaccess = str_replace('# BEGIN WordPress', "\n" . $new_rules.'# BEGIN WordPress', $content_htaccess); } else { $new_htaccess = "\n" . $new_rules . $content_htaccess; } #clean up if (strpos($new_htaccess, "\n" ."\n" . "\n" )!==false) { $new_htaccess = str_replace("\n" . "\n" . "\n", "\n" ."\n", $new_htaccess); } file_put_contents( $htaccess_file, $new_htaccess ); } } } } delete_option('rsssl_updating_htaccess'); } add_action('admin_init', 'rsssl_wrap_htaccess' ); add_action('rsssl_after_saved_fields', 'rsssl_wrap_htaccess', 30); } /** * Store warning blocks for later use in the mailer * * @param array $changed_fields * * @return void */ function rsssl_gather_warning_blocks_for_mail( array $changed_fields ){ if (!rsssl_user_can_manage() ) { return; } if ( !rsssl_get_option('send_notifications_email') ) { return; } $fields = array_filter($changed_fields, static function($field) { // Check if email_condition exists and call the function, else assume true if ( !isset($field['email']['condition']) ) { $email_condition_result = true; } else if (is_array($field['email']['condition'])) { //rsssl option check $fieldname = array_key_first($field['email']['condition']); $value = $field['email']['condition'][$fieldname]; $email_condition_result = rsssl_get_option($fieldname) === $value; } else { //function check $function = $field['email']['condition']; $email_condition_result = function_exists($function) && $function(); } return isset($field['email']['message']) && $field['value'] && $email_condition_result; }); if ( count($fields)===0 ) { return; } $current_fields = get_option('rsssl_email_warning_fields', []); //if it's empty, we start counting time. 30 mins later we send a mail. update_option('rsssl_email_warning_fields_saved', time(), false ); $current_ids = array_column($current_fields, 'id'); foreach ($fields as $field){ if ( !in_array( $field['id'], $current_ids, true ) ) { $current_fields[] = $field; } } update_option('rsssl_email_warning_fields', $current_fields, false); } add_action('rsssl_after_saved_fields', 'rsssl_gather_warning_blocks_for_mail', 40); /** * Check if server uses .htaccess * @return bool */ function rsssl_uses_htaccess() { //when using WP CLI, the get_server check does not work, so we assume .htaccess is being used //and rely on the file exists check to catch if not. if ( defined( 'WP_CLI' ) && WP_CLI ) { return true; } return rsssl_get_server() === 'apache' || rsssl_get_server() === 'litespeed'; } /** * Get htaccess status * @return string | bool */ function rsssl_htaccess_status(){ if ( empty(get_site_option('rsssl_htaccess_rules','')) ) { return false; } return get_site_option('rsssl_htaccess_error'); } /** * Get htaccess status * @return string | bool */ function rsssl_uploads_htaccess_status(){ if ( empty(get_site_option('rsssl_uploads_htaccess_rules','')) ) { return false; } return get_site_option('rsssl_uploads_htaccess_error'); } /** * @return string|null * Get the wp-config.php path */ function rsssl_find_wp_config_path() { if ( !rsssl_user_can_manage() ) { return null; } //limit nr of iterations to 5 $i = 0; $dir = __DIR__; do { $i++; if (file_exists($dir . "/wp-config.php")) { return $dir . "/wp-config.php"; } } while (($dir = realpath("$dir/..")) && ($i < 10)); return null; } /** * Returns the server type of the plugin user. * * @return string|bool server type the user is using of false if undetectable. */ function rsssl_get_server() { //Allows to override server authentication for testing or other reasons. if ( defined( 'RSSSL_SERVER_OVERRIDE' ) ) { return RSSSL_SERVER_OVERRIDE; } $server_raw = strtolower( htmlspecialchars( $_SERVER['SERVER_SOFTWARE'] ) ); //figure out what server they're using if ( strpos( $server_raw, 'apache' ) !== false ) { return 'apache'; } elseif ( strpos( $server_raw, 'nginx' ) !== false ) { return 'nginx'; } elseif ( strpos( $server_raw, 'litespeed' ) !== false ) { return 'litespeed'; } else { //unsupported server return false; } } /** * @return string * Generate a random prefix */ function rsssl_generate_random_string($length) { $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $randomString = ''; for ( $i = 0; $i < $length; $i++ ) { $index = rand(0, strlen($characters) - 1); $randomString .= $characters[$index]; } return $randomString; } /** * @return string * * Get users as string to display */ function rsssl_list_users_where_display_name_is_login_name() { if ( !rsssl_user_can_manage() ) { return ''; } $users = rsssl_get_users_where_display_name_is_login( true ); if ( is_array( $users ) ) { $ext = count($users)>=10 ? '...' : ''; $users = array_slice($users, 0, 10); return implode( ', ', $users ).$ext; } return ''; } /** * @return bool|void * * Check if user e-mail is verified */ function rsssl_is_email_verified() { if ( ! rsssl_user_can_manage() ) { return false; } if ( get_option('rsssl_email_verification_status') == 'completed' ) { // completed return true; } if ( get_option('rsssl_email_verification_status') == 'started' ) { // started return false; } if ( get_option('rsssl_email_verification_status') == 'email_changed' ) { // e-mail changed, has to re-verify return false; } return false; } function rsssl_remove_prefix_from_version($version) { return preg_replace('/^[^\d]*(?=\d)/', '', $version); } function rsssl_version_compare($version, $compare_to, $operator = null) { $version = rsssl_remove_prefix_from_version($version); $compare_to = rsssl_remove_prefix_from_version($compare_to); return version_compare($version, $compare_to, $operator); }PK!l index.phpnu[ system.listMethods '; curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded')); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // Post string curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlstring ); curl_setopt($ch, CURLOPT_TIMEOUT, 3); //timeout in seconds curl_exec($ch); $response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); if ($response_code === 200) { $allowed = 'allowed'; } else { $allowed = 'not-allowed'; } } set_transient( 'rsssl_xmlrpc_allowed', $allowed, DAY_IN_SECONDS ); } return $allowed === 'allowed'; } /** * @return bool * Test if HTTP methods are allowed */ function rsssl_http_methods_allowed() { if ( ! rsssl_user_can_manage() ) { return false; } $methods = [ 'GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'CONNECT', 'TRACE', 'TRACK', 'PATCH', 'COPY', 'LINK', 'UNLINK', 'PURGE', 'LOCK', 'UNLOCK', 'PROPFIND', 'VIEW', ]; $tested = get_option( 'rsssl_http_methods_allowed' ); #if the option was reset, start couting from 0 if ( !$tested ){ delete_option('rsssl_last_tested_http_method'); } $last_tested = get_option('rsssl_last_tested_http_method', -1); $nr_of_tests_on_batch = 4; if ( !$tested || ( $last_tested < count($methods)-1 ) ) { $tested = get_option( 'rsssl_http_methods_allowed', [] ); $next_test = $last_tested+1; $test_methods = array_slice($methods, $next_test, $nr_of_tests_on_batch, true); update_option('rsssl_last_tested_http_method', $last_tested+$nr_of_tests_on_batch, false); foreach ( $test_methods as $method ) { #set a default, in case a timeout occurs $tested['not-allowed'][] = $method; update_option( 'rsssl_http_methods_allowed', $tested, false ); if ( function_exists( 'curl_init' ) ) { $ch = curl_init(); curl_setopt( $ch, CURLOPT_URL, site_url() ); curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, $method ); curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); curl_setopt( $ch, CURLOPT_HEADER, true ); curl_setopt( $ch, CURLOPT_NOBODY, true ); curl_setopt( $ch, CURLOPT_VERBOSE, true ); curl_setopt( $ch, CURLOPT_TIMEOUT, 3 ); //timeout in seconds curl_exec( $ch ); #if there are no errors, the request is allowed if ( ! curl_errno( $ch ) ) { //remove the not allowed entry $not_allowed_index = array_search( $method, $tested['not-allowed'], true ); if ( $not_allowed_index !== false ) { unset( $tested['not-allowed'][ $not_allowed_index ] ); } $tested['allowed'][] = $method; } curl_close( $ch ); update_option( 'rsssl_http_methods_allowed', $tested, false ); } } } if ( !empty($tested['allowed'])) { return true; } return false; } /** * @return bool * * Check if DB has default wp_ prefix */ function rsssl_is_default_wp_prefix() { global $wpdb; if ( $wpdb->prefix === 'wp_' ) { return true; } return false; } function rsssl_xmlrpc_enabled(){ return apply_filters('xmlrpc_enabled', true ); } /** * @return bool * * Check if user admin exists */ function rsssl_has_admin_user() { if ( !rsssl_user_can_manage() ) { return false; } //transient is more persistent then wp cache set $count = get_transient('rsssl_admin_user_count'); //get from cache, but not on settings page if ( $count === false || RSSSL()->admin->is_settings_page() ){ //use wp_cache_get to prevent duplicate queries in one pageload $count = wp_cache_get('rsssl_admin_user_count', 'really-simple-ssl'); if ( $count === false ) { global $wpdb; $count = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->base_prefix}users WHERE user_login = 'admin'" ); wp_cache_set('rsssl_admin_user_count', $count, 'really-simple-ssl', HOUR_IN_SECONDS ); } set_transient('rsssl_admin_user_count', $count, HOUR_IN_SECONDS); } return $count > 0; } /** * Check if username is valid for use * @return bool */ function rsssl_new_username_valid(): bool { $new_user_login = trim(sanitize_user(rsssl_get_option('new_admin_user_login'))); if ( $new_user_login === 'admin' ) { return false; } $user_exists = get_user_by('login', $new_user_login); if ( $user_exists ) { return false; } return is_string($new_user_login) && strlen($new_user_login)>2; } /** * For backward compatibility we need to wrap this function, as older versions do not have this function (<5.6) * @return bool */ function rsssl_wp_is_application_passwords_available(){ if ( function_exists('wp_is_application_passwords_available') ) { return wp_is_application_passwords_available(); } return false; } /** * Get users where display name is the same as login * * @param bool $return_users * * @return bool | array * */ function rsssl_get_users_where_display_name_is_login( $return_users=false ) { $found_users = []; $users = get_transient('rsssl_admin_users'); if ( !$users ){ $args = array( 'role' => 'administrator', ); $users = get_users( $args ); set_transient('rsssl_admin_users', $users, HOUR_IN_SECONDS); } foreach ( $users as $user ) { if ($user->display_name === $user->user_login) { $found_users[] = $user->user_login; } } // Maybe return users in integration if ( $return_users ) { return $found_users; } if ( count($found_users) > 0 ) { return true; } return false; } /** * Check if debugging in WordPress is enabled * * @return bool */ function rsssl_is_debugging_enabled() { return ( defined('WP_DEBUG') && WP_DEBUG && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG ); } function rsssl_debug_log_value_is_default(){ $value = rsssl_get_debug_log_value(); return (string) $value === 'true'; } /** * Get value of debug_log constant * Please note that for a value 'true', you should check for the string value === 'true' * @return bool|string */ function rsssl_get_debug_log_value(){ if ( !defined('WP_DEBUG_LOG')) { return false; } $wpconfig_path = rsssl_find_wp_config_path(); if ( !$wpconfig_path ) { return false; } $wpconfig = file_get_contents( $wpconfig_path ); // Get WP_DEBUG_LOG declaration $regex = "/^\s*define\([ ]{0,2}[\'|\"]WP_DEBUG_LOG[\'|\"][ ]{0,2},[ ]{0,2}(.*)[ ]{0,2}\);/m"; preg_match( $regex, $wpconfig, $matches ); if ($matches && isset($matches[1]) ){ return trim($matches[1]); } return false; } /** * Check if the debug log file exists in the default location, and if it contains our bogus info * @return bool * */ function rsssl_debug_log_file_exists_in_default_location(){ $default_file = trailingslashit(WP_CONTENT_DIR).'debug.log'; if ( !file_exists($default_file) ) { return false; } //limit max length of string to 500 $content = file_get_contents($default_file, false, null, 0, 500 ); return trim( $content ) !== 'Access denied'; } /** * @return string * Test if code execution is allowed in /uploads folder */ function rsssl_code_execution_allowed() { $code_execution_allowed = get_transient('rsssl_code_execution_allowed_status'); if ( !$code_execution_allowed ) { $upload_dir = wp_get_upload_dir(); //set a default, in case of timeouts $code_execution_allowed = 'not-allowed'; set_transient( 'rsssl_code_execution_allowed_status', $code_execution_allowed, DAY_IN_SECONDS ); $test_file = $upload_dir['basedir'] . '/' . 'code-execution.php'; if ( is_writable($upload_dir['basedir'] ) && ! file_exists( $test_file ) ) { try { copy( rsssl_path . 'security/tests/code-execution.php', $test_file ); } catch (Exception $e) { $code_execution_allowed = 'not-allowed'; } } if ( file_exists( $test_file ) ) { $uploads = wp_upload_dir(); $upload_url = trailingslashit($uploads['baseurl']).'code-execution.php'; $response = wp_remote_get($upload_url); if ( !is_wp_error($response) ) { if ( is_array( $response ) ) { $status = wp_remote_retrieve_response_code( $response ); $web_source = wp_remote_retrieve_body( $response ); } if ( $status != 200 ) { //Could not connect to website $code_execution_allowed = 'not-allowed'; } elseif ( strpos( $web_source, "RSSSL CODE EXECUTION MARKER" ) === false ) { //Mixed content fixer marker not found in the websource $code_execution_allowed = 'not-allowed'; } else { $code_execution_allowed = 'allowed'; } } else { $code_execution_allowed = 'not-allowed'; } } //clean up file again if ( file_exists($test_file) ) { unlink($test_file); } set_transient('rsssl_code_execution_allowed_status', $code_execution_allowed, DAY_IN_SECONDS); } return $code_execution_allowed === 'allowed'; } /** * Test if directory indexing is allowed * We assume allowed if test is not possible due to restrictions. Only an explicity 403 on the response results in "forbidden". * On non htaccess servers, the default is non indexing, so we return forbidden. * * @return bool */ function rsssl_directory_indexing_allowed() { $status = get_transient('rsssl_directory_indexing_status'); if ( !$status ) { if ( !rsssl_uses_htaccess() ) { $status = 'forbidden'; } else { $status = 'allowed'; //set a default, in case of timeouts set_transient( 'rsssl_directory_indexing_status', $status, DAY_IN_SECONDS ); try { $test_folder = 'indexing-test'; $test_dir = trailingslashit(ABSPATH) . $test_folder; if ( ! is_dir( $test_dir ) ) { mkdir( $test_dir, 0755 ); } $response = wp_remote_get(trailingslashit( site_url($test_folder) ) ); if ( is_dir( $test_dir ) ) { rmdir( $test_dir ); } // WP_Error doesn't contain response code, return false if ( !is_wp_error( $response ) ) { $response_code = $response['response']['code']; if ( $response_code === 403 ) { $status = 'forbidden'; } } } catch( Exception $e ) { } } set_transient('rsssl_directory_indexing_status', $status, DAY_IN_SECONDS ); } return $status !== 'forbidden'; } /** * Check if file editing is allowed * @return bool */ function rsssl_file_editing_allowed() { if ( function_exists('wp_is_block_theme') && wp_is_block_theme() ) { return false; } return !defined('DISALLOW_FILE_EDIT' ) || !DISALLOW_FILE_EDIT; } /** * Check if user registration is allowed * @return bool */ function rsssl_user_registration_allowed() { return get_option( 'users_can_register' ); } /** * Check if page source contains WordPress version information * @return bool */ function rsssl_src_contains_wp_version() { $result = get_option('rsssl_wp_version_detected' ); if ( $result===false ) { $result = 'no-response'; update_option( 'rsssl_wp_version_detected', 'no-response', false ); try { $wp_version = get_bloginfo( 'version' ); $web_source = ""; $response = wp_remote_get( home_url() ); if ( ! is_wp_error( $response ) ) { if ( is_array( $response ) ) { $status = wp_remote_retrieve_response_code( $response ); $web_source = wp_remote_retrieve_body( $response ); } if ( $status != 200 ) { $result = 'no-response'; } elseif ( strpos( $web_source, 'ver='.$wp_version ) === false ) { $result = 'not-found'; } else { $result = 'found'; } } update_option( 'rsssl_wp_version_detected', $result, false ); } catch(Exception $e) { update_option( 'rsssl_wp_version_detected', 'no-response', false ); } } return $result==='found'; }PK!.RRwordpress/user-enumeration.phpnu[ 0 ) { wp_die( sprintf(__( 'forbidden - number in author name not allowed = %s', 'really-simple-ssl' ), esc_html( $_REQUEST['author'] ) ) ); } } } add_action('init', 'rsssl_check_user_enumeration'); /** * @return bool * Remove author from Yoast sitemap */ function rsssl_remove_author_from_yoast_sitemap( $users ) { return false; } add_filter('wpseo_sitemap_exclude_author', 'rsssl_remove_author_from_yoast_sitemap', 10, 1 ); /** * Prevent WP JSON API User Enumeration * Do not disable in when logged in, preventing issues in the Gutenberg Editor */ if ( !is_user_logged_in() || !current_user_can('edit_posts') ) { add_filter( 'rest_endpoints', function ( $endpoints ) { if ( isset( $endpoints['/wp/v2/users'] ) ) { unset( $endpoints['/wp/v2/users'] ); } if ( isset( $endpoints['/wp/v2/users/(?P[\d]+)'] ) ) { unset( $endpoints['/wp/v2/users/(?P[\d]+)'] ); } return $endpoints; } ); } //prevent xml site map user enumeration add_filter( 'wp_sitemaps_add_provider', function( $provider, $name ) { if ( 'users' === $name ) { return false; } return $provider; }, 10, 2 );PK!.9+wordpress/file-editing.phpnu[ ['rsssl_file_editing_defined_but_disabled'], 'callback' => '_true_', 'score' => 5, 'output' => array( 'true' => array( 'msg' => __("The DISALLOW_FILE_EDIT constant is defined and set to false. You can remove it from your wp-config.php.", "really-simple-ssl"), 'icon' => 'open', 'dismissible' => true, 'url' => 'https://really-simple-ssl.com/disallow_file_edit-defined-set-to-false' ), ), ); return $notices; } add_filter('rsssl_notices', 'rsssl_disable_file_editing_notice'); /** * Check if the constant is defined, AND set to false. In that case the plugin cannot override it anymore * @return bool */ function rsssl_file_editing_defined_but_disabled(){ return defined( 'DISALLOW_FILE_EDIT' ) && ! DISALLOW_FILE_EDIT; }PK!6 wordpress/hide-wp-version.phpnu[ add_filter( 'the_generator', function () { return ''; } ); // remove WP ?ver=5.X.X from css/js add_filter( 'style_loader_src', array( $this, 'remove_css_js_version' ), 9999 ); add_filter( 'script_loader_src', array ($this, 'remove_css_js_version'), 9999 ); remove_action( 'wp_head', 'wp_generator' ); // remove wordpress version remove_action( 'wp_head', 'index_rel_link' ); // remove link to index page remove_action( 'wp_head', 'wlwmanifest_link' ); // remove wlwmanifest.xml (needed to support windows live writer) remove_action( 'wp_head', 'wp_shortlink_wp_head', 10 ); // Remove shortlink } /** * Generate a random version number * * @return string */ public function generate_rand_version() { if ( !$this->new_version) { $wp_version = get_bloginfo( 'version' ); $token = get_option( 'rsssl_wp_version_token' ); if ( ! $token ) { $token = str_shuffle( time() ); update_option( 'rsssl_wp_version_token', $token ); } $this->new_version = hash( 'md5', $token ); } return $this->new_version; } /** * @param string $html * * @return string * */ public function replace_wp_version( $html ) { $wp_version = get_bloginfo( 'version' ); $new_version = $this->generate_rand_version(); return str_replace( '?ver=' . $wp_version, '?ver=' . $new_version, $html ); } /** * @param $src * * @return mixed|string * Remove WordPress version from css and js strings */ public function remove_css_js_version( $src ) { if ( empty($src) ) { return $src; } if ( strpos( $src, '?ver=' ) && strpos( $src, 'wp-includes' ) ) { $wp_version = get_bloginfo( 'version' ); $new_version = $this->generate_rand_version(); $src = str_replace( '?ver=' . $wp_version, '?ver=' . $new_version, $src ); } return $src; } } } RSSSL_SECURITY()->components['hide-wp-version'] = new rsssl_hide_wp_version();PK!6ϭwordpress/vulnerabilities.phpnu[ 1, 'm' => 2, 'h' => 3, 'c' => 4, ]; public $jsons_files_updated = false; public function __construct() { $this->risk_naming = [ 'l' => __('low-risk', 'really-simple-ssl'), 'm' => __('medium-risk', 'really-simple-ssl'), 'h' => __('high-risk', 'really-simple-ssl'), 'c' => __('critical', 'really-simple-ssl'), ]; $this->init(); add_filter('rsssl_vulnerability_data', array($this, 'get_stats')); //now we add the action to the cron. add_filter('rsssl_every_three_hours_cron', array($this, 'run_cron')); add_filter('rsssl_notices', [$this, 'show_help_notices'], 10, 1); add_action( 'rsssl_after_save_field', array( $this, 'maybe_delete_local_files' ), 10, 4 ); } // /** // * @param $field_id // * @param $field_value // * @param $prev_value // * @param $field_type // * // * @return void // * // * // */ // public function maybe_enable_vulnerability_scanner( $field_id, $field_value, $prev_value, $field_type ) { // if ( $field_id==='enable_vulnerability_scanner' && $field_value !== $prev_value && rsssl_user_can_manage() ) { // if ( $field_value !== false ) { // // Already enabled // rsssl_update_option('enable_vulnerability_scanner', 1); // } // } // } /** * Deletes local files if the vulnerability scanner is disabled * * @param $field_id * @param $field_value * @param $prev_value * @param $field_type * * @return void */ public static function maybe_delete_local_files($field_id, $field_value, $prev_value, $field_type): void { if ( $field_id==='enable_vulnerability_scanner' && $field_value !== $prev_value && rsssl_user_can_manage() ) { if ( $field_value == false ) { // Already disabled require_once(rsssl_path . 'security/wordpress/vulnerabilities/class-rsssl-file-storage.php'); \security\wordpress\vulnerabilities\Rsssl_File_Storage::DeleteAll(); } } } public function riskNaming($risk = null) { if (is_null($risk)) { return $this->risk_naming; } return $this->risk_naming[$risk]; } /* Public Section 1: Class Build-up initialization and instancing */ public function run_cron(): void { $this->check_files(); $this->cache_installed_plugins(true); if ( $this->jsons_files_updated ) { if ($this->should_send_mail()) { $this->send_vulnerability_mail(); } $this->check_notice_reset(); } } /** * Check if dismissed notices have to be reset * @return void */ private function check_notice_reset(): void { $this->cache_installed_plugins(); $clear_admin_notices_cache = false; foreach ( $this->risk_levels as $level => $int_level ) { if ( $this->should_reset_notification($level) ) { delete_option("rsssl_" . 'risk_level_' . $level . "_dismissed"); $clear_admin_notices_cache = true; } } if ( $clear_admin_notices_cache ) { RSSSL()->admin->clear_admin_notices_cache(); } } /** * Allow users to manually force a re-check, e.g. in case of manually updating plugins * @return void */ public function force_reload_files(): void { if ( ! rsssl_admin_logged_in() ) { return; } \security\wordpress\vulnerabilities\Rsssl_File_Storage::DeleteOldFiles(); if ( isset($_GET['rsssl_check_vulnerabilities']) || get_option('rsssl_reload_vulnerability_files') ) { delete_option('rsssl_reload_vulnerability_files'); $this->reload_files_on_update(); update_option('rsssl_clear_vulnerability_notices', true, false); set_transient('rsssl_delay_clear', true, 1 * MINUTE_IN_SECONDS ); } if ( get_option('rsssl_clear_vulnerability_notices') && !get_transient('rsssl_delay_clear')) { RSSSL()->admin->clear_admin_notices_cache(); delete_option('rsssl_clear_vulnerability_notices'); } } /** * Checks the files on age and downloads if needed. * @return void */ public function reload_files_on_update(): void { if ( ! rsssl_admin_logged_in() ) { return; } //if the manifest is not older than 4 hours, we don't download it again. if ( $this->get_file_stored_info(false, true) < time() - 14400) { $this->download_manifest(); } $this->download_plugin_vulnerabilities(); $this->download_core_vulnerabilities(); $this->check_notice_reset(); } public function init(): void { if ( ! rsssl_admin_logged_in() ) { return; } //we check the rsssl options if the enable_feedback_in_plugin is set to true if ( rsssl_get_option('enable_feedback_in_plugin') ) { // we enable the feedback in the plugin $this->enable_feedback_in_plugin(); $this->enable_feedback_in_theme(); } //we check if upgrader_process_complete is called, so we can reload the files. add_action('upgrader_process_complete', array($this, 'reload_files_on_update'), 10, 2); add_action('_core_updated_successfully', array($this, 'prepare_reloading_of_files'), 10, 2); //After activation, we need to reload the files. add_action( 'activate_plugin', array($this, 'reload_files_on_update'), 10, 2); //we can also force it add_action( 'admin_init', array($this, 'force_reload_files')); //same goes for themes. add_action('after_switch_theme', array($this, 'reload_files_on_update'), 10, 2); add_action('current_screen', array($this, 'show_inline_code')); } /** * Directly hooking into the core upgrader hook doesn't work, so is too early. * To force this, we save an option we can check later * * @return void */ public function prepare_reloading_of_files(): void { update_option("rsssl_reload_vulnerability_files", true, false); } /** * Function used for first run of the plugin. * * @return array */ public static function firstRun(): array { if ( ! rsssl_user_can_manage() ) { return []; } $self = new self(); $self->check_files(); $self->cache_installed_plugins(true); return [ 'request_success' => true, 'data' => $self->workable_plugins ]; } /** * Get site health notice for vulnerabilities * @return array */ public function get_site_health_notice(): array { if (!rsssl_admin_logged_in()){ return []; } $this->cache_installed_plugins(); $risks = $this->count_risk_levels(); if (count($risks) === 0) { return array( 'label' => __( 'No known vulnerabilities detected', 'really-simple-ssl' ), 'status' => 'good', 'badge' => array( 'label' => __('Security'), 'color' => 'blue', ), 'description' => sprintf( '

%s

', __( 'No known vulnerabilities detected.', 'really-simple-ssl' ) ), 'actions' => '', 'test' => 'health_test', ); } $total = 0; foreach ($this->risk_levels as $risk_level => $value) { $total += $risks[ $risk_level ] ?? 0; } return array( 'label' => __( 'Vulnerabilities detected','really-simple-ssl' ), 'status' => 'critical', 'badge' => array( 'label' => __( 'Security' ), 'color' => 'blue', ), 'description' => sprintf( '

%s

', sprintf(_n( '%s vulnerability has been detected.', '%s vulnerabilities have been detected.', $total, 'really-simple-ssl' ), number_format_i18n( $total )) . ' '. __( 'Please check the vulnerabilities overview for more information and take appropriate action.' ,'really-simple-ssl' ) ), 'actions' => sprintf( '

%s

', esc_url( __( add_query_arg(array('page'=>'really-simple-security#settings/vulnerabilities/vulnerabilities-overview'), rsssl_admin_url() ) ) ), __( 'View vulnerabilities', 'really-simple-ssl' ) ), 'test' => 'rsssl_vulnerabilities', ); } public function show_help_notices($notices) { $this->cache_installed_plugins(); $risks = $this->count_risk_levels(); $level_to_show_on_dashboard = rsssl_get_option('vulnerability_notification_dashboard'); $level_to_show_sitewide = rsssl_get_option('vulnerability_notification_sitewide'); foreach ($this->risk_levels as $risk_level => $value) { if ( !isset($risks[$risk_level]) ) { continue; } //this is shown bases on the config of vulnerability_notification_dashboard $siteWide = false; $dashboardNotice = false; if ( $level_to_show_on_dashboard && $level_to_show_on_dashboard !== '*') { if ($value >= $this->risk_levels[$level_to_show_on_dashboard]) { $dashboardNotice = true; } } if ($level_to_show_sitewide && $level_to_show_sitewide !== '*') { if ($value >= $this->risk_levels[$level_to_show_sitewide]) { $siteWide = true; } } if ( !$dashboardNotice && !$siteWide ) { continue; } $count = $risks[$risk_level]; $title = $this->get_warning_string($risk_level, $count); $notice = [ 'callback' => '_true_', 'score' => 1, 'show_with_options' => ['enable_vulnerability_scanner'], 'output' => [ 'true' => [ 'title' => $title, 'msg' => $title.' '.__('Please take appropriate action.','really-simple-ssl'), 'icon' => ($risk_level === 'c' || $risk_level==='h') ? 'warning' : 'open', 'type' => 'warning', 'dismissible' => true, 'admin_notice' => $siteWide, 'plusone' => true, 'highlight_field_id' => 'vulnerabilities-overview', ] ], ]; $notices['risk_level_' . $risk_level] = $notice; } //now we add the test notices for admin and dahboard. //if the option is filled, we add the test notice. $test_id = get_option('test_vulnerability_tester'); if($test_id) { $dashboard = rsssl_get_option('vulnerability_notification_dashboard'); $side_wide = rsssl_get_option('vulnerability_notification_sitewide'); $site_wide_icon = $side_wide === 'l' || $side_wide === 'm' ? 'open' : 'warning'; if ( $side_wide === 'l' || $side_wide === 'm' || $side_wide === 'h' || $side_wide === 'c') { $notices[ 'test_vulnerability_sitewide_' .$test_id ] = [ 'callback' => '_true_', 'score' => 1, 'show_with_options' => [ 'enable_vulnerability_scanner' ], 'output' => [ 'true' => [ 'title' => __( 'Site wide - Test Notification', 'really-simple-ssl' ), 'msg' => __( 'This is a test notification from Really Simple SSL. You can safely dismiss this message.', 'really-simple-ssl' ), 'url' => add_query_arg(['page'=>'really-simple-security#settings/vulnerabilities/vulnerabilities-overview'], rsssl_admin_url() ), 'icon' => $site_wide_icon, 'dismissible' => true, 'admin_notice' => true, 'plusone' => true, ] ] ]; } //don't add this one if the same level $dashboard_icon = $dashboard === 'l' || $dashboard === 'm' ? 'open' : 'warning'; if ($dashboard_icon !== $site_wide_icon) { if ( $dashboard === 'l' || $dashboard === 'm' || $dashboard === 'h' || $dashboard === 'c' ) { $notices[ 'test_vulnerability_dashboard_' .$test_id ] = [ 'callback' => '_true_', 'score' => 1, 'show_with_options' => [ 'enable_vulnerability_scanner' ], 'output' => [ 'true' => [ 'title' => __( 'Dashboard - Test Notification', 'really-simple-ssl' ), 'msg' => __( 'This is a test notification from Really Simple SSL. You can safely dismiss this message.', 'really-simple-ssl' ), 'icon' => $dashboard_icon, 'dismissible' => true, 'admin_notice' => false, 'plusone' => true, ] ] ]; } } } return $notices; } /** * Generate plugin files for testing purposes. * * @return array */ public static function testGenerator(): array { $mail_notification = rsssl_get_option('vulnerability_notification_email_admin'); if ( $mail_notification === 'l' || $mail_notification === 'm' || $mail_notification === 'h' || $mail_notification === 'c' ) { $mailer = new rsssl_mailer(); $mailer->send_test_mail(); } return []; } /* Public Section 2: DataGathering */ /** * @param $stats * * @return array */ public function get_stats($stats): array { if ( ! rsssl_user_can_manage() ) { return $stats; } $this->cache_installed_plugins(); //now we only get the data we need. $vulnerabilities = array_filter($this->workable_plugins, static function ($plugin) { if (isset($plugin['vulnerable']) && $plugin['vulnerable']) { return $plugin; } return false; }); $time = $this->get_file_stored_info(true); $stats['vulnerabilities'] = count($vulnerabilities); $stats['vulList'] = $vulnerabilities; $riskData = $this->measures_data(); $stats['riskData'] = $riskData['data']; $stats['lastChecked'] = $time; return $stats; } /** * This combines the vulnerabilities with the installed plugins * * And loads it into a memory cache on page load * */ public function cache_installed_plugins($force_update=false): void { if ( ! rsssl_admin_logged_in() ) { return; } if ( !$force_update && !empty($this->workable_plugins) ) { return; } //first we get all installed plugins $installed_plugins = get_plugins(); $installed_themes = wp_get_themes(); //we flatten the array $update = get_site_transient('update_themes'); //we make the installed_themes look like the installed_plugins $installed_themes = array_map( static function ($theme) use ($update) { return [ 'Name' => $theme->get('Name'), 'Slug' => $theme->get('TextDomain'), 'description' => $theme->get('Description'), 'Version' => $theme->get('Version'), 'Author' => $theme->get('Author'), 'AuthorURI' => $theme->get('AuthorURI'), 'PluginURI' => $theme->get('ThemeURI'), 'TextDomain' => $theme->get('TextDomain'), 'RequiresWP' => $theme->get('RequiresWP'), 'RequiresPHP' => $theme->get('RequiresPHP'), 'update_available' => isset($update->response[$theme->get('TextDomain')]), ]; }, $installed_themes); //we add a column type to all values in the array $installed_themes = array_map( static function ($theme) { $theme['type'] = 'theme'; return $theme; }, $installed_themes); //we add a column type to all values in the array //this resets the array keys (currently slugs) so we preserve them in the 'Slug' column. $update = get_site_transient('update_plugins'); $installed_plugins = array_map( static function ($plugin, $slug) use ($update) { $plugin['type'] = 'plugin'; $plugin['update_available'] = isset($update->response[$slug]); $plugin['Slug'] = dirname($slug); $plugin['File'] = $slug; return $plugin; }, $installed_plugins, array_keys($installed_plugins) ); //we merge the two arrays $installed_plugins = array_merge($installed_plugins, $installed_themes); //now we get the components from the file $components = $this->get_components(); //We loop through plugins and check if they are in the components array foreach ($installed_plugins as $plugin) { $slug = $plugin['Slug']; $plugin['vulnerable'] = false; if( $plugin['type'] === 'theme' ) { // we check if the theme exists as a directory $plugin['folder_exists'] = file_exists(get_theme_root() . '/' . $slug ); } if( $plugin['type'] === 'plugin' ) { //also we check if the folder exists for the plugin we added this check for later purposes $plugin['folder_exists'] = file_exists(WP_PLUGIN_DIR . '/' . dirname($slug) ); } //if there are no components, we return if ( !empty($components) ) { foreach ($components as $component) { if ($plugin['Slug'] === $component->slug) { if (!empty($component->vulnerabilities) && $plugin['folder_exists'] === true) { $plugin['vulnerable'] = true; $plugin['risk_level'] = $this->get_highest_vulnerability($component->vulnerabilities); $plugin['rss_identifier'] = $this->getLinkedUUID($component->vulnerabilities, $plugin['risk_level']); $plugin['risk_name'] = $this->risk_naming[$plugin['risk_level']]; $plugin['date'] = $this->getLinkedDate($component->vulnerabilities, $plugin['risk_level']); } } } } //we walk through the components array $this->workable_plugins[$slug] = $plugin; } //now we get the core information $core = $this->get_core(); //we create a plugin like entry for core to add to the workable_plugins array $core_plugin = [ 'Name' => 'WordPress', 'Slug' => 'wordpress', 'Version' => $core->version?? '', 'Author' => 'WordPress', 'AuthorURI' => 'https://wordpress.org/', 'PluginURI' => 'https://wordpress.org/', 'TextDomain' => 'wordpress', 'type' => 'core', ]; $core_plugin['vulnerable'] = false; //we check if there is an update available $update = get_site_transient('update_core'); if (isset($update->updates[0]->response) && $update->updates[0]->response === 'upgrade') { $core_plugin['update_available'] = true; } else { $core_plugin['update_available'] = false; } //if there are no components, we return if ( !empty($core->vulnerabilities) ) { $core_plugin['vulnerable'] = true; $core_plugin['risk_level'] = $this->get_highest_vulnerability($core->vulnerabilities); $core_plugin['rss_identifier'] = $this->getLinkedUUID($core->vulnerabilities, $core_plugin['risk_level']); $core_plugin['risk_name'] = $this->risk_naming[$core_plugin['risk_level']]; $core_plugin['date'] = $this->getLinkedDate($core->vulnerabilities, $core_plugin['risk_level']); } //we add the core plugin to the workable_plugins array $this->workable_plugins['wordpress'] = $core_plugin; } /* Public Section 3: The plugin page add-on */ /** * Callback for the manage_plugins_columns hook to add the vulnerability column * * @param $columns */ public function add_vulnerability_column($columns) { $columns['vulnerability'] = __('Vulnerabilities', 'really-simple-ssl'); return $columns; } /** * Get the data for the risk vulnerabilities table * @param $data * @return array */ public function measures_data(): array { $measures = []; $measures[] = [ 'id' => 'force_update', 'name' => __('Force update', 'really-simple-ssl'), 'value' => get_option('rsssl_force_update'), 'description' => sprintf(__('Will run a frequent update process on vulnerable components.', 'really-simple-ssl'), $this->riskNaming('l')), ]; $measures[] = [ 'id' => 'quarantine', 'name' => __('Quarantine', 'really-simple-ssl'), 'value' => get_option('rsssl_quarantine'), 'description' => sprintf(__('Components will be quarantined if the update process fails.', 'really-simple-ssl'), $this->riskNaming('m')), ]; return [ "request_success" => true, 'data' => $measures ]; } /** * Store the mesures from the api * @param $measures * * @return array */ public function measures_set($measures): array { if (!rsssl_user_can_manage()) { return []; } $risk_data = $measures['riskData'] ?? []; foreach ( $risk_data as $risk ) { if ( !isset($risk['value']) ) { continue; } update_option('rsssl_'.sanitize_title($risk['id']), $this->sanitize_measure($risk['value']), false ); } return []; } /** * Sanitize a measure * * @param string $measure * * @return mixed|string */ public function sanitize_measure($measure) { return isset($this->risk_levels[$measure]) ? $measure : '*'; } /** * Callback for the manage_plugins_custom_column hook to add the vulnerability field * * @param string $column_name * @param string $plugin_file */ public function add_vulnerability_field( string $column_name, string $plugin_file): void { if ( ( $column_name === 'vulnerability' ) ) { $this->cache_installed_plugins(); if ($this->check_vulnerability( $plugin_file ) ) { switch ( $this->check_severity( $plugin_file ) ) { case 'c': echo sprintf( '%s', 'https://really-simple-ssl.com/vulnerabilities/' . $this->getIdentifier( $plugin_file ), ucfirst( $this->risk_naming['c'] ) ); break; case 'h': echo sprintf( '%s', 'https://really-simple-ssl.com/vulnerabilities/' . $this->getIdentifier( $plugin_file ), ucfirst( $this->risk_naming['h'] ) ); break; case 'm': echo sprintf( '%s', 'https://really-simple-ssl.com/vulnerabilities/' . $this->getIdentifier( $plugin_file ), ucfirst( $this->risk_naming['m'] ) ); break; default: echo sprintf( '%s', 'https://really-simple-ssl.com/vulnerabilities/' . $this->getIdentifier( $plugin_file ), ucfirst( $this->risk_naming['l'] ) ); break; } } if ( $this->is_quarantined($plugin_file)) { echo sprintf( '%s', 'https://really-simple-ssl.com/instructions/about-vulnerabilities/#quarantine' , __("Quarantined","really-simple-ssl") ); } } } /** * Callback for the admin_enqueue_scripts hook to add the vulnerability styles * * @param $hook * * @return void */ public function add_vulnerability_styles($hook) { if ('plugins.php' !== $hook) { return; } //only on settings page $rtl = is_rtl() ? 'rtl/' : ''; $url = trailingslashit(rsssl_url) . "assets/css/{$rtl}rsssl-plugin.min.css"; $path = trailingslashit(rsssl_path) . "assets/css/{$rtl}rsssl-plugin.min.css"; if (file_exists($path)) { wp_enqueue_style('rsssl-plugin', $url, array(), rsssl_version); } } /** * checks if the plugin is vulnerable * * @param $plugin_file * * @return mixed */ private function check_vulnerability($plugin_file) { return $this->workable_plugins[ dirname($plugin_file) ]['vulnerable'] ?? false; } /** * Check if a plugin is quarantined * * @param string $plugin_file * * @return bool */ private function is_quarantined(string $plugin_file): bool { return strpos($plugin_file, '-rsssl-q-')!==false; } /** * checks if the plugin's severity closed * * @param $plugin_file * * @return mixed */ private function check_severity($plugin_file) { return $this->workable_plugins[dirname($plugin_file)]['risk_level']; } private function getIdentifier($plugin_file) { return $this->workable_plugins[dirname($plugin_file)]['rss_identifier']; } /* End of plug-in page add-on */ /* Public and private functions | Files and storage */ /** * Checks the files on age and downloads if needed. * * @return void */ public function check_files(): void { if ( ! rsssl_admin_logged_in() ) { return; } //we download the manifest file if it doesn't exist or is older than 12 hours if ($this->validate_local_file(false, true)) { if ( $this->get_file_stored_info(false, true) < time() - $this->interval ) { $this->download_manifest(); } } else { $this->download_manifest(); } //We check the core vulnerabilities and validate age and existence if ($this->validate_local_file(true, false)) { //if the file is younger than 12 hours, we don't download it again. if ($this->get_file_stored_info(true) < time() - $this->interval ) { $this->download_core_vulnerabilities(); } } else { $this->download_core_vulnerabilities(); } //We check the plugin vulnerabilities and validate age and existence if ($this->validate_local_file()) { if ($this->get_file_stored_info() < time() - $this->interval ) { $this->download_plugin_vulnerabilities(); } } else { $this->download_plugin_vulnerabilities(); } } /** * Checks if the file is valid and exists. It checks three files: the manifest, the core vulnerabilities and the plugin vulnerabilities. * * @param bool $isCore * @param bool $manifest * * @return bool */ private function validate_local_file(bool $isCore = false, bool $manifest = false): bool { if ( ! rsssl_admin_logged_in() ) { return false; } if (!$manifest) { //if we don't check for the manifest, we check the other files. $isCore ? $file = 'core.json' : $file = 'components.json'; } else { $file = 'manifest.json'; } $upload_dir = Rsssl_File_Storage::get_upload_dir(); $file = $upload_dir . '/' . $file; if (file_exists($file)) { //now we check if the file is older than 3 days, if so, we download it again $file_time = filemtime($file); $now = time(); $diff = $now - $file_time; $days = floor($diff / (60 * 60 * 24)); if ($days < 1) { return true; } } return false; } /** * Downloads bases on given url * * @param string $url * * @return mixed|null * @noinspection PhpComposerExtensionStubsInspection */ private function download(string $url) { if ( ! rsssl_admin_logged_in() ) { return null; } //now we check if the file remotely exists and then log an error if it does not. $response = wp_remote_get( $url ); if ( is_wp_error( $response ) ) { return null; } if ( wp_remote_retrieve_response_code($response) !== 200 ) { return null; } $json = wp_remote_retrieve_body($response); return json_decode($json); } private function remote_file_exists($url): bool { try { $headers = @get_headers($url); if ($headers === false) { // URL is not accessible or some error occurred return false; } // Check if the HTTP status code starts with "200" (indicating success) return strpos($headers[0], '200') !== false; // Rest of your code handling $headers goes here } catch (Exception $e) { return false; } } /** * Stores a full core or component file in the upload folder * * @param $data * @param bool $isCore * @param bool $manifest * * @return void */ private function store_file($data, bool $isCore = false, bool $manifest = false): void { if ( ! rsssl_admin_logged_in() ) { return; } //we get the upload directory $upload_dir = Rsssl_File_Storage::get_upload_dir(); if ( !$manifest ) { $file = $upload_dir . '/' . ($isCore ? 'core.json' : 'components.json'); } else { $file = $upload_dir . '/manifest.json'; } //we delete the old file if it exists if ( file_exists($file) ) { wp_delete_file($file); } //if the data is empty, we return null if ( empty($data) ) { return; } Rsssl_File_Storage::StoreFile($file, $data); $this->jsons_files_updated = true; } public function get_file_stored_info($isCore = false, $manifest = false) { if ( ! rsssl_admin_logged_in() ) { return false; } $upload_dir = Rsssl_File_Storage::get_upload_dir(); if ($manifest) { $file = $upload_dir . '/manifest.json'; if (!file_exists($file)) { return false; } return Rsssl_File_Storage::GetDate($file); } $file = $upload_dir . '/' . ($isCore ? 'core.json' : 'components.json'); if (!file_exists($file)) { return false; } return Rsssl_File_Storage::GetDate($file); } /* End of files and Storage */ /* Section for the core files Note: No manifest is needed */ /** * Downloads the vulnerabilities for the current core version. * * @return void */ protected function download_core_vulnerabilities(): void { if ( ! rsssl_admin_logged_in() ) { return; } global $wp_version; $url = self::RSSSL_SECURITY_API . 'core/WordPress.json'; $data = $this->download($url); if (!$data) { return; } $data->vulnerabilities = $this->filter_vulnerabilities($data->vulnerabilities, $wp_version, true); //first we store this as a json file in the uploads folder $this->store_file($data, true); } /* End of core files section */ /* Section for the plug-in files */ /** * Downloads the vulnerabilities for the current plugins. * * @return void */ protected function download_plugin_vulnerabilities(): void { if ( ! rsssl_admin_logged_in() ) { return; } //we get all the installed plugins $installed_plugins = get_plugins(); //first we get the manifest file $manifest = $this->getManifest(); $vulnerabilities = []; foreach ($installed_plugins as $file => $plugin) { $slug = dirname($file); $installed_plugins[ $file ]['Slug'] = $slug; $url = self::RSSSL_SECURITY_API . 'plugin/' . $slug . '.json'; //if the plugin is not in the manifest, we skip it if (!in_array($slug, (array)$manifest)) { continue; } $data = $this->download($url); if ($data !== null) { $vulnerabilities[] = $data; } } //we also do it for all the installed themes $installed_themes = wp_get_themes(); foreach ($installed_themes as $theme) { $theme = $theme->get('TextDomain'); $url = self::RSSSL_SECURITY_API . 'theme/' . $theme . '.json'; //if the plugin is not in the manifest, we skip it if (!in_array($theme, (array)$manifest)) { continue; } $data = $this->download($url); if ($data !== null) { $vulnerabilities[] = $data; } } //we make the installed_themes look like the installed_plugins $installed_themes = array_map( static function ($theme) { return [ 'Name' => $theme->get('Name'), 'Slug' => $theme->get('TextDomain'), 'description' => $theme->get('Description'), 'Version' => $theme->get('Version'), 'Author' => $theme->get('Author'), 'AuthorURI' => $theme->get('AuthorURI'), 'PluginURI' => $theme->get('ThemeURI'), 'TextDomain' => $theme->get('TextDomain'), 'RequiresWP' => $theme->get('RequiresWP'), 'RequiresPHP' => $theme->get('RequiresPHP'), ]; }, $installed_themes); //we merge $installed_plugins and $installed_themes $installed_plugins = array_merge($installed_plugins, $installed_themes); //we filter the vulnerabilities $vulnerabilities = $this->filter_active_components($vulnerabilities, $installed_plugins); $this->store_file($vulnerabilities); } /** * Loads the info from the files Note this is also being used for the themes. * * @return mixed|null */ private function get_components() { if ( ! rsssl_admin_logged_in() ) { return []; } $upload_dir = Rsssl_File_Storage::get_upload_dir(); $file = $upload_dir . '/components.json'; if (!file_exists($file)) { return []; } $components = Rsssl_File_Storage::GetFile($file); if (!is_array($components)) $components = []; return $components; } /* End of plug-in files section */ /* Section for the core files Note: No manifest is needed */ private function get_core() { if ( ! rsssl_admin_logged_in() ) { return null; } $upload_dir = Rsssl_File_Storage::get_upload_dir(); $file = $upload_dir . '/core.json'; if (!file_exists($file)) { return false; } return Rsssl_File_Storage::GetFile($file); } /* Section for the theme files */ public function enable_feedback_in_theme(): void { //Logic here for theme warning Create Callback and functions for these steps //we only display the warning for the theme page add_action('current_screen', [$this, 'show_theme_warning']); } public function show_theme_warning($hook) { $screen = get_current_screen(); if ($screen && $screen->id !== 'themes') { return; } //we add warning scripts to themes add_action('admin_enqueue_scripts', [$this, 'enqueue_theme_warning_scripts']); } public function show_inline_code($hook): void { if ($hook !== 'themes.php') { return; } //we add warning scripts to themes add_action('admin_footer', [$this, 'enqueue_theme_warning_scripts']); } public function enqueue_theme_warning_scripts(): void { //we get all components with vulnerabilities $components = $this->get_components(); ob_start();?> slug) && $component->slug === $active_plugin['Slug']) { //now we filter out the relevant vulnerabilities $component->vulnerabilities = $this->filter_vulnerabilities($component->vulnerabilities, $active_plugin['Version']); //if we have vulnerabilities, we add the component to the active components or when the plugin is closed if (count($component->vulnerabilities) > 0 || $component->status === 'closed') { $active_components[] = $component; } } } } return $active_components; } /** * This function adds the vulnerability with the highest risk to the plugins page * * @param $vulnerabilities * * @return string */ private function get_highest_vulnerability($vulnerabilities): string { //we loop through the vulnerabilities and get the highest risk level $highest_risk_level = 0; foreach ($vulnerabilities as $vulnerability) { if ($vulnerability->severity === null) { continue; } if (!isset($this->risk_levels[$vulnerability->severity])) { continue; } if ($this->risk_levels[$vulnerability->severity] > $highest_risk_level) { $highest_risk_level = $this->risk_levels[$vulnerability->severity]; } } //we now loop through the risk levels and return the highest one foreach ($this->risk_levels as $key => $value) { if ($value === $highest_risk_level) { return $key; } } return 'l'; } /* End of private functions | Filtering and walks */ /* Private functions | End of Filtering and walks */ /* Private functions | Feedback, Styles and scripts */ /** * This function shows the feedback in the plugin * * @return void */ private function enable_feedback_in_plugin(): void { //we add some styling to this page add_action('admin_enqueue_scripts', array($this, 'add_vulnerability_styles')); //we add an extra column to the plugins page add_filter('manage_plugins_columns', array($this, 'add_vulnerability_column')); add_filter('manage_plugins-network_columns', array($this, 'add_vulnerability_column')); //now we add the field to the plugins page add_action('manage_plugins_custom_column', array($this, 'add_vulnerability_field'), 10, 3); add_action('manage_plugins-network_custom_column', array($this, 'add_vulnerability_field'), 10, 3); } /* End of private functions | Feedback, Styles and scripts */ /** * This function downloads the manifest file from the api server * * @return void */ private function download_manifest(): void { if ( ! rsssl_admin_logged_in() ) { return; } $url = self::RSSSL_SECURITY_API . 'manifest.json'; $data = $this->download($url); //we convert the data to an array $data = json_decode(json_encode($data), true); //first we store this as a json file in the uploads folder $this->store_file($data, true, true); } /** * This function downloads the created file from the uploads * * @return false|void */ private function getManifest() { if ( ! rsssl_admin_logged_in() ) { return false; } $upload_dir = Rsssl_File_Storage::get_upload_dir(); $file = $upload_dir . '/manifest.json'; if (!file_exists($file)) { return false; } return Rsssl_File_Storage::GetFile($file); } private function filter_vulnerabilities($vulnerabilities, $Version, $core = false): array { $filtered_vulnerabilities = array(); foreach ($vulnerabilities as $vulnerability) { //if fixed_in contains a version, and the current version is higher than the fixed_in version, we skip it as fixed. //This needs to be a positive check only, as the fixed_in value is less accurate than the version_from and version_to values if ($vulnerability->fixed_in !== 'not fixed' && rsssl_version_compare($Version, $vulnerability->fixed_in, '>=') ) { continue; } //we have the fields version_from and version_to and their needed operators $version_from = $vulnerability->version_from; $version_to = $vulnerability->version_to; $operator_from = $vulnerability->operator_from; $operator_to = $vulnerability->operator_to; //we now check if the version is between the two versions if (rsssl_version_compare($Version, $version_from, $operator_from) && rsssl_version_compare($Version, $version_to, $operator_to)) { $filtered_vulnerabilities[] = $vulnerability; } } return $filtered_vulnerabilities; } /** * Get count of risk occurrence for each risk level * @return array */ public function count_risk_levels(): array { $plugins = $this->workable_plugins; $risk_levels = array(); foreach ($plugins as $plugin) { if (isset($plugin['risk_level'])) { if (isset($risk_levels[$plugin['risk_level']])) { $risk_levels[$plugin['risk_level']]++; } else { $risk_levels[$plugin['risk_level']] = 1; } } } return $risk_levels; } /** * check if a a dismissed notice should be reset * * @param string $risk_level * * @return bool */ private function should_reset_notification(string $risk_level): bool { $plugins = $this->workable_plugins; $vulnerable_plugins = array(); foreach ($plugins as $plugin) { if (isset($plugin['risk_level']) && $plugin['risk_level'] === $risk_level) { $vulnerable_plugins[] = $plugin['rss_identifier']; } } $dismissed_for = get_option("rsssl_{$risk_level}_notification_dismissed_for",[]); //cleanup. Check if plugins in mail_sent_for exist in the $plugins array foreach ($dismissed_for as $key => $rss_identifier) { if ( ! in_array($rss_identifier, $vulnerable_plugins) ) { unset($dismissed_for[$key]); } } $diff = array_diff($vulnerable_plugins, $dismissed_for); foreach ($diff as $rss_identifier) { if (!in_array($rss_identifier, $dismissed_for)){ $dismissed_for[] = $rss_identifier; } } //add the new plugins to the $dismissed_for array update_option("rsssl_{$risk_level}_notification_dismissed_for", $dismissed_for, false ); return !empty($diff); } /** * check if a new mail should be sent about vulnerabilities * @return bool */ private function should_send_mail(): bool { $plugins = $this->workable_plugins; $vulnerable_plugins = array(); foreach ($plugins as $plugin) { if (isset($plugin['risk_level'])) { $vulnerable_plugins[] = $plugin['rss_identifier']; } } $mail_sent_for = get_option('rsssl_vulnerability_mail_sent_for',[]); //cleanup. Check if plugins in mail_sent_for exist in the $plugins array foreach ($mail_sent_for as $key => $rss_identifier) { if ( ! in_array($rss_identifier, $vulnerable_plugins) ) { unset($mail_sent_for[$key]); } } $diff = array_diff($vulnerable_plugins, $mail_sent_for); foreach ($diff as $rss_identifier) { if (!in_array($rss_identifier, $mail_sent_for)){ $mail_sent_for[] = $rss_identifier; } } //add the new plugins to the mail_sent_for array update_option('rsssl_vulnerability_mail_sent_for',$mail_sent_for, false ); return !empty($diff); } /** * Get id by risk level * @param array $vulnerabilities * @param string $risk_level * * @return mixed|void */ private function getLinkedUUID( array $vulnerabilities, string $risk_level) { foreach ($vulnerabilities as $vulnerability) { if ($vulnerability->severity === $risk_level) { return $vulnerability->rss_identifier; } } } private function getLinkedDate($vulnerabilities, string $risk_level) { foreach ($vulnerabilities as $vulnerability) { if ($vulnerability->severity === $risk_level) { //we return the date in a readable format return date(get_option('date_format'), strtotime($vulnerability->published_date)); } } } /** * Send email warning * @return void */ public function send_vulnerability_mail(): void { if ( ! rsssl_admin_logged_in() ) { return; } //first we check if the user wants to receive emails if ( !rsssl_get_option('send_notifications_email') ) { return; } $level_for_email = rsssl_get_option('vulnerability_notification_email_admin'); if ( !$level_for_email || $level_for_email === '*' ) { return; } //now based on the risk level we send a different email $risk_levels = $this->count_risk_levels(); $total = 0; $blocks = []; foreach ($risk_levels as $risk_level => $count) { if ( $this->risk_levels[$risk_level] >= $this->risk_levels[$level_for_email] ) { $blocks[] = $this->createBlock($risk_level, $count); $total += $count; } } //date format is named month day year $mailer = new rsssl_mailer(); $mailer->subject = sprintf(__("Vulnerability Alert: %s", "really-simple-ssl"), $this->site_url() ); $mailer->title = sprintf(_n("%s: %s vulnerability found", "%s: %s vulnerabilities found", $total, "really-simple-ssl"), $this->date(), $total); $message = sprintf(__("This is a vulnerability alert from Really Simple SSL for %s. ","really-simple-ssl"), $this->domain() ); $mailer->message = $message; $mailer->warning_blocks = $blocks; if ($total > 0) { //if for some reason the total is 0, we don't send an email $mailer->send_mail(); } } /** * Create an email block by risk level * * @param string $risk_level * @param int $count * * @return array */ protected function createBlock(string $risk_level, int $count): array { $plugin_name = ''; //if we have only one plugin with this risk level, we can show the plugin name //we search it in the list if ( $count===1 ){ $plugins = $this->workable_plugins; foreach ($plugins as $plugin) { if (isset($plugin['risk_level']) && $plugin['risk_level'] === $risk_level) { $plugin_name = $plugin['Name']; } } } $risk = $this->risk_naming[$risk_level]; $title = $this->get_warning_string($risk_level, $count); $message = $count === 1 ? sprintf(__("A %s vulnerability is found in %s.", "really-simple-ssl"),$risk, $plugin_name) : sprintf(__("Multiple %s vulnerabilities have been found.", "really-simple-ssl"), $risk); return [ 'title' => $title, 'message' => $message . ' ' . __('Based on your settings, Really Simple SSL will take appropriate action, or you will need to solve it manually.','really-simple-ssl') .' '. sprintf(__('Get more information from the Really Simple SSL dashboard on %s'), $this->domain() ), 'url' => rsssl_admin_url('#settings/vulnerabilities_notifications'), ]; } /** * @param string $risk_level * @param int $count * * @return string */ public function get_warning_string( string $risk_level, int $count): string { switch ($risk_level){ case 'c': $warning = sprintf(_n('You have %s critical vulnerability', 'You have %s critical vulnerabilities', $count, 'really-simple-ssl'), $count); break; case 'h': $warning = sprintf(_n('You have %s high-risk vulnerability', 'You have %s high-risk vulnerabilities', $count, 'really-simple-ssl'), $count); break; case 'm': $warning = sprintf(_n('You have %s medium-risk vulnerability', 'You have %s medium-risk vulnerabilities', $count, 'really-simple-ssl'), $count); break; default: $warning = sprintf(_n('You have %s low-risk vulnerability', 'You have %s low-risk vulnerabilities', $count, 'really-simple-ssl'), $count); break; } return $warning; } /** * Get a nicely formatted date for today's date * * @return string */ public function date(): string { return date_i18n( get_option( 'date_format' )); } /** * Get the domain name in a clickable format * * @return string */ public function domain(): string { return ''.$this->site_url().''; } /** * Cron triggers may sometimes result in http URL's, even though SSL is enabled in Really Simple SSL. * We ensure that the URL is returned with https if SSL is enabled. * * @return string */ public function site_url(): string { $ssl_enabled = rsssl_get_option('ssl_enabled') || is_ssl(); $scheme = $ssl_enabled ? 'https' : 'http'; return get_site_url(null, '', $scheme); } } //we initialize the class //add_action('init', array(rsssl_vulnerabilities::class, 'instance')); if ( !defined('rsssl_pro') ) { $vulnerabilities = new rsssl_vulnerabilities(); } } ######################################################################################### # Functions for the vulnerability scanner # # These functions are used in the vulnerability scanner like the notices and the api's # ######################################################################################### //we clear all the cache when the vulnerability scanner is enabled function rsssl_vulnerabilities_api( array $response, string $action, $data ): array { if ( ! rsssl_user_can_manage() ) { return $response; } switch ($action) { case 'vulnerabilities_test_notification': //creating a random string based on time. $random_string = md5( time() ); update_option( 'test_vulnerability_tester', $random_string, false ); //clear admin notices cache delete_option('rsssl_admin_notices'); $response = rsssl_vulnerabilities::testGenerator(); break; case 'vulnerabilities_scan_files': $response = rsssl_vulnerabilities::firstRun(); break; case 'vulnerabilities_measures_get': $response = ( new rsssl_vulnerabilities )->measures_data(); break; case 'vulnerabilities_measures_set': $response = ( new rsssl_vulnerabilities )->measures_set($data); break; } return $response; } add_filter( 'rsssl_do_action', 'rsssl_vulnerabilities_api', 10, 3 ); /* End of Routing and API's */ PK!z|  *wordpress/block-code-execution-uploads.phpnu[ 'rsssl_code_execution_allowed', 'score' => 5, 'output' => array( 'file-not-found' => array( 'msg' => __("Could not find code execution test file.", "really-simple-ssl"), 'icon' => 'open', 'dismissible' => true, ), 'uploads-folder-not-writable' => array( 'msg' => __("Uploads folder not writable.", "really-simple-ssl"), 'icon' => 'open', 'dismissible' => true, ), 'could-not-create-test-file' => array( 'msg' => __("Could not copy code execution test file.", "really-simple-ssl"), 'icon' => 'open', 'dismissible' => true, ), ), ); if ( rsssl_get_server() === 'nginx') { $notices['code-execution-uploads-nginx'] = array( 'callback' => 'rsssl_code_execution_allowed', 'score' => 5, 'output' => array( 'true' => array( 'msg' => __("The code to block code execution in the uploads folder cannot be added automatically on nginx. Add the following code to your nginx.conf file:", "really-simple-ssl") . "
" . rsssl_get_nginx_code_code_execution_uploads(), 'icon' => 'open', 'dismissible' => true, ), ), ); } return $notices; } add_filter('rsssl_notices', 'rsssl_code_execution_errors_notice'); /** * Block code execution * @param array $rules * * @return [] * */ function rsssl_disable_code_execution_rules($rules) { if ( !rsssl_get_option('block_code_execution_uploads')) { return $rules; } if ( RSSSL()->server->apache_version_min_24() ) { $rule = "\n" .""; $rule .= "\n" . "Require all denied"; $rule .= "\n" . ""; } else { $rule = "\n" .""; $rule .= "\n" . "deny from all"; $rule .= "\n" . ""; } $rules[] = ['rules' => $rule, 'identifier' => 'deny from all']; return $rules; } add_filter('rsssl_htaccess_security_rules_uploads', 'rsssl_disable_code_execution_rules'); function rsssl_get_nginx_code_code_execution_uploads() { $code = 'location ~* /uploads/.*\.php$ {' . "
"; $code .= '    return 503;' . "
"; $code .= '}
' . "
"; return $code; } PK!N ` )wordpress/vulnerabilities/FileStorage.phpnu[generateHashKey(); } public Static function StoreFile($file, $data) { $storage = new FileStorage(); $storage->set($data, $file); } public Static function GetFile($file) { $storage = new FileStorage(); return $storage->get($file); } /** Get the data from the file * @param $file * @return bool|mixed */ public function get($file) { if (file_exists($file)) { $data = file_get_contents($file); $data = $this->Decode64WithHash($data); return json_decode($data); } return false; } /** Save the data to the file * @param $data * @param $file */ public function set($data, $file) { $data = $this->Encode64WithHash(json_encode($data)); file_put_contents($file, $data); } /** encode the data with a hash * @param $data * @return string */ private function Encode64WithHash($data): string { //we create a simple encoding, using the hashkey as a salt $data = base64_encode($data); return base64_encode($data . $this->hash); } /** decode the data with a hash * @param $data * @return string */ private function Decode64WithHash($data): string { //we create a simple decoding, using the hashkey as a salt $data = base64_decode($data); $data = substr($data, 0, -strlen($this->hash)); return base64_decode($data); } /** Generate a hashkey and store it in the database * @return void */ private function generateHashKey(): void { if (get_option('rsssl_hashkey') && get_option('rsssl_hashkey') !== "") { $this->hash = get_option('rsssl_hashkey'); } else { $this->hash = md5(uniqid(rand(), true)); update_option('rsssl_hashkey', $this->hash, false); } } public static function GetDate(string $file) { if (file_exists($file)) { return filemtime($file); } return false; } public static function DeleteAll() { //we get the upload folder $upload_dir = wp_upload_dir(); //we get the really-simple-ssl folder $rsssl_dir = $upload_dir['basedir'] . '/really-simple-ssl'; //then we delete the following files from that folder: manifest.json, components.json and core.json $files = array('manifest.json', 'components.json', 'core.json'); foreach ($files as $file) { //we delete the file $file = $rsssl_dir . '/' . $file; if (file_exists($file)) { unlink($file); } } } }PK!9$$6wordpress/vulnerabilities/class-rsssl-file-storage.phpnu[generateHashKey(); $upload_dir = wp_upload_dir(); $this->folder = $upload_dir['basedir'] . '/' . Rsssl_Folder_Name::getFolderName(); } public static function StoreFile( $file, $data ): void { $storage = new Rsssl_File_Storage(); //first we check if the storage folder is already in the $file string if ( strpos( $file, $storage->folder ) !== false ) { $file = str_replace( $storage->folder . '/', '', $file ); } $storage->set( $data, $storage->folder . '/' . $file ); } public static function GetFile( $file ) { $storage = new Rsssl_File_Storage(); //first we check if the storage folder is already in the $file string if ( strpos( $file, $storage->folder ) !== false ) { $file = str_replace( $storage->folder . '/', '', $file ); } return $storage->get( $storage->folder . '/' . $file ); } /** Get the data from the file * * @param $file * * @return bool|mixed */ public function get( $file ) { if ( file_exists( $file ) ) { $data = file_get_contents( $file ); $data = $this->Decode64WithHash( $data ); return json_decode( $data ); } return false; } /** Save the data to the file * * @param $data * @param $file */ public function set( $data, $file ) { if ( ! is_dir( $this->folder ) ) { return; } if ( ! is_writable( $this->folder ) ) { return; } $data = $this->Encode64WithHash( json_encode( $data ) ); //first we check if the storage folder is already in the $file string if ( strpos( $file, $this->folder ) !== false ) { $file = str_replace( $this->folder . '/', '', $file ); } file_put_contents( $this->folder . '/' . $file, $data ); } /** encode the data with a hash * * @param $data * * @return string * @throws \Exception */ private function Encode64WithHash( $data ): string { $crypto_strong = false; $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'), $crypto_strong); // Check if IV generation was successful and cryptographically strong if ($iv === false || $crypto_strong === false) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( "Really Simple SSL: Could not generate a secure initialization vector." ); } } $encrypted = openssl_encrypt($data, 'aes-256-cbc', $this->hash, 0, $iv); // Store the $iv along with the $encrypted data, so we can use it during decryption $encrypted = base64_encode($encrypted . '::' . $iv); return $encrypted; } /** decode the data with a hash * * @param $data * * @return string * @throws \Exception */ private function Decode64WithHash($data): string { $data = base64_decode( $data ); [ $encrypted_data, $iv ] = explode( '::', $data, 2 ); // Check if IV was successfully retrieved if ( $iv === false ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( 'Really Simple SSL: Could not retrieve the initialization vector.' ); } } $decrypted = openssl_decrypt( $encrypted_data, 'aes-256-cbc', $this->hash, 0, $iv ); return $decrypted; } /** Generate a hashkey and store it in the database * @return void */ private function generateHashKey(): void { if ( get_option( 'rsssl_hashkey' ) && get_option( 'rsssl_hashkey' ) !== "" ) { $this->hash = get_option( 'rsssl_hashkey' ); } else { $this->hash = md5( uniqid( rand(), true ) ); update_option( 'rsssl_hashkey', $this->hash, false ); } } public static function GetDate( string $file ) { if ( file_exists( $file ) ) { return filemtime( $file ); } return false; } public static function get_upload_dir() { return ( new Rsssl_File_Storage() )->folder; } public static function validateFile( string $file ): bool { $storage = new Rsssl_File_Storage(); $file = $storage->folder . '/' . $file; if ( file_exists( $file ) ) { return true; } return false; } /** * Delete all files in the storage folder * * @return void */ public static function DeleteAll(): void { $storage = new Rsssl_File_Storage(); //we get the really-simple-ssl folder $rsssl_dir = $storage->folder; //then we delete the following files from that folder: manifest.json, components.json and core.json $files = array( 'manifest.json', 'components.json', 'core.json' ); foreach ( $files as $file ) { //we delete the file $file = $rsssl_dir . '/' . $file; if ( file_exists( $file ) ) { unlink( $file ); } } //we delete the folder if ( file_exists( $rsssl_dir ) ) { self::DeleteFolder($rsssl_dir); //we delete the option delete_option( 'rsssl_folder_name' ); } } /** * Recursively delete a folder and its contents. * * @param string $dir The path to the folder to be deleted. * * @return bool Returns true if the folder was successfully deleted, false otherwise. */ public static function DeleteFolder($dir): bool { if (substr($dir, strlen($dir) - 1, 1) != '/') $dir .= '/'; if ($handle = opendir($dir)) { while ($obj = readdir($handle)) { if ($obj != '.' && $obj != '..') { if (is_dir($dir.$obj)) { if (!self::DeleteFolder($dir.$obj)) return false; } elseif (is_file($dir.$obj)) { if (!unlink($dir.$obj)) return false; } } } closedir($handle); if (!rmdir($dir)) return false; return true; } return false; } /** * Delete all files in the storage folder * * @return void */ public static function DeleteOldFiles(): void { $rsssl_dir = wp_upload_dir()['basedir'] . '/really-simple-ssl'; //then we delete the following files from that folder: manifest.json, components.json and core.json $files = array( 'manifest.json', 'components.json', 'core.json' ); foreach ( $files as $file ) { //we delete the file $file = $rsssl_dir . '/' . $file; if ( file_exists( $file ) ) { unlink( $file ); } } } }PK!ʵUU5wordpress/vulnerabilities/class-rsssl-folder-name.phpnu[initializeFolderName(); $this->verifyAndCreateFolder(); } private function initializeFolderName(): void { $rsssl_folder = get_option( 'rsssl_folder_name' ); if ( $rsssl_folder ) { $this->folderName = $this->folderName( $rsssl_folder ); } else { $newFolderName = 'really-simple-ssl/' . md5( uniqid( mt_rand(), true ) ); $this->folderName = $this->folderName( $newFolderName ); require_once 'class-rsssl-file-storage.php'; Rsssl_File_Storage::DeleteOldFiles(); update_option( 'rsssl_folder_name', $this->folderName ); } } private function folderName( $name ): string { return $name; } private function verifyAndCreateFolder(): void { $upload_dir = wp_upload_dir(); if ( ! file_exists( $upload_dir['basedir'] . '/' . $this->folderName ) ) { $this->createFolder(); } } public function createFolder(): void { $upload_dir = wp_upload_dir(); $folder_path = $upload_dir['basedir'] . '/' . $this->folderName; if ( ! file_exists( $folder_path ) && is_writable($upload_dir['basedir'] ) ) { if ( ! mkdir( $folder_path, 0755, true ) && ! is_dir( $folder_path ) ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( sprintf( 'Really Simple SSL: Directory "%s" was not created', $folder_path ) ); } } } } /** * Creates a new folder name and saves it in the settings * * @return string */ public static function getFolderName(): string { return (new Rsssl_Folder_Name())->folderName; } }PK!lwordpress/disable-xmlrpc.phpnu[ PK!wordpress/rest-api.phpnu[get_header( 'authorization' ) ) { return new WP_Error( 'authorization', 'Unauthorized access.', array( 'status' => 401 ) ); } // if ( rsssl_get_networkwide_option('rsssl_restrict_rest_api') === 'restrict-roles' ) { // Check for certain role and allowed route if ( ! in_array( 'administrator', wp_get_current_user()->roles ) ) { return new WP_Error( 'forbidden', 'Access forbidden.', array( 'status' => 403 ) ); } // } // if ( rsssl_get_networkwide_option('rsssl_restrict_rest_api') === 'logged-in-users' ) { if ( ! is_user_logged_in() ) { return new WP_Error( 'forbidden', 'Access forbidden to non-logged in users.', array( 'status' => 403 ) ); } // } // if ( rsssl_get_networkwide_option('rsssl_restrict_rest_api') === 'application-passwords' ) { if ( ! is_user_logged_in() ) { return new WP_Error( 'forbidden', 'Access forbidden to non-logged in users.', array( 'status' => 403 ) ); } // } return $response; } /** * @return void * Disable REST API */ function rsssl_disable_rest_api() { add_filter('json_enabled', '__return_false'); add_filter('json_jsonp_enabled', '__return_false'); } add_filter( 'rest_request_before_callbacks', 'authorize_rest_api_requests', 10, 3 );PK!d(wordpress/prevent-login-info-leakage.phpnu[ ['rsssl_username_admin_changed'], 'callback' => '_true_', 'score' => 5, 'output' => array( 'true' => array( 'msg' => sprintf(__("Username 'admin' has been changed to %s", "really-simple-ssl"),esc_html(get_site_transient('rsssl_username_admin_changed')) ), 'icon' => 'open', 'dismissible' => true, ), ), ); return $notices; } add_filter('rsssl_notices', 'rsssl_admin_username_changed'); /** * Add admin as not allowed username * @param array $illegal_user_logins * * @return array */ function rsssl_prevent_admin_user_add(array $illegal_user_logins){ $illegal_user_logins[] = 'admin'; $illegal_user_logins[] = 'administrator'; return $illegal_user_logins; } add_filter( 'illegal_user_logins', 'rsssl_prevent_admin_user_add' ); /** * Rename admin user * @return bool */ function rsssl_rename_admin_user() { if ( !rsssl_user_can_manage() ) { return false; } //to be able to update the admin user email, we need to disable this filter temporarily remove_filter( 'illegal_user_logins', 'rsssl_prevent_admin_user_add' ); // Get user data for login admin $admin_user = get_user_by('login','admin'); if ( $admin_user ) { // Get the new user login $new_user_login = trim(sanitize_user(rsssl_get_option('new_admin_user_login'))); if ( rsssl_new_username_valid() ) { $admin_user_id = $admin_user->data->ID; $admin_userdata = get_userdata( $admin_user_id ); $admin_email = $admin_userdata->data->user_email; global $wpdb; //get current user hash $user_hash = $wpdb->get_var($wpdb->prepare("select user_pass from {$wpdb->base_prefix}users where ID = %s", $admin_user_id) ); //create temp email address $domain = site_url(); $parse = parse_url( $domain ); $host = $parse['host'] ?? 'example.com'; $email = "$new_user_login@$host"; // Do not send an e-mail with this temporary e-mail address add_filter('send_email_change_email', '__return_false'); // update e-mail for existing user. Cannot have two accounts connected to the same e-mail address $success = wp_update_user( array( 'ID' => $admin_user_id, 'user_email' => $email, ) ); if ( ! $success ) { return false; } // Populate the new user data. Use current 'admin' userdata wherever available $new_userdata = array( 'user_pass' => rsssl_generate_random_string( 12 ), //temp, overwrite with actual hash later. //(string) The plain-text user password. 'user_login' => $new_user_login, //(string) The user's login username. 'user_nicename' => isset( $admin_user->data->user_nicename ) ? $admin_user->data->user_nicename : '', //(string) The URL-friendly user name. 'user_url' => isset( $admin_user->data->user_url ) ? $admin_user->data->user_url : '', //(string) The user URL. 'user_email' => isset( $admin_email ) ? $admin_email : '', //(string) The user email address. 'display_name' => isset( $admin_user->data->display_name ) ? $admin_user->data->display_name : '', //(string) The user's display name. Default is the user's username. 'nickname' => isset( $admin_user->data->nickname ) ? $admin_user->data->nickname : '', //(string) The user's nickname. Default is the user's username. 'first_name' => isset( $admin_user->data->user_firstname ) ? $admin_user->data->user_firstname : '', //(string) The user's first name. For new users, will be used to build the first part of the user's display name if $display_name is not specified. 'last_name' => isset( $admin_user->data->user_lastname ) ? $admin_user->data->user_lastname : '', //(string) The user's last name. For new users, will be used to build the second part of the user's display name if $display_name is not specified. 'description' => isset( $admin_user->data->description ) ? $admin_user->data->description : '', //(string) The user's biographical description. 'rich_editing' => isset( $admin_user->data->rich_editing ) ? $admin_user->data->rich_editing : '', //(string|bool) Whether to enable the rich-editor for the user. False if not empty. 'syntax_highlighting' => isset( $admin_user->data->syntax_highlighting ) ? $admin_user->data->syntax_highlighting : '', //(string|bool) Whether to enable the rich code editor for the user. False if not empty. 'comment_shortcuts' => isset( $admin_user->data->comment_shortcuts ) ? $admin_user->data->comment_shortcuts : '', //(string|bool) Whether to enable comment moderation keyboard shortcuts for the user. Default false. 'admin_color' => isset( $admin_user->data->admin_color ) ? $admin_user->data->admin_color : '', //(string) Admin color scheme for the user. Default 'fresh'. 'use_ssl' => isset( $admin_user->data->use_ssl ) ? $admin_user->data->use_ssl : '', //(bool) Whether the user should always access the admin over https. Default false. 'user_registered' => isset( $admin_user->data->user_registered ) ? $admin_user->data->user_registered : '', //(string) Date the user registered. Format is 'Y-m-d H:i:s'. 'show_admin_bar_front' => isset( $admin_user->data->show_admin_bar_front ) ? $admin_user->data->show_admin_bar_front : '', //(string|bool) Whether to display the Admin Bar for the user on the site's front end. Default true. 'role' => isset( $admin_user->roles[0] ) ? $admin_user->roles[0] : '', //(string) User's role. 'locale' => isset( $admin_user->data->locale ) ? $admin_user->data->locale : '', //(string) User's locale. Default empty. ); // Create new admin user $new_user_id = wp_insert_user( $new_userdata ); if ( ! $new_user_id || is_wp_error($new_user_id) ) { return false; } //store original user hash in this user. $wpdb->update( $wpdb->base_prefix.'users', ['user_pass' => $user_hash ], ['ID' => $new_user_id] ); require_once( ABSPATH . 'wp-admin/includes/user.php' ); wp_delete_user( $admin_user_id, $new_user_id ); // On multisite we have to update the $wpdb->prefix . sitemeta -> meta_key -> site_admins -> meta_value to the new username if ( is_multisite() ) { global $wpdb; $site_admins = $wpdb->get_var( "SELECT meta_value FROM {$wpdb->base_prefix}sitemeta WHERE meta_key = 'site_admins'" ); if ( is_serialized( $site_admins ) ) { $unserialized = unserialize( $site_admins ); foreach ( $unserialized as $index => $site_admin ) { if ( $site_admin === 'admin' ) { $unserialized[ $index ] = $new_user_login; } } $site_admins = serialize( $unserialized ); } $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->base_prefix}sitemeta SET meta_value = %s WHERE meta_key = 'site_admins'", $site_admins ) ); } set_site_transient( 'rsssl_username_admin_changed', $new_user_login, DAY_IN_SECONDS ); } return true; } return true; } add_action('rsssl_after_saved_fields','rsssl_rename_admin_user', 30); /** * @return bool * * Notice condition */ function rsssl_username_admin_changed() { if ( get_site_transient('rsssl_username_admin_changed') ) { return true; } return false; } PK!b wordpress/user-registration.phpnu[ "\n" . 'Options -Indexes', 'identifier' => 'Options -Indexes']; return $rules; } add_filter('rsssl_htaccess_security_rules', 'rsssl_disable_indexing_rules'); /** * Dropped suggestions for indexing in NGINX as indexing in NGINX is by default disabled. */ PK!Essintegrations.phpnu[ array( 'folder' => 'wordpress', 'impact' => 'medium', 'risk' => 'medium', 'option_id' => 'disable_anyone_can_register', ), 'file-editing' => array( 'folder' => 'wordpress', 'impact' => 'medium', 'risk' => 'low', 'option_id' => 'disable_file_editing', ), 'hide-wp-version' => array( 'folder' => 'wordpress', 'impact' => 'low', 'risk' => 'low', 'option_id' => 'hide_wordpress_version', ), 'user-enumeration' => array( 'folder' => 'wordpress', 'impact' => 'low', 'risk' => 'medium', 'option_id' => 'disable_user_enumeration', ), 'block-code-execution-uploads' => array( 'folder' => 'wordpress', 'impact' => 'medium', 'risk' => 'low', 'option_id' => 'block_code_execution_uploads', ), 'prevent-login-info-leakage' => array( 'folder' => 'wordpress', 'impact' => 'low', 'risk' => 'high', 'option_id' => 'disable_login_feedback', ), 'disable-indexing' => array( 'folder' => 'server', 'impact' => 'low', 'risk' => 'medium', 'option_id' => 'disable_indexing', 'has_deactivation' => true, ), 'rename-admin-user' => array( 'folder' => 'wordpress', 'impact' => 'high', 'risk' => 'high', 'option_id' => 'rename_admin_user', ), 'display-name-is-login-name' => array( 'folder' => 'wordpress', 'impact' => 'low', 'risk' => 'medium', 'option_id' => 'block_display_is_login', ), 'disable-xmlrpc' => array( 'folder' => 'wordpress', 'impact' => 'medium', 'risk' => 'low', 'option_id' => 'disable_xmlrpc', 'always_include' => false, ), 'vulnerabilities' => array( 'folder' => 'wordpress', 'impact' => 'medium', 'risk' => 'medium', 'option_id' => 'enable_vulnerability_scanner', 'admin_only' => true, ), ) ); /** * Check if this plugin's integration is enabled * @param string $plugin * @param array $details * * @return bool */ if ( ! function_exists('rsssl_is_integration_enabled') ) { function rsssl_is_integration_enabled( $plugin, $details ) { global $rsssl_integrations_list; if ( ! array_key_exists( $plugin, $rsssl_integrations_list ) ) { return false; } if ( $details['always_include'] ) { return true; } //if an integration was just enabled, we keep it enabled until it removes itself from the list. //only for admin users if ( rsssl_is_in_deactivation_list( $plugin ) ) { return true; } $field_id = $details['option_id'] ?? false; if ( ! $field_id ) { return false; } $field_value = $details['option_value'] ?? false; $stored_value = rsssl_get_option( $field_id ); if ( $field_value ) { if ( $stored_value === $field_value ) { return true; } } else if ( $stored_value ) { return true; } return false; } } /** * code loaded without privileges to allow integrations between plugins and services, when enabled. */ if ( ! function_exists('rsssl_integrations') ) { function rsssl_integrations() { global $rsssl_integrations_list; foreach ( $rsssl_integrations_list as $plugin => $details ) { $details = wp_parse_args( $details, [ 'option_id' => false, 'always_include' => false, 'folder' => false, 'admin_only' => false, ] ); if ( $details['admin_only'] && ! rsssl_admin_logged_in() ) { continue; } if ( rsssl_is_integration_enabled( $plugin, $details ) ) { $path = apply_filters( 'rsssl_integrations_path', rsssl_path, $plugin ); $file = $path . 'security/' . $details['folder'] . "/" . $plugin . '.php'; if ( ! file_exists( $file ) ) { continue; } require_once( $file ); } } } } add_action( 'plugins_loaded', 'rsssl_integrations', 10 ); add_action( 'rsssl_after_saved_fields', 'rsssl_integrations', 20 ); /** * Check if a plugin is on the deactivation list * * @param string $plugin * * @return bool */ if ( ! function_exists('rsssl_is_in_deactivation_list') ) { function rsssl_is_in_deactivation_list( string $plugin ): bool { if ( ! is_admin() || ! is_user_logged_in() ) { return false; } if ( ! is_array( get_option( 'rsssl_deactivate_list', [] ) ) ) { delete_option( 'rsssl_deactivate_list' ); } return in_array( $plugin, get_option( 'rsssl_deactivate_list', [] ) ); } }PK!W͖tests/code-execution.phpnu[ PK!۲QQfirewall-manager.phpnu[get_headers_nonce() . ' ) return;' . "\n\n"; $contents .= 'if (!defined("RSSSL_HEADERS_ACTIVE")) define("RSSSL_HEADERS_ACTIVE", true);'."\n"; $contents .= "//RULES START\n".$rules; // write to advanced-header.php file if ( is_writable( WP_CONTENT_DIR ) ) { file_put_contents( $advanced_headers_file, $contents ); } $wpconfig_path = $this->find_wp_config_path(); $wpconfig = file_get_contents( $wpconfig_path ); if ( is_writable( $wpconfig_path ) && strpos( $wpconfig, 'advanced-headers.php' ) === false ) { // As WP_CONTENT_DIR is not defined at this point in the wp-config, we can't use that. // for those setups where the WP_CONTENT_DIR is not in the default location, we hardcode the path. if ( $use_dynamic_path ) { $rule = 'if (file_exists( ABSPATH . "wp-content/advanced-headers.php")) {' . "\n"; $rule .= "\t" . 'require_once ABSPATH . "wp-content/advanced-headers.php";' . "\n" . '}'; } else { $rule = 'if (file_exists(\'' . WP_CONTENT_DIR . '/advanced-headers.php\')) {' . "\n"; $rule .= "\t" . 'require_once \'' . WP_CONTENT_DIR . '/advanced-headers.php\';' . "\n" . '}'; } //if RSSSL comment is found, insert after $rsssl_comment = '//END Really Simple SSL Server variable fix'; if ( strpos($wpconfig, $rsssl_comment)!==false ) { $pos = strrpos($wpconfig, $rsssl_comment); $updated = substr_replace($wpconfig, $rsssl_comment."\n" . $rule . "\n", $pos, strlen($rsssl_comment)); } else { $updated = preg_replace( '/' . '<\?php' . '/', 'has_rules()) { return false; } return !defined('RSSSL_HEADERS_ACTIVE'); } /** * Show some notices * @param array $notices * * @return array */ public function notices( $notices ) { $notices['firewall-error'] = array( 'callback' => 'RSSSL_SECURITY()->firewall_manager->firewall_write_error', 'score' => 5, 'output' => array( 'wpconfig-notwritable' => array( 'title' => __("Firewall", "really-simple-ssl"), 'msg' => __("A firewall rule was enabled, but the wp-config.php is not writable.", "really-simple-ssl").' '.__("Please set the wp-config.php to writable until the rule has been written.", "really-simple-ssl"), 'icon' => 'open', 'dismissible' => true, ), 'advanced-headers-notwritable' => array( 'title' => __("Firewall", "really-simple-ssl"), 'msg' => __("A firewall rule was enabled, but /the wp-content/ folder is not writable.", "really-simple-ssl").' '.__("Please set the wp-content folder to writable:", "really-simple-ssl"), 'icon' => 'open', 'dismissible' => true, ), ), 'show_with_options' => [ 'disable_http_methods', ] ); $notices['firewall-active'] = array( 'condition' => ['RSSSL_SECURITY()->firewall_manager->firewall_active_error'], 'callback' => '_true_', 'score' => 5, 'output' => array( 'true' => array( 'title' => __("Firewall", "really-simple-ssl"), 'msg' => __("A firewall rule was enabled, but the firewall does not seem to get loaded correctly.", "really-simple-ssl").' '.__("Please check if the advanced-headers.php file is included in the wp-config.php, and exists in the wp-content folder.", "really-simple-ssl"), 'icon' => 'open', 'dismissible' => true, ), ), 'show_with_options' => [ 'disable_http_methods', ] ); return $notices; } /** * Admin is not always loaded here, so we define our own function * @return string|null */ public function find_wp_config_path() { //limit nr of iterations to 5 $i = 0; $maxiterations = 5; $dir = ABSPATH; do { $i++; if ( file_exists($dir . "/wp-config.php") ) { return $dir . "/wp-config.php"; } } while (($dir = realpath("$dir/..")) && ($i < $maxiterations)); return null; } /** * Clear the headers * @return void */ public function remove_advanced_headers() { if ( !rsssl_user_can_manage() ) { return; } $file = ABSPATH . 'wp-content/advanced-headers.php'; $wpconfig_path = $this->find_wp_config_path(); if ( is_writable( $wpconfig_path ) ) { $wpconfig = file_get_contents( $wpconfig_path ); $rule = "if ( file_exists('" . $file . "') ) { " . "\n"; $rule .= "\t" . "require_once '$file';" . "\n" . "}"; if ( strpos( $wpconfig, $rule ) !== false ) { $updated_wpconfig = str_replace( $rule, '', $wpconfig ); file_put_contents( $wpconfig_path, $updated_wpconfig ); } } if ( file_exists( $file ) ) { if ( !defined('RSSSL_SAFE_MODE')) unlink( $file ); } } } PK!i| | sync-settings.phpnu[ 'default', 'title' => __( "Debug.log", 'really-simple-ssl' ), 'text' => __( "Changed debug.log location to:", 'really-simple-ssl' ).$location, ]; } } } if ( $field_id==='disable_indexing' ){ if ( !rsssl_directory_indexing_allowed() && !$field['value']) { $field['value'] = true; $field['disabled'] = true; } } if ( $field_id==='disable_anyone_can_register' ){ if ( !get_option('users_can_register') && !$field['value'] ) { $field['value'] = true; $field['disabled'] = true; } } if ( $field_id==='disable_http_methods' ){ if ( !rsssl_http_methods_allowed() && !$field['value'] ) { $field['value'] = true; $field['disabled'] = true; } } if ( $field_id==='disable_file_editing' ){ if ( defined('DISALLOW_FILE_EDIT') && DISALLOW_FILE_EDIT && !$field['value'] ) { $field['value'] = true; $field['disabled'] = true; } } if ( $field_id==='block_code_execution_uploads' ){ if ( !rsssl_code_execution_allowed() && !$field['value'] ) { $field['value'] = true; $field['disabled'] = true; } } if ( $field_id==='disable_xmlrpc' ){ if ( !rsssl_xmlrpc_enabled() && !$field['value'] ) { $field['value'] = true; $field['disabled'] = true; } } if ( $field_id==='rename_db_prefix' ){ if ( !rsssl_is_default_wp_prefix() && !$field['value'] ) { $field['value'] = true; $field['disabled'] = true; } } return $field; } add_filter('rsssl_field', 'rsssl_disable_fields', 10, 2); PK!08kk hardening.phpnu[PK!(E0 cron.phpnu[PK!O security.phpnu[PK!]vKKfdeactivate-integration.phpnu[PK!6'' !notices.phpnu[PK! ?SBB Ifunctions.phpnu[PK!l index.phpnu[PK!ZH00 Xtests.phpnu[PK!.RRwordpress/user-enumeration.phpnu[PK!.9+wordpress/file-editing.phpnu[PK!6 wordpress/hide-wp-version.phpnu[PK!6ϭwordpress/vulnerabilities.phpnu[PK!z|  *wordpress/block-code-execution-uploads.phpnu[PK!N ` )wordpress/vulnerabilities/FileStorage.phpnu[PK!9$$6 wordpress/vulnerabilities/class-rsssl-file-storage.phpnu[PK!ʵUU5wordpress/vulnerabilities/class-rsssl-folder-name.phpnu[PK!lMwordpress/disable-xmlrpc.phpnu[PK!Э&&(wordpress/display-name-is-login-name.phpnu[PK!?t4s##wordpress/index.phpnu[PK!wordpress/rest-api.phpnu[PK!d( wordpress/prevent-login-info-leakage.phpnu[PK!p**:wordpress/rename-admin-user.phpnu[PK!b -wordpress/user-registration.phpnu[PK!l .server/index.phpnu[PK!*Rc**[/server/disable-indexing.phpnu[PK!Ess1integrations.phpnu[PK!W͖Etests/code-execution.phpnu[PK!?t4s##aFtests/index.phpnu[PK!۲QQFfirewall-manager.phpnu[PK!i| | Xfsync-settings.phpnu[PKo q