403 ) ); } } /** * Verify nonce or die. * * @param string $action Nonce action. * @param string $query_arg Query arg or post field name. * @return bool */ public static function verify_nonce( $action, $query_arg = '_wpnonce' ) { $nonce = ''; if ( isset( $_REQUEST[ $query_arg ] ) ) { $nonce = sanitize_text_field( wp_unslash( $_REQUEST[ $query_arg ] ) ); } elseif ( isset( $_REQUEST['nonce'] ) ) { $nonce = sanitize_text_field( wp_unslash( $_REQUEST['nonce'] ) ); } elseif ( isset( $_REQUEST['_wpnonce'] ) ) { $nonce = sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ); } elseif ( isset( $_REQUEST['security'] ) ) { $nonce = sanitize_text_field( wp_unslash( $_REQUEST['security'] ) ); } if ( ! wp_verify_nonce( $nonce, $action ) ) { if ( wp_doing_ajax() ) { wp_send_json_error( array( 'message' => __( 'Security verification failed (invalid nonce). Please refresh the page and try again.', 'speednest' ), ), 403 ); } else { wp_die( esc_html__( 'Security check failed. Please reload the page and try again.', 'speednest' ), esc_html__( 'Security Check Failed', 'speednest' ), array( 'response' => 403 ) ); } } return true; } /** * Validate a file or directory path to prevent path traversal. * Only allows paths strictly inside the WP Content directory or plugin cache directory. * * @param string $path Target path. * @param string $allowed_base Base path that $path must reside within. * @return string|false Realpath if safe, false if invalid/traversal attempt. */ public static function validate_path( $path, $allowed_base = '' ) { if ( empty( $path ) || ! is_string( $path ) ) { return false; } // Detect null bytes and directory traversal strings. if ( strpos( $path, "\0" ) !== false || strpos( $path, '..' ) !== false ) { wpsco_log( 'Potential path traversal detected and blocked: ' . sanitize_text_field( $path ), 'error' ); return false; } if ( empty( $allowed_base ) ) { $allowed_base = function_exists( 'wpsco_get_cache_dir' ) ? wpsco_get_cache_dir() : ( defined( 'WPSCO_CACHE_DIR' ) ? WPSCO_CACHE_DIR : '' ); } $real_base = realpath( $allowed_base ); $real_path = realpath( $path ); // If file doesn't exist yet, check directory of parent. if ( false === $real_path ) { $dir = dirname( $path ); $real_dir = realpath( $dir ); if ( false === $real_dir || false === $real_base || strpos( $real_dir, $real_base ) !== 0 ) { return false; } return $path; } if ( false === $real_base || strpos( $real_path, $real_base ) !== 0 ) { wpsco_log( 'Unauthorized path access attempt blocked: ' . sanitize_text_field( $path ), 'error' ); return false; } return $real_path; } /** * Securely sanitize an array of settings. * * @param array $settings Raw settings. * @return array Sanitized settings. */ public static function sanitize_settings( $settings ) { if ( ! is_array( $settings ) ) { return array(); } $clean = array(); foreach ( $settings as $key => $value ) { $key = sanitize_key( $key ); if ( is_bool( $value ) ) { $clean[ $key ] = (bool) $value; } elseif ( is_numeric( $value ) ) { $clean[ $key ] = is_float( $value ) ? (float) $value : (int) $value; } elseif ( is_array( $value ) ) { $clean[ $key ] = array_map( function( $item ) { return is_string( $item ) ? sanitize_text_field( $item ) : $item; }, $value ); } elseif ( is_string( $value ) ) { // URLs. if ( strpos( $key, 'url' ) !== false ) { $clean[ $key ] = esc_url_raw( trim( $value ) ); } else { $clean[ $key ] = sanitize_text_field( trim( $value ) ); } } else { $clean[ $key ] = $value; } } return $clean; } }