From 2de344cfbdfb7cb482c9211d52d9a215f13af0b9 Mon Sep 17 00:00:00 2001 From: Jason Crist Date: Wed, 24 Sep 2025 16:26:52 -0400 Subject: [PATCH 1/2] Eliminated unnecessary security measures and fixed broken tests --- includes/class-pattern-builder-api.php | 43 +-- includes/class-pattern-builder-controller.php | 258 ++++-------------- includes/class-pattern-builder-security.php | 83 ------ tests/php/test-pattern-builder-api.php | 32 ++- 4 files changed, 75 insertions(+), 341 deletions(-) diff --git a/includes/class-pattern-builder-api.php b/includes/class-pattern-builder-api.php index fd1718b..1bed502 100644 --- a/includes/class-pattern-builder-api.php +++ b/includes/class-pattern-builder-api.php @@ -74,7 +74,6 @@ public function read_permission_callback() { /** * Permission callback for write operations (PUT, POST, DELETE). * Restricts access to users with pattern editing capabilities. - * Also verifies the REST API nonce for additional security. * * @param WP_REST_Request $request The REST request object. * @return bool|WP_Error True if the user can modify patterns, WP_Error otherwise. @@ -204,7 +203,7 @@ public function inject_theme_patterns( $response, $server, $request ) { if ( $tbell_pattern_block && $tbell_pattern_block->post_type === 'tbell_pattern_block' ) { // make sure the pattern has a pattern file $pattern_file_path = $this->controller->get_pattern_filepath( Abstract_Pattern::from_post( $tbell_pattern_block ) ); - if ( ! $pattern_file_path ) { + if ( is_wp_error( $pattern_file_path ) || ! $pattern_file_path ) { return $response; // No pattern file found, return the original response } $tbell_pattern_block->post_name = $this->controller->format_pattern_slug_from_post( $tbell_pattern_block->post_name ); @@ -342,16 +341,6 @@ function handle_hijack_block_delete( $response, $server, $request ) { if ( $post && $post->post_type === 'tbell_pattern_block' && $request->get_method() === 'DELETE' ) { - // Verify nonce for additional security - $nonce = $request->get_header( 'X-WP-Nonce' ); - if ( ! $nonce || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) { - return new WP_Error( - 'rest_cookie_invalid_nonce', - __( 'Cookie nonce is invalid', 'pattern-builder' ), - array( 'status' => 403 ) - ); - } - $deleted = wp_delete_post( $id, true ); if ( ! $deleted ) { @@ -362,14 +351,12 @@ function handle_hijack_block_delete( $response, $server, $request ) { $path = $this->controller->get_pattern_filepath( $abstract_pattern ); - if ( ! $path ) { - return new WP_Error( 'pattern_not_found', 'Pattern not found', array( 'status' => 404 ) ); + if ( is_wp_error( $path ) ) { + return $path; } - // Validate that the path is within the patterns directory - $validation = \Pattern_Builder_Security::validate_pattern_path( $path ); - if ( is_wp_error( $validation ) ) { - return $validation; + if ( ! $path ) { + return new WP_Error( 'pattern_not_found', 'Pattern not found', array( 'status' => 404 ) ); } // Use secure file delete operation @@ -443,16 +430,6 @@ function handle_hijack_block_update( $response, $handler, $request ) { ); } - // Verify the REST API nonce - $nonce = $request->get_header( 'X-WP-Nonce' ); - if ( ! $nonce || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) { - return new WP_Error( - 'rest_cookie_invalid_nonce', - __( 'Cookie nonce is invalid', 'pattern-builder' ), - array( 'status' => 403 ) - ); - } - $pattern = Abstract_Pattern::from_post( $post ); if ( isset( $updated_pattern['content'] ) ) { @@ -600,16 +577,6 @@ private function sanitize_pattern_input( $input ) { public function handle_block_to_pattern_conversion( $response, $handler, $request ) { if ( $request->get_method() === 'PUT' || $request->get_method() === 'POST' ) { - // Verify nonce for additional security on state-changing operations - $nonce = $request->get_header( 'X-WP-Nonce' ); - if ( ! $nonce || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) { - return new WP_Error( - 'rest_cookie_invalid_nonce', - __( 'Cookie nonce is invalid', 'pattern-builder' ), - array( 'status' => 403 ) - ); - } - $body = json_decode( $request->get_body(), true ); // Validate JSON decode was successful diff --git a/includes/class-pattern-builder-controller.php b/includes/class-pattern-builder-controller.php index bfb9ec4..b68a25c 100644 --- a/includes/class-pattern-builder-controller.php +++ b/includes/class-pattern-builder-controller.php @@ -23,42 +23,29 @@ public static function format_pattern_slug_from_post( $slug ) { } /** - * Get tbell_pattern_block post for a pattern with proper sanitization and caching. + * Get tbell_pattern_block post for a pattern. * * @param Abstract_Pattern $pattern The pattern object. * @return WP_Post|null The pattern post or null if not found. */ public function get_tbell_pattern_block_post_for_pattern( $pattern ) { - // Sanitize the pattern name for safe database usage - $sanitized_name = sanitize_title_with_dashes( $pattern->name ); - $sanitized_name = wp_strip_all_tags( $sanitized_name ); - $path = $this->format_pattern_slug_for_post( $sanitized_name ); - - // Create cache key for this specific pattern - $cache_key = 'tbell_pattern_post_' . md5( $sanitized_name ); - $pattern_post = get_transient( $cache_key ); - - if ( false === $pattern_post ) { - // Use WP_Query for better performance and security - $query = new WP_Query( - array( - 'name' => sanitize_title( $path ), - 'post_type' => 'tbell_pattern_block', - 'posts_per_page' => 1, - 'no_found_rows' => true, - 'update_post_meta_cache' => false, - 'update_post_term_cache' => false, - ) - ); + $path = $this->format_pattern_slug_for_post( $pattern->name ); - $pattern_post = $query->have_posts() ? $query->posts[0] : null; + $query = new WP_Query( + array( + 'name' => sanitize_title( $path ), + 'post_type' => 'tbell_pattern_block', + 'posts_per_page' => 1, + 'no_found_rows' => true, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + ) + ); - // Cache the result for 1 hour - set_transient( $cache_key, $pattern_post, HOUR_IN_SECONDS ); + $pattern_post = $query->have_posts() ? $query->posts[0] : null; - // Clean up - wp_reset_postdata(); - } + // Clean up + wp_reset_postdata(); if ( $pattern_post ) { $pattern_post->post_name = $pattern->name; @@ -68,85 +55,9 @@ public function get_tbell_pattern_block_post_for_pattern( $pattern ) { return $this->create_tbell_pattern_block_post_for_pattern( $pattern ); } - /** - * Secure alternative to get_page_by_path with proper sanitization and caching. - * - * @param string $page_path The page path to search for. - * @param string $output Optional. Output type. OBJECT, ARRAY_N, or ARRAY_A. - * @param string|array $post_type Optional. Post type or types to search. - * @return WP_Post|array|null The page object or null if not found. - */ - private function get_page_by_path_secure( $page_path, $output = OBJECT, $post_type = 'page' ) { - // Sanitize the page path - $sanitized_path = sanitize_title_with_dashes( $page_path ); - $sanitized_path = wp_strip_all_tags( $sanitized_path ); - - // Ensure post_type is safe - $post_types = is_array( $post_type ) ? $post_type : array( $post_type ); - $post_types = array_map( 'sanitize_key', $post_types ); - - // Create cache key - $cache_key = 'page_by_path_' . md5( $sanitized_path . serialize( $post_types ) ); - $cached_post = get_transient( $cache_key ); - - if ( false === $cached_post ) { - // Use WP_Query instead of direct get_page_by_path - $query = new WP_Query( - array( - 'name' => $sanitized_path, - 'post_type' => $post_types, - 'posts_per_page' => 1, - 'no_found_rows' => true, - 'update_post_meta_cache' => false, - 'update_post_term_cache' => false, - ) - ); - - $cached_post = $query->have_posts() ? $query->posts[0] : null; - - // Cache for 30 minutes (shorter than pattern cache due to potentially more frequent changes) - set_transient( $cache_key, $cached_post, 30 * MINUTE_IN_SECONDS ); - - // Clean up - wp_reset_postdata(); - } - - if ( $cached_post && $output === OBJECT ) { - return $cached_post; - } elseif ( $cached_post && $output === ARRAY_A ) { - return get_object_vars( $cached_post ); - } elseif ( $cached_post && $output === ARRAY_N ) { - return array_values( get_object_vars( $cached_post ) ); - } - - return null; - } - - /** - * Invalidate pattern-related caches when patterns are modified. - * - * @param string $pattern_name The pattern name that was modified. - */ - private function invalidate_pattern_cache( $pattern_name ) { - // Sanitize the pattern name - $sanitized_name = sanitize_title_with_dashes( $pattern_name ); - $sanitized_name = wp_strip_all_tags( $sanitized_name ); - - // Delete relevant transients - $cache_keys = array( - 'tbell_pattern_post_' . md5( $sanitized_name ), - 'page_by_path_' . md5( $sanitized_name . serialize( array( 'wp_block' ) ) ), - 'page_by_path_' . md5( $sanitized_name . serialize( array( 'tbell_pattern_block' ) ) ), - 'page_by_path_' . md5( $this->format_pattern_slug_for_post( $sanitized_name ) . serialize( array( 'tbell_pattern_block' ) ) ), - ); - - foreach ( $cache_keys as $key ) { - delete_transient( $key ); - } - } - public function create_tbell_pattern_block_post_for_pattern( $pattern ) { - $existing_post = $this->get_page_by_path_secure( $this->format_pattern_slug_for_post( $pattern->name ), OBJECT, array( 'tbell_pattern_block' ) ); + + $existing_post = get_page_by_path( $this->format_pattern_slug_for_post( $pattern->name ), OBJECT, array( 'tbell_pattern_block' ) ); $post_id = $existing_post ? $existing_post->ID : null; @@ -245,7 +156,7 @@ public function update_theme_pattern( Abstract_Pattern $pattern, $options = arra } // get the tbell_pattern_block post if it already exists - $post = $this->get_page_by_path_secure( $this->format_pattern_slug_for_post( $pattern->name ), OBJECT, array( 'tbell_pattern_block', 'wp_block' ) ); + $post = get_page_by_path( $this->format_pattern_slug_for_post( $pattern->name ), OBJECT, array( 'tbell_pattern_block', 'wp_block' ) ); if ( $post && $post->post_type === 'wp_block' ) { // this is being converted to theme patterns, change the slug to include the theme domain @@ -267,7 +178,7 @@ public function update_theme_pattern( Abstract_Pattern $pattern, $options = arra // rebuild the pattern from the file (so that the content has no PHP tags) $filepath = $this->get_pattern_filepath( $pattern ); - if ( $filepath ) { + if ( ! is_wp_error( $filepath ) && $filepath ) { $pattern = Abstract_Pattern::from_file( $filepath ); } @@ -315,9 +226,6 @@ public function update_theme_pattern( Abstract_Pattern $pattern, $options = arra // store categories wp_set_object_terms( $post_id, $pattern->categories, 'wp_pattern_category', false ); - // Invalidate cache for this pattern - $this->invalidate_pattern_cache( $pattern->name ); - return $pattern; } @@ -330,12 +238,10 @@ private function export_pattern_image_assets( $pattern ) { // skip if the asset isn't an image if ( ! preg_match( '/\.(jpg|jpeg|png|gif|webp|svg)$/i', $url ) ) { - return \Pattern_Builder_Security::create_error( + return new WP_Error( 'invalid_image_type', __( 'Asset is not a valid image type.', 'pattern-builder' ), - array( 'url' => $url ), - __METHOD__, - false // Don't log this as it's expected behavior + array( 'url' => $url ) ); } @@ -368,14 +274,13 @@ private function export_pattern_image_assets( $pattern ) { } if ( is_wp_error( $download_file ) ) { - return \Pattern_Builder_Security::create_error( + return new WP_Error( 'image_download_failed', __( 'Failed to download image asset.', 'pattern-builder' ), array( 'url' => $url, 'error' => $download_file->get_error_message(), - ), - __METHOD__ + ) ); } @@ -399,14 +304,13 @@ private function export_pattern_image_assets( $pattern ) { WP_Filesystem(); } if ( ! $wp_filesystem->move( $download_file, $upload_file ) ) { - return \Pattern_Builder_Security::create_error( + return new WP_Error( 'file_move_failed', __( 'Failed to move image file to uploads directory.', 'pattern-builder' ), array( 'source' => $download_file, 'destination' => $upload_file, - ), - __METHOD__ + ) ); } @@ -423,14 +327,13 @@ private function export_pattern_image_assets( $pattern ) { // Insert the attachment into the media library $attachment_id = wp_insert_attachment( $attachment, $upload_file ); if ( is_wp_error( $attachment_id ) ) { - return \Pattern_Builder_Security::create_error( + return new WP_Error( 'attachment_insert_failed', __( 'Failed to create media library attachment.', 'pattern-builder' ), array( 'file' => $upload_file, 'error' => $attachment_id->get_error_message(), - ), - __METHOD__ + ) ); } @@ -452,13 +355,9 @@ function ( $matches ) use ( $upload_image ) { if ( $new_url && ! is_wp_error( $new_url ) ) { return $matches[1] . '="' . $new_url . '"'; } - // Log error if image upload failed, but don't break the pattern + // Image upload failed, but don't break the pattern if ( is_wp_error( $new_url ) ) { - \Pattern_Builder_Security::log_error( - 'Image upload failed during pattern import: ' . $new_url->get_error_message(), - 'import_pattern_image_assets', - array( 'url' => $matches[2] ) - ); + // Error occurred but we continue without logging } return $matches[0]; }, @@ -474,13 +373,9 @@ function ( $matches ) use ( $upload_image ) { if ( $new_url && ! is_wp_error( $new_url ) ) { return '"url":"' . $new_url . '"'; } - // Log error if image upload failed, but don't break the pattern + // Image upload failed, but don't break the pattern if ( is_wp_error( $new_url ) ) { - \Pattern_Builder_Security::log_error( - 'JSON URL image upload failed during pattern import: ' . $new_url->get_error_message(), - 'import_pattern_image_assets', - array( 'url' => $url ) - ); + // Error occurred but we continue without logging } return $matches[0]; }, @@ -523,26 +418,17 @@ private function import_pattern_image_assets( $pattern ) { return false; } - $filename = \Pattern_Builder_Security::sanitize_filename( basename( $url ) ); + $filename = sanitize_file_name( basename( $url ) ); $asset_dir = get_stylesheet_directory() . '/assets/images/'; $destination_path = $asset_dir . $filename; - // Validate destination path - $validation = \Pattern_Builder_Security::validate_asset_path( $destination_path ); - if ( is_wp_error( $validation ) ) { - // Clean up the temp file and return false - if ( file_exists( $download_file ) ) { - wp_delete_file( $download_file ); - } - return false; - } - if ( ! is_dir( $asset_dir ) ) { wp_mkdir_p( $asset_dir ); } // Use secure file move operation $allowed_dirs = array( + '/tmp', get_stylesheet_directory() . '/assets', get_template_directory() . '/assets', ); @@ -603,7 +489,7 @@ public function update_user_pattern( Abstract_Pattern $pattern ) { array( 'status' => 403 ) ); } - $post = $this->get_page_by_path_secure( $pattern->name, OBJECT, 'wp_block' ); + $post = get_page_by_path( $pattern->name, OBJECT, 'wp_block' ); $convert_from_theme_pattern = false; if ( empty( $post ) ) { @@ -611,7 +497,7 @@ public function update_user_pattern( Abstract_Pattern $pattern ) { // this is for any user patterns that are being converted from theme patterns // It will be converted to a wp_block post when it is updated $slug = $this->format_pattern_slug_for_post( $pattern->name ); - $post = $this->get_page_by_path_secure( $slug, OBJECT, 'tbell_pattern_block' ); + $post = get_page_by_path( $slug, OBJECT, 'tbell_pattern_block' ); $convert_from_theme_pattern = true; } @@ -655,32 +541,11 @@ public function update_user_pattern( Abstract_Pattern $pattern ) { // if we are converting a theme pattern to a user pattern delete the theme pattern file if ( $convert_from_theme_pattern ) { $path = $this->get_pattern_filepath( $pattern ); - if ( $path ) { - // Validate that the path is within the patterns directory - $validation = \Pattern_Builder_Security::validate_pattern_path( $path ); - if ( ! is_wp_error( $validation ) ) { - // Use secure file delete operation - $allowed_dirs = array( - get_stylesheet_directory() . '/patterns', - get_template_directory() . '/patterns', - ); - $deleted = \Pattern_Builder_Security::safe_file_delete( $path, $allowed_dirs ); - - // Log if deletion failed but don't break the conversion - if ( is_wp_error( $deleted ) ) { - \Pattern_Builder_Security::log_error( - 'Failed to delete theme pattern file during conversion: ' . $deleted->get_error_message(), - __METHOD__, - array( 'path' => $path ) - ); - } - } + if ( ! is_wp_error( $path ) && $path ) { + $deleted = \Pattern_Builder_Security::safe_file_delete( $path ); } } - // Invalidate cache for this pattern - $this->invalidate_pattern_cache( $pattern->name ); - return $pattern; } @@ -689,12 +554,8 @@ public function get_block_patterns_from_theme_files() { $patterns = array(); foreach ( $pattern_files as $pattern_file ) { - // Validate each pattern file path - $validation = \Pattern_Builder_Security::validate_pattern_path( $pattern_file ); - if ( ! is_wp_error( $validation ) ) { - $pattern = Abstract_Pattern::from_file( $pattern_file ); - $patterns[] = $pattern; - } + $pattern = Abstract_Pattern::from_file( $pattern_file ); + $patterns[] = $pattern; } return $patterns; @@ -721,7 +582,7 @@ public function delete_user_pattern( Abstract_Pattern $pattern ) { ); } - $post = $this->get_page_by_path_secure( $pattern->name, OBJECT, 'wp_block' ); + $post = get_page_by_path( $pattern->name, OBJECT, 'wp_block' ); if ( empty( $post ) ) { return new WP_Error( 'pattern_not_found', 'Pattern not found', array( 'status' => 404 ) ); @@ -743,18 +604,7 @@ public function delete_user_pattern( Abstract_Pattern $pattern ) { * @return string|WP_Error Pattern file path on success, WP_Error on failure. */ public function get_pattern_filepath( $pattern ) { - $path = $pattern->filePath ?? get_stylesheet_directory() . '/patterns/' . \Pattern_Builder_Security::sanitize_filename( basename( $pattern->name ) ); - - // Validate the path before checking existence - $validation = \Pattern_Builder_Security::validate_pattern_path( $path ); - if ( is_wp_error( $validation ) ) { - return \Pattern_Builder_Security::create_error( - 'invalid_pattern_path', - __( 'Pattern file path validation failed.', 'pattern-builder' ), - array( 'status' => 400 ), - __METHOD__ - ); - } + $path = $pattern->filePath ?? get_stylesheet_directory() . '/patterns/' . sanitize_file_name( basename( $pattern->name ) ) . '.php'; if ( file_exists( $path ) ) { return $path; @@ -769,18 +619,13 @@ function ( $p ) use ( $pattern ) { ); if ( $pattern && isset( $pattern->filePath ) ) { - // Validate the found path as well - $validation = \Pattern_Builder_Security::validate_pattern_path( $pattern->filePath ); - if ( ! is_wp_error( $validation ) && file_exists( $pattern->filePath ) ) { - return $pattern->filePath; - } + return $pattern->filePath; } - return \Pattern_Builder_Security::create_error( + return new WP_Error( 'pattern_file_not_found', __( 'Pattern file not found.', 'pattern-builder' ), - array( 'status' => 404 ), - __METHOD__ + array( 'status' => 404 ) ); } @@ -800,11 +645,6 @@ public function delete_theme_pattern( Abstract_Pattern $pattern ) { return $path; // Return the error from get_pattern_filepath } - // Validate that the path is within the patterns directory - $validation = \Pattern_Builder_Security::validate_pattern_path( $path ); - if ( is_wp_error( $validation ) ) { - return $validation; - } // Use secure file delete operation $allowed_dirs = array( @@ -832,14 +672,8 @@ public function update_theme_pattern_file( Abstract_Pattern $pattern ) { // If get_pattern_filepath returns an error, create a new path if ( is_wp_error( $path ) ) { - $filename = \Pattern_Builder_Security::sanitize_filename( basename( $pattern->name ) ); - $path = get_stylesheet_directory() . '/patterns/' . $filename; - } - - // Validate that the path is within the patterns directory - $validation = \Pattern_Builder_Security::validate_pattern_path( $path ); - if ( is_wp_error( $validation ) ) { - return $validation; + $filename = sanitize_file_name( basename( $pattern->name ) ); + $path = get_stylesheet_directory() . '/patterns/' . $filename . '.php'; } $formatted_content = $this->format_block_markup( $pattern->content ); diff --git a/includes/class-pattern-builder-security.php b/includes/class-pattern-builder-security.php index 11666f0..6478672 100644 --- a/includes/class-pattern-builder-security.php +++ b/includes/class-pattern-builder-security.php @@ -301,88 +301,5 @@ public static function safe_file_move( $source, $destination, $allowed_dirs = ar return true; } - /** - * Sanitize a filename for safe use. - * - * @param string $filename The filename to sanitize. - * @return string Sanitized filename. - */ - public static function sanitize_filename( $filename ) { - // Use WordPress built-in sanitization. - $filename = sanitize_file_name( $filename ); - - // Remove any remaining directory traversal attempts. - $filename = str_replace( array( '..', '/', '\\' ), '', $filename ); - - // Ensure it has a valid extension for patterns. - if ( ! preg_match( '/\.(php|json|html?)$/i', $filename ) ) { - $filename .= '.php'; - } - - return $filename; - } - /** - * Log an error with context for debugging purposes. - * - * Respects WordPress debug settings and provides consistent error logging - * throughout the plugin. - * - * @param string $message The error message to log. - * @param string $context Optional. Context where the error occurred (method name, etc.). - * @param mixed $data Optional. Additional data to log (will be serialized). - * @param string $level Optional. Error level: 'error', 'warning', 'info', 'debug'. Default 'error'. - */ - public static function log_error( $message, $context = '', $data = null, $level = 'error' ) { - // Only log if WordPress debugging is enabled - if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) { - return; - } - - // Build the log message - $log_message = '[Pattern Builder] '; - - if ( ! empty( $context ) ) { - $log_message .= "[$context] "; - } - - $log_message .= $message; - - if ( ! is_null( $data ) ) { - $log_message .= ' | Data: ' . wp_json_encode( $data ); - } - - // Log to WordPress debug log if enabled - if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) { - error_log( $log_message ); - } - - // Also trigger WordPress action for extensibility - do_action( 'pattern_builder_log_error', $level, $message, $context, $data ); - } - - /** - * Create a standardized WP_Error with optional logging. - * - * @param string $code Error code. - * @param string $message Error message. - * @param array $data Optional. Error data array. - * @param string $context Optional. Context where error occurred. - * @param bool $log_error Optional. Whether to log this error. Default true. - * @return WP_Error - */ - public static function create_error( $code, $message, $data = array(), $context = '', $log_error = true ) { - if ( $log_error ) { - self::log_error( - $message, - $context, - array( - 'code' => $code, - 'data' => $data, - ) - ); - } - - return new WP_Error( $code, $message, $data ); - } } diff --git a/tests/php/test-pattern-builder-api.php b/tests/php/test-pattern-builder-api.php index 19c86de..c95342a 100644 --- a/tests/php/test-pattern-builder-api.php +++ b/tests/php/test-pattern-builder-api.php @@ -12,22 +12,38 @@ public function setUp(): void { $admin_id = self::factory()->user->create(['role' => 'administrator']); wp_set_current_user($admin_id); + $admin_user = wp_get_current_user(); - // Initialize the post type class to set up custom capabilities - new \TwentyBellows\PatternBuilder\Pattern_Builder_Post_Type(); - do_action('init'); - - // Directly grant capabilities to the test user - $admin_user = new WP_User($admin_id); + // Grant capabilities to the test user $admin_user->add_cap('read_tbell_pattern_block'); $admin_user->add_cap('edit_tbell_pattern_blocks'); $admin_user->add_cap('delete_tbell_pattern_blocks'); - + // Refresh current user to pick up new capabilities wp_cache_delete( $admin_id, 'users' ); wp_cache_delete( $admin_id, 'user_meta' ); wp_set_current_user( $admin_id ); + // remove all existing tbell_pattern_block posts + $all_tbell_pattern_block_posts = get_posts([ + 'post_type' => 'tbell_pattern_block', + 'numberposts' => -1, + 'post_status' => 'any', + ]); + foreach ($all_tbell_pattern_block_posts as $post) { + wp_delete_post($post->ID, true); + } + + // remove all existing wp_block posts + $all_wp_block_posts = get_posts([ + 'post_type' => 'wp_block', + 'numberposts' => -1, + 'post_status' => 'any', + ]); + foreach ($all_wp_block_posts as $post) { + wp_delete_post($post->ID, true); + } + // Create a temporary directory for the test patterns $this->test_dir = sys_get_temp_dir() . '/pattern-builder-test'; $this->remove_test_directory($this->test_dir); @@ -54,7 +70,7 @@ public function tearDown(): void { $admin_role->remove_cap('edit_tbell_pattern_blocks'); $admin_role->remove_cap('delete_tbell_pattern_blocks'); } - + $this->remove_test_directory($this->test_dir); remove_filter('stylesheet_directory', [$this, 'get_test_directory']); parent::tearDown(); From 306c1932188b9668ee7c9d632ac5092aecbc569f Mon Sep 17 00:00:00 2001 From: Jason Crist Date: Wed, 24 Sep 2025 16:48:00 -0400 Subject: [PATCH 2/2] Removed additional unused security methods --- includes/class-pattern-builder-security.php | 46 --------------------- 1 file changed, 46 deletions(-) diff --git a/includes/class-pattern-builder-security.php b/includes/class-pattern-builder-security.php index 6478672..200d4cc 100644 --- a/includes/class-pattern-builder-security.php +++ b/includes/class-pattern-builder-security.php @@ -83,52 +83,6 @@ public static function validate_file_path( $path, $allowed_dirs = array() ) { return true; } - /** - * Validate pattern file path specifically. - * - * @param string $path The pattern file path to validate. - * @return bool|WP_Error True if valid, WP_Error otherwise. - */ - public static function validate_pattern_path( $path ) { - // Pattern files should only be in the patterns directory. - $allowed_dirs = array( - wp_normalize_path( get_stylesheet_directory() . '/patterns' ), - wp_normalize_path( get_template_directory() . '/patterns' ), - ); - - $validation = self::validate_file_path( $path, $allowed_dirs ); - - if ( is_wp_error( $validation ) ) { - return $validation; - } - - // Ensure it's a PHP file. - if ( '.php' !== substr( $path, -4 ) ) { - return new WP_Error( - 'invalid_file_type', - __( 'Pattern files must be PHP files.', 'pattern-builder' ), - array( 'status' => 400 ) - ); - } - - return true; - } - - /** - * Validate asset file path. - * - * @param string $path The asset file path to validate. - * @return bool|WP_Error True if valid, WP_Error otherwise. - */ - public static function validate_asset_path( $path ) { - // Assets should only be in the assets directory. - $allowed_dirs = array( - wp_normalize_path( get_stylesheet_directory() . '/assets' ), - wp_normalize_path( get_template_directory() . '/assets' ), - ); - - return self::validate_file_path( $path, $allowed_dirs ); - } /** * Initialize WordPress Filesystem.