Server IP : 104.21.14.48 / Your IP : 3.145.65.157 [ Web Server : Apache System : Linux b70eb322-3aee-0c53-7c82-0db91281f2c6.secureserver.net 6.1.90-1.el9.elrepo.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 2 12:09:22 EDT 2024 x86_64 User : root ( 0) PHP Version : 8.0.30.2 Disable Function : NONE Domains : 0 Domains MySQL : ON | cURL : ON | WGET : ON | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/chroot/var/www/wp-content/plugins/defender-security/src/component/ |
Upload File : |
<?php /** * Responsible for handling legacy data migration and management related to scan issues and ignored items. * * @package WP_Defender\Component */ namespace WP_Defender\Component; use WP_Defender\Component; use WP_Defender\Model\Scan; use WP_Defender\Model\Scan_Item; /** * Responsible for handling legacy data migration and management related to scan issues and ignored items. * * @since 2.5.6 */ class Legacy_Versions extends Component { public const IGNORE_LIST = 'wdfscanignore'; /** * Retrieves all scan issue items from the database. * * @return array Array of scan issue items. */ private function find_all_scan_issue_items() { global $wpdb; $sql = 'SELECT t0.ID AS id,t0.post_parent AS parentId,t1.meta_value AS type,t2.meta_value AS raw'; $sql .= ' FROM ' . $wpdb->posts . ' AS t0'; $sql .= ' LEFT JOIN ' . $wpdb->postmeta . " as t1 ON t1.post_id=ID AND t1.meta_key='type'"; $sql .= ' LEFT JOIN ' . $wpdb->postmeta . " as t2 ON t2.post_id=ID AND t2.meta_key='raw'"; $sql .= " WHERE t0.post_type='wdf_scan_item' AND t0.post_status = 'issue'"; $sql .= ' GROUP BY ID'; return $wpdb->get_results( $sql, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery } /** * Retrieves all ignored scan items based on provided IDs. * * @param array $ids Array of IDs to retrieve. * * @return array Array of ignored scan items. */ private function find_all_scan_ignored_items( $ids ) { global $wpdb; if ( is_array( $ids ) && count( $ids ) > 0 ) { $sql = 'SELECT t0.ID AS id,t0.post_parent AS parentId,t1.meta_value AS type,t2.meta_value AS raw'; $sql .= ' FROM ' . $wpdb->posts . ' AS t0'; $sql .= ' LEFT JOIN ' . $wpdb->postmeta . " as t1 ON t1.post_id=ID AND t1.meta_key='type'"; $sql .= ' LEFT JOIN ' . $wpdb->postmeta . " as t2 ON t2.post_id=ID AND t2.meta_key='raw'"; $sql .= " WHERE t0.post_type='wdf_scan_item' AND t0.post_status = 'ignored'"; $sql .= ' AND t0.ID IN (' . implode( ', ', $ids ) . ')'; // SQL is prepared above. so we will ignore prepare warning. return $wpdb->get_results( $sql, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery } return array(); } /** * Adapts old scan data format to the new format. * * @param array $old_data Old scan data to adapt. * * @return array Adapted scan data. */ private function adapt_scan_data( $old_data ) { $raw = ''; switch ( $old_data['type'] ) { case 'vuln': $new_type = Scan_Item::TYPE_VULNERABILITY; if ( isset( $old_data['raw'] ) && ! empty( $old_data['raw'] ) ) { $raw = maybe_unserialize( $old_data['raw'] ); /** * Different $raw['slug'] values depending on the type: * 'type' => 'theme', 'slug' => {THEME_SLUG}, ... * 'type' => 'plugin', 'slug' => {PLUGIN_SLUG}, ... * 'type' => 'WordPress', 'slug' => 'WordPress', ... */ $file = is_array( $raw ) && isset( $raw['slug'] ) ? $raw['slug'] : ''; } else { $file = ''; } if ( is_array( $raw ) && 'WordPress' !== $raw['type'] ) { $raw_data = array( 'type' => $raw['type'], 'slug' => $raw['slug'], 'base_slug' => $raw['slug'], 'version' => '', 'name' => $raw['slug'], 'bugs' => $raw['bugs'], ); } else { $raw_data = $old_data['raw']; } break; case 'content': $new_type = Scan_Item::TYPE_SUSPICIOUS; if ( isset( $old_data['raw'] ) && ! empty( $old_data['raw'] ) ) { $raw = maybe_unserialize( $old_data['raw'] ); $file = is_array( $raw ) && isset( $raw['file'] ) ? $raw['file'] : ''; } else { $file = ''; } $raw_data = is_array( $raw ) && isset( $raw['meta'] ) ? $raw['meta'] : array(); $raw_data['file'] = $file; break; case 'core': default: $new_type = Scan_Item::TYPE_INTEGRITY; if ( isset( $old_data['raw'] ) && ! empty( $old_data['raw'] ) ) { $raw = maybe_unserialize( $old_data['raw'] ); $file = is_array( $raw ) && isset( $raw['file'] ) ? $raw['file'] : ''; } else { $file = ''; } if ( is_array( $raw ) && isset( $raw['type'] ) ) { $short_desc = 'unknown' === $raw['type'] ? 'unversion' : $raw['type']; } else { $short_desc = ''; } $raw_data = array( 'file' => $file, 'type' => $short_desc, ); break; } return array( 'type' => $new_type, 'file' => $file, 'raw' => $raw_data, ); } /** * Saves scan issue data to the database. * * @param array $issue_items Array of issue items to save. * @param Scan $model Scan model instance to use for saving. */ private function save_scan_issue_data( $issue_items, $model ) { if ( empty( $issue_items ) ) { return; } // Get existed issues. $existed_issues = $model->get_issues( null, Scan_Item::STATUS_ACTIVE ); $unique_arr = array(); foreach ( $issue_items as $item ) { $data = $this->adapt_scan_data( $item ); if ( empty( $data['file'] ) ) { continue; } if ( ! empty( $existed_issues ) ) { // Scan was started in version > 2.3.2. foreach ( $existed_issues as $issue ) { if ( Scan_Item::TYPE_VULNERABILITY === $data['type'] ) { $file_name = $issue->raw_data['base_slug'] ?? ''; } else { $file_name = $issue->raw_data['file'] ?? ''; } // Check for uniqueness of elements. if ( $data['file'] !== $file_name && ! in_array( $file_name, $unique_arr, true ) ) { $unique_arr[] = $file_name; $model->add_item( $data['type'], $data['raw'], Scan_Item::STATUS_ACTIVE ); } } } else { $file_name = $data['file'] ?? $data['raw']['file'] ?? false; // Scan wasn't start. if ( $file_name && ! in_array( $file_name, $unique_arr, true ) ) { $unique_arr[] = $file_name; $model->add_item( $data['type'], $data['raw'], Scan_Item::STATUS_ACTIVE ); } } } } /** * Saves ignored scan data to the database. * * @param array $ignored_items Array of ignored items to save. * @param Scan $model Scan model instance to use for saving. */ private function save_scan_ignored_data( $ignored_items, $model ) { if ( empty( $ignored_items ) ) { return; } // Get INDEXER of ignored issues. $index_lists = get_site_option( Scan::IGNORE_INDEXER, array() ); // Get existed ignored issues. $existed_issues = $model->get_issues( null, Scan_Item::STATUS_IGNORE ); foreach ( $ignored_items as $item ) { $data = $this->adapt_scan_data( $item ); if ( empty( $data['file'] ) ) { continue; } if ( ! empty( $existed_issues ) ) { // Scan was started in version > 2.3.2 . foreach ( $existed_issues as $issue ) { if ( Scan_Item::TYPE_VULNERABILITY === $data['type'] ) { $file_name = $issue->raw_data['base_slug'] ?? ''; } else { $file_name = $issue->raw_data['file'] ?? ''; } // Check for uniqueness of elements. if ( $data['file'] !== $file_name && ! in_array( $file_name, $index_lists, true ) ) { if ( isset( $data['file'] ) ) { $index_lists[] = $data['file']; } elseif ( isset( $data['raw']['slug'] ) ) { $index_lists[] = $data['raw']['slug']; } $model->add_item( $data['type'], $data['raw'], Scan_Item::STATUS_IGNORE ); } } } else { $file_name = $data['file'] ?? $data['raw']['slug'] ?? false; // Scan wasn't start. if ( $file_name && ! in_array( $file_name, $index_lists, true ) ) { $index_lists[] = $file_name; $model->add_item( $data['type'], $data['raw'], Scan_Item::STATUS_IGNORE ); } } } // Update IGNORE_INDEXER. $model->update_ignore_list( $index_lists ); } /** * Retrieves all scan issue data. * * @return array Array of all scan issue data. */ public function get_scan_issue_data() { return $this->find_all_scan_issue_items(); } /** * Retrieves all scan ignored data. * * @return array Array of all scan ignored data. */ public function get_scan_ignored_data() { $ids = get_option( self::IGNORE_LIST, false ); if ( ! is_array( $ids ) ) { $ids = get_site_option( self::IGNORE_LIST, array() ); if ( ! is_array( $ids ) ) { return array(); } } return $this->find_all_scan_ignored_items( $ids ); } /** * Runs a simple scan. * * @return int ID of the scan. */ private function run_simlpe_scan() { $scan_component = wd_di()->get( Scan::class ); $scan_model = wd_di()->get( Scan::class ); // The simplest data. $scan_model->percent = 100; $scan_model->total_tasks = 1; $scan_model->task_checkpoint = ''; $scan_model->status = Scan::STATUS_FINISH; $scan_model->date_start = gmdate( 'Y-m-d H:i:s' ); $scan_model->date_end = gmdate( 'Y-m-d H:i:s' ); $scan_model->is_automation = false; $last_id = $scan_model->save(); $this->log( 'Scan ID during data migration: ' . $last_id, 'scan.log' ); $scan_component->advanced_scan_actions( $scan_model ); return $last_id; } /** * Migrates scan data from old format to new format. * * @param array $issue_list List of issue items to migrate. * @param array $ignored_list List of ignored items to migrate. */ public function migrate_scan_data( $issue_list, $ignored_list ) { $scan = Scan::get_active(); if ( is_object( $scan ) ) { $this->log( 'Scan is still running', 'scan.log' ); return; } $model = Scan::get_last(); // Scan was started and finished before. if ( is_object( $model ) && ! is_wp_error( $model ) ) { $this->save_scan_ignored_data( $ignored_list, $model ); $this->save_scan_issue_data( $issue_list, $model ); } else { /** * Scan hasn't started before. Next steps: * create Scan record, * no send notification, * create Scan items. */ $this->run_simlpe_scan(); $model = Scan::get_last(); if ( is_object( $model ) && ! is_wp_error( $model ) ) { $this->save_scan_ignored_data( $ignored_list, $model ); $this->save_scan_issue_data( $issue_list, $model ); } } } /** * Removes old scan data from the database. * * @param array $issue_list List of issue items to remove. * @param array $ignored_list List of ignored items to remove. */ public function remove_old_scan_data( $issue_list, $ignored_list ) { // Delete the list of scan issues. if ( ! empty( $issue_list ) ) { $parent_post_id = (int) $issue_list[0]['parentId']; foreach ( $issue_list as $key => $issue ) { // Delete scan items. wp_delete_post( (int) $issue['id'] ); } // Delete scan. wp_delete_post( $parent_post_id ); } // Delete the ignored list. if ( ! empty( $ignored_list ) ) { $parent_post_id = (int) $ignored_list[0]['parentId']; foreach ( $ignored_list as $key => $item ) { // Delete scan items. wp_delete_post( (int) $item['id'] ); } // Delete scan. wp_delete_post( $parent_post_id ); delete_site_option( self::IGNORE_LIST ); delete_option( self::IGNORE_LIST ); } } /** * Changes the onboarding status in the database. */ public function change_onboarding_status() { update_site_option( 'wp_defender_shown_activator', true ); } /** * Decrypts data using a public key. * * @param string $encrypt_data Data to decrypt. * @param string $key Public key to use for decryption. * * @return bool|string Decrypted data or false on failure. */ private static function decrypt_data_with_pub_key( $encrypt_data, $key ) { $str = base64_decode( $encrypt_data ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode if ( ! $str ) { return false; } $cipher = 'aes-256-cbc'; $iv_len = openssl_cipher_iv_length( $cipher ); $iv = substr( $str, 0, $iv_len ); $ciphertext_raw = substr( $str, $iv_len + 32 ); return openssl_decrypt( $ciphertext_raw, $cipher, $key, OPENSSL_RAW_DATA, $iv ); } /** * Decrypts data using a stored public key. * * @param string $data Data to decrypt. * * @return bool|string Decrypted data or false on failure. */ public static function get_decrypted_data_with_pub_key( $data ) { global $wp_filesystem; // Initialize the WP filesystem, no more using 'file-put-contents' function. if ( empty( $wp_filesystem ) ) { require_once ABSPATH . '/wp-admin/includes/file.php'; WP_Filesystem(); } $path_to_pub_key = __DIR__ . '/def.key'; if ( ! file_exists( $path_to_pub_key ) ) { return false; } $key = $wp_filesystem->get_contents( $path_to_pub_key ); return false !== $key ? self::decrypt_data_with_pub_key( $data, $key ) : ''; } }