query( $wpdb->prepare( 'DROP TABLE IF EXISTS %i', $wpdb->prefix . 'nlssl_' . $nlssl_table ) ); } // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange $nlssl_user_keys = array( '_nlssl_totp_secret', '_nlssl_totp_pending', '_nlssl_totp_enabled', '_nlssl_backup_codes', '_nlssl_backup_codes_once', '_nlssl_backup_codes_once_expires', '_nlssl_recovery_confirmed', '_nlssl_totp_last_counter', ); foreach ( $nlssl_user_keys as $nlssl_user_key ) { delete_metadata( 'user', 0, $nlssl_user_key, '', true ); } // Remove known per-user transient/cache state as well as SQL rows, without flushing another plugin's cache. $nlssl_page = 0; $nlssl_cache_deadline = microtime( true ) + 2.0; do { $nlssl_page++; $nlssl_ids = get_users( array( 'fields' => 'ID', 'orderby' => 'ID', 'order' => 'ASC', 'number' => 250, 'paged' => $nlssl_page, 'count_total' => false ) ); foreach ( $nlssl_ids as $nlssl_id ) { if ( microtime( true ) >= $nlssl_cache_deadline ) { break 2; } $nlssl_id = (int) $nlssl_id; $nlssl_active_key = 'nlssl_emergency_active_' . $nlssl_id; $nlssl_active = get_transient( $nlssl_active_key ); if ( is_array( $nlssl_active ) && is_string( $nlssl_active['challenge'] ?? null ) ) { delete_transient( 'nlssl_emergency_' . substr( hash( 'sha256', $nlssl_active['challenge'] ), 0, 32 ) ); } delete_transient( $nlssl_active_key ); delete_transient( 'nlssl_alert_' . md5( 'admin-login-' . $nlssl_id ) ); delete_transient( 'nlssl_alert_retry_' . md5( 'admin-login-' . $nlssl_id ) ); foreach ( array( 'nlssl_emergency_lock_' . $nlssl_id, 'nlssl_alert_lock_' . md5( 'admin-login-' . $nlssl_id ) ) as $nlssl_cached_lock ) { delete_option( $nlssl_cached_lock ); wp_cache_delete( $nlssl_cached_lock, 'options' ); } wp_cache_delete( $nlssl_id, 'user_meta' ); } } while ( count( $nlssl_ids ) === 250 ); foreach ( array( 'integrity-critical', 'health-critical', 'login-freeze' ) as $nlssl_alert_key ) { delete_transient( 'nlssl_alert_' . md5( $nlssl_alert_key ) ); delete_transient( 'nlssl_alert_retry_' . md5( $nlssl_alert_key ) ); delete_option( 'nlssl_alert_lock_' . md5( $nlssl_alert_key ) ); wp_cache_delete( 'nlssl_alert_lock_' . md5( $nlssl_alert_key ), 'options' ); } // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching foreach ( array( 'nlssl_alert_', 'nlssl_emergency_', 'nlssl_bot_', 'nlssl_audit_window_', 'nlssl_admin_', 'nlssl_pkg_', 'nlssl_core_' ) as $nlssl_transient_prefix ) { foreach ( array( '_transient_', '_transient_timeout_' ) as $nlssl_transient_kind ) { $wpdb->query( $wpdb->prepare( 'DELETE FROM %i WHERE option_name LIKE %s', $wpdb->options, $wpdb->esc_like( $nlssl_transient_kind . $nlssl_transient_prefix ) . '%' ) ); } } // Locks, and storage probes that Repair storage could not remove because the database failed while it ran. foreach ( array( 'nlssl_alert_lock_', 'nlssl_emergency_lock_', 'nlssl_emergency_action_', 'nlssl_storage_probe_' ) as $nlssl_lock_prefix ) { $wpdb->query( $wpdb->prepare( 'DELETE FROM %i WHERE option_name LIKE %s', $wpdb->options, $wpdb->esc_like( $nlssl_lock_prefix ) . '%' ) ); } // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching foreach ( array( 'nlssl_storage_degraded', 'nlssl_schema_checked', 'nlssl_schema_repair_backoff' ) as $nlssl_transient ) { delete_transient( $nlssl_transient ); } foreach ( array( 'nlssl_settings', 'nlssl_db_version', 'nlssl_login_freeze_until', 'nlssl_last_health', 'nlssl_last_integrity', 'nlssl_baseline_created_at', 'nlssl_baseline_stale', 'nlssl_baseline_components', 'nlssl_emergency_secret_hash', 'nlssl_bot_ip_lists', 'nlssl_bot_ip_status', 'nlssl_rate_fallback', 'nlssl_rate_fallback_lock', 'nlssl_login_rate_fallback', 'nlssl_login_rate_fallback_lock', 'nlssl_emergency_rate_fallback', 'nlssl_emergency_rate_fallback_lock', 'nlssl_event_rate_fallback', 'nlssl_event_rate_fallback_lock', 'nlssl_public_rate_fallback', 'nlssl_public_rate_fallback_lock', 'nlssl_integrity_action_lock', 'nlssl_integrity_trusted_hashes', 'nlssl_upload_scan_state', 'nlssl_integrity_scan_state', 'nlssl_baseline_build_state', 'nlssl_freeze_action_lock', ) as $nlssl_option ) { delete_option( $nlssl_option ); } /* * The held quarantine copies are the administrator's only copy of files NeverLast Security removed from the * site, so they survive an ordinary uninstall. This branch runs only when the administrator asked * for data deletion explicitly, which is the same request as "remove what NeverLast Security stored". */ // Opt-in data removal. Only plugin-owned regular files are deleted; never follow symlinks. // Cleanup requires a local direct transport. Failure to initialize leaves empty directories // behind rather than trying to remove an unrelated/remote directory recursively. global $wp_filesystem; if ( ! function_exists( 'WP_Filesystem' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } $nlssl_fs = null; if ( $wp_filesystem instanceof WP_Filesystem_Direct || WP_Filesystem( false, WP_CONTENT_DIR, true ) ) { $nlssl_fs = $wp_filesystem instanceof WP_Filesystem_Direct ? $wp_filesystem : null; } $nlssl_uploads = wp_upload_dir( null, false ); $nlssl_dirs = array(); if ( empty( $nlssl_uploads['error'] ) && ! empty( $nlssl_uploads['basedir'] ) ) { $nlssl_basedir = wp_normalize_path( untrailingslashit( (string) $nlssl_uploads['basedir'] ) ); if ( is_dir( $nlssl_basedir ) && ! is_link( $nlssl_basedir ) ) { $nlssl_dirs[] = $nlssl_basedir . '/neverlast-security-lite/quarantine'; } } if ( defined( 'WP_CONTENT_DIR' ) ) { $nlssl_dirs[] = wp_normalize_path( untrailingslashit( WP_CONTENT_DIR ) . '/.nlssl-quarantine' ); } foreach ( $nlssl_dirs as $nlssl_quarantine ) { if ( ! is_dir( $nlssl_quarantine ) || is_link( $nlssl_quarantine ) || is_link( dirname( $nlssl_quarantine ) ) ) { continue; } foreach ( (array) scandir( $nlssl_quarantine ) as $nlssl_entry ) { $nlssl_held = $nlssl_quarantine . '/' . $nlssl_entry; if ( is_string( $nlssl_entry ) && 1 === preg_match( '#^(?:[a-f0-9]{64}\.(?:qtn|json)|\.htaccess|web\.config|index\.php)$#D', $nlssl_entry ) && is_file( $nlssl_held ) && ! is_link( $nlssl_held ) ) { wp_delete_file( $nlssl_held ); } } if ( null !== $nlssl_fs ) { $nlssl_fs->rmdir( $nlssl_quarantine, false ); } // Never recursively delete unrecognized evidence. } if ( isset( $nlssl_basedir ) ) { $nlssl_parent = $nlssl_basedir . '/neverlast-security-lite'; if ( is_dir( $nlssl_parent ) && ! is_link( $nlssl_parent ) ) { foreach ( array( '.htaccess', 'web.config', 'index.php' ) as $nlssl_entry ) { $nlssl_held = $nlssl_parent . '/' . $nlssl_entry; if ( is_file( $nlssl_held ) && ! is_link( $nlssl_held ) ) { wp_delete_file( $nlssl_held ); } } if ( null !== $nlssl_fs ) { $nlssl_fs->rmdir( $nlssl_parent, false ); } } } unset( $nlssl_fs, $nlssl_dirs, $nlssl_quarantine, $nlssl_entry, $nlssl_held, $nlssl_basedir, $nlssl_parent, $nlssl_uploads ); unset( $nlssl_settings, $nlssl_table, $nlssl_user_keys, $nlssl_user_key, $nlssl_option, $nlssl_page, $nlssl_ids, $nlssl_id, $nlssl_active_key, $nlssl_active, $nlssl_alert_key, $nlssl_transient_prefix, $nlssl_transient_kind, $nlssl_lock_prefix, $nlssl_transient, $nlssl_cached_lock, $nlssl_cache_deadline );