Commit 6904f47f25 for woocommerce

commit 6904f47f2578651459d069991967ba18913ba38c
Author: Chi-Hsuan Huang <chihsuan.tw@gmail.com>
Date:   Fri Apr 25 17:15:41 2025 +0800

    [Blueprint] Add permission validation for import steps (#57294)

    * Refactor ImportSchema and ImportStep classes to enhance permissions validation

    - Removed the built-in step processors from ImportSchema, simplifying the class structure.
    - Introduced a new ImportStep class to handle step processing, improving modularity.
    - Added capability checks in step processors to ensure users have the necessary permissions before executing steps.
    - Updated validation methods to return boolean values, enhancing clarity on validation outcomes.

    * Update ImportStepTest to extend WP_UnitTestCase and add capability validation test

    - Changed ImportStepTest to extend WP_UnitTestCase for better integration with WordPress testing framework.
    - Added a new test to validate error handling when user capabilities are insufficient for executing a step, enhancing test coverage and robustness of the import process.
    - Introduced a check_step_capabilities method in DummyImporter to support capability validation.

    * Fix function existence check in CliResultFormatter to use namespaced format_items function

    - Updated the function existence check for format_items to use the correct namespaced path '\WP_CLI\Utils\format_items'.
    - Reorganized the conditional logic to throw an exception if the function is not found before calling it, improving error handling and clarity.

    * Update ImportStep to use step definition in success result

    - Modified the import method to utilize the step definition's step property in the success result, enhancing clarity and ensuring the correct step context is used during the import process.

    * Add changefile(s) from automation for the following project(s): packages/php/blueprint

    * Refactor capability checks in ImportRunSql to check all permissions

    * Refactor capability check formatting in ImportDeletePlugin for improved readability

    * [Blueprint] Add permission validation for export steps and REST API (#57303)

    * Enhance ExportSchema and related exporters with capability checks and error handling

    - Updated the export method in ExportSchema to return WP_Error for invalid landing page paths instead of throwing exceptions, improving error handling.
    - Added check_step_capabilities method to various StepExporter implementations to validate user permissions before executing steps, ensuring only authorized users can perform exports.
    - Updated CLI export command to handle WP_Error responses gracefully, providing clear error messages to the user.
    - Enhanced documentation for methods to clarify expected return types and capabilities checks.

    * Refactor permission checks in RestApi for export and import actions

    - Updated permission callbacks for export and import methods to use specific checks: `check_export_permission` and `check_import_permission`.
    - Enhanced permission validation to ensure only users with appropriate capabilities can perform export and import actions, improving security and clarity in user permissions.

    * Improve error handling in ExportSchema and RestApi

    - Updated error message in ExportSchema to clarify the context of insufficient permissions during export steps.
    - Added handling for WP_Error responses in RestApi to return appropriate HTTP responses, enhancing API robustness and user feedback.

    * Improve error handling in slotfill.js for export functionality

    - Enhanced error handling in the Blueprint component to differentiate between various error types, including string errors and specific permission-related errors.
    - Added user-friendly error messages for insufficient permissions during export, improving clarity for users.
    - Ensured that the export functionality is re-enabled after error handling, maintaining a smooth user experience.

    * Enhance steps_payload_to_blueprint_steps method in RestApi

    - Added checks to ensure that 'settings', 'plugins', and 'themes' arrays are not only set but also contain elements before processing them, improving the robustness of the steps payload handling.

    * Refactor error handling in ExportSchema for consistency

    - Updated the ExportSchema class to use the WP_Error class consistently without the namespace prefix, improving code readability and maintaining consistency across error handling.
    - Clarified return type documentation for the export method to reflect the use of WP_Error, enhancing developer understanding of the method's behavior.

    * Initialize error state in exportBlueprint function in slotfill.js

    - Added a line to reset the error state to null before starting the export process, ensuring that previous errors do not affect the current export operation.

    * Add changelog

    * Fix tests

    * Enhance DummyExporter with strict types and additional methods

    - Added strict types declaration for improved type safety.
    - Introduced check_step_capabilities method to validate export capabilities.
    - Enhanced documentation for existing methods to clarify their purpose and return types, improving code readability and developer understanding.

    * Fix lint

    * [Blueprint] Implement logging for export and import operations (#57384)

    * Implement logging functionality in ExportSchema and ImportStep classes

    - Introduced a Logger class to handle logging of export and import steps, improving traceability and debugging.
    - Enhanced ExportSchema to log the start and completion of export steps, as well as errors encountered during the process.
    - Updated ImportStep to log the start and result of the import process, including success and error messages.
    - Refactored error handling in both classes to utilize the new logging system, ensuring consistent logging practices across the codebase.

    * Add changelog

    * Fix tests

    * Refactor Logger class to utilize WP functions

    - Introduced UseWPFunctions trait to encapsulate WordPress function usage.
    - Updated Logger class to use wp_get_current_user_id() for retrieving the current user's ID, enhancing consistency with WordPress standards.
    - Minor adjustment in ImportStepTest class to ensure proper namespace usage for WP_UnitTestCase.

    * Refactor logging in ExportSchema and ImportStep classes

    - Updated ExportSchema and ImportStep to utilize new logging methods in the Logger class for starting, completing, and handling failures of export and import steps.
    - Removed redundant logging code and centralized logging functionality in the Logger class, enhancing code clarity and maintainability.

    * Remove WC_Log_Levels and WC_Logger_Interface stubs; centralize logging in tests

    - Deleted the WC_Log_Levels and WC_Logger_Interface stub files to streamline the codebase.
    - Introduced a new stubs.php file to provide necessary stubs for testing, ensuring compatibility and reducing redundancy.
    - Updated references in the Logger class to use fully qualified class names for WC_Log_Levels, enhancing clarity and consistency.

    * Fix tests

    * Add changefile(s) from automation for the following project(s): packages/php/blueprint, woocommerce, woocommerce/client/admin

    ---------

    Co-authored-by: github-actions <github-actions@github.com>

diff --git a/packages/php/blueprint/changelog/57294-wooplug-2482-feature-blueprint-require-permission-based-on-the-steps b/packages/php/blueprint/changelog/57294-wooplug-2482-feature-blueprint-require-permission-based-on-the-steps
new file mode 100644
index 0000000000..2da1557574
--- /dev/null
+++ b/packages/php/blueprint/changelog/57294-wooplug-2482-feature-blueprint-require-permission-based-on-the-steps
@@ -0,0 +1,4 @@
+Significance: patch
+Type: enhancement
+
+Add permission validation for import steps
\ No newline at end of file
diff --git a/packages/php/blueprint/changelog/update-blueprint-require-permission-exporters-api b/packages/php/blueprint/changelog/update-blueprint-require-permission-exporters-api
new file mode 100644
index 0000000000..3cda0d466d
--- /dev/null
+++ b/packages/php/blueprint/changelog/update-blueprint-require-permission-exporters-api
@@ -0,0 +1,4 @@
+Significance: patch
+Type: enhancement
+
+Add permission validation for export steps and REST API
diff --git a/packages/php/blueprint/changelog/wooplug-3946-blueprint-centralized-audit-logging b/packages/php/blueprint/changelog/wooplug-3946-blueprint-centralized-audit-logging
new file mode 100644
index 0000000000..33633cf484
--- /dev/null
+++ b/packages/php/blueprint/changelog/wooplug-3946-blueprint-centralized-audit-logging
@@ -0,0 +1,4 @@
+Significance: patch
+Type: add
+
+Implement logging functionality in ExportSchema and ImportStep classes
diff --git a/packages/php/blueprint/src/ExportSchema.php b/packages/php/blueprint/src/ExportSchema.php
index b58f9d4bd8..fa7d532256 100644
--- a/packages/php/blueprint/src/ExportSchema.php
+++ b/packages/php/blueprint/src/ExportSchema.php
@@ -4,6 +4,8 @@ namespace Automattic\WooCommerce\Blueprint;

 use Automattic\WooCommerce\Blueprint\Exporters\StepExporter;
 use Automattic\WooCommerce\Blueprint\Exporters\HasAlias;
+use Automattic\WooCommerce\Blueprint\Logger;
+use Automattic\WooCommerce\Blueprint\Steps\Step;
 use WP_Error;

 /**
@@ -74,7 +76,6 @@ class ExportSchema {
 		 * @since 0.0.1
 		 */
 		$exporters = $this->wp_apply_filters( 'wooblueprint_exporters', array_merge( $this->exporters, $built_in_exporters ) );
-
 		// Validate that the exporters are instances of StepExporter.
 		$exporters = array_filter(
 			$exporters,
@@ -94,23 +95,31 @@ class ExportSchema {
 			}
 		}

-		/**
-		 * StepExporter.
-		 *
-		 * @var StepExporter $exporter
-		 */
+		// Make sure the user has the required capabilities to export the steps.
 		foreach ( $exporters as $exporter ) {
-			$this->publish( 'onBeforeExport', $exporter );
-			$step = $exporter->export();
-			if ( is_array( $step ) ) {
-				foreach ( $step as $_step ) {
-					$schema['steps'][] = $_step->get_json_array();
-				}
-			} else {
-				$schema['steps'][] = $step->get_json_array();
+			if ( ! $exporter->check_step_capabilities() ) {
+				return new WP_Error( 'wooblueprint_insufficient_permissions', 'Insufficient permissions to export for step: ' . $exporter->get_step_name() );
+			}
+		}
+
+		$logger = new Logger();
+		$logger->start_export( $exporters );
+
+		foreach ( $exporters as $exporter ) {
+			try {
+				$this->publish( 'onBeforeExport', $exporter );
+				$step = $exporter->export();
+				$this->add_result_to_schema( $schema, $step );
+
+			} catch ( \Throwable $e ) {
+				$step_name = $exporter instanceof HasAlias ? $exporter->get_alias() : $exporter->get_step_name();
+				$logger->export_step_failed( $step_name, $e );
+				return new WP_Error( 'wooblueprint_export_step_failed', 'Export step failed: ' . $e->getMessage() );
 			}
 		}

+		$logger->complete_export( $exporters );
+
 		return $schema;
 	}

@@ -130,4 +139,21 @@ class ExportSchema {
 			}
 		);
 	}
+
+	/**
+	 * Add export result to the schema array.
+	 *
+	 * @param array      $schema Schema array to add steps to.
+	 * @param array|Step $step   Step or array of steps to add.
+	 */
+	private function add_result_to_schema( array &$schema, $step ): void {
+		if ( is_array( $step ) ) {
+			foreach ( $step as $_step ) {
+				$schema['steps'][] = $_step->get_json_array();
+			}
+			return;
+		}
+
+		$schema['steps'][] = $step->get_json_array();
+	}
 }
diff --git a/packages/php/blueprint/src/Exporters/ExportInstallPluginSteps.php b/packages/php/blueprint/src/Exporters/ExportInstallPluginSteps.php
index c11bd652ae..48ca9bd49b 100644
--- a/packages/php/blueprint/src/Exporters/ExportInstallPluginSteps.php
+++ b/packages/php/blueprint/src/Exporters/ExportInstallPluginSteps.php
@@ -158,4 +158,12 @@ class ExportInstallPluginSteps implements StepExporter {
 	public function get_step_name() {
 		return InstallPlugin::get_step_name();
 	}
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities(): bool {
+		return current_user_can( 'activate_plugins' );
+	}
 }
diff --git a/packages/php/blueprint/src/Exporters/ExportInstallThemeSteps.php b/packages/php/blueprint/src/Exporters/ExportInstallThemeSteps.php
index db9ee4aeeb..dbd975128f 100644
--- a/packages/php/blueprint/src/Exporters/ExportInstallThemeSteps.php
+++ b/packages/php/blueprint/src/Exporters/ExportInstallThemeSteps.php
@@ -80,4 +80,13 @@ class ExportInstallThemeSteps implements StepExporter {
 	public function get_step_name() {
 		return InstallTheme::get_step_name();
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities(): bool {
+		return current_user_can( 'switch_themes' );
+	}
 }
diff --git a/packages/php/blueprint/src/Exporters/StepExporter.php b/packages/php/blueprint/src/Exporters/StepExporter.php
index ddbd5f309a..4e263f5b03 100644
--- a/packages/php/blueprint/src/Exporters/StepExporter.php
+++ b/packages/php/blueprint/src/Exporters/StepExporter.php
@@ -24,4 +24,11 @@ interface StepExporter {
 	 * @return string
 	 */
 	public function get_step_name();
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities(): bool;
 }
diff --git a/packages/php/blueprint/src/ImportSchema.php b/packages/php/blueprint/src/ImportSchema.php
index f1c913a7a7..0b0c16fc79 100644
--- a/packages/php/blueprint/src/ImportSchema.php
+++ b/packages/php/blueprint/src/ImportSchema.php
@@ -30,12 +30,6 @@ class ImportSchema {
 	 */
 	private Validator $validator;

-	/**
-	 * Built-in step processors.
-	 *
-	 * @var BuiltInStepProcessors The built-in step processors instance.
-	 */
-	private BuiltInStepProcessors $builtin_step_processors;

 	/**
 	 * ImportSchema constructor.
@@ -50,8 +44,6 @@ class ImportSchema {
 		}

 		$this->validator = $validator;
-
-		$this->builtin_step_processors = new BuiltInStepProcessors();
 	}

 	/**
@@ -99,89 +91,11 @@ class ImportSchema {
 		$result    = StepProcessorResult::success( 'ImportSchema' );
 		$results[] = $result;

-		$step_processors = $this->builtin_step_processors->get_all();
-
-		/**
-		 * Filters the step processors.
-		 *
-		 * Allows adding/removing custom step processors.
-		 *
-		 * @param StepProcessor[] $step_processors The step processors.
-		 *
-		 * @since 0.0.1
-		 */
-		$step_processors = $this->wp_apply_filters( 'wooblueprint_importers', $step_processors );
-
-		// Validate that the step processors are instances of StepProcessor.
-		$step_processors = array_filter(
-			$step_processors,
-			function ( $step_processor ) {
-				return $step_processor instanceof StepProcessor;
-			}
-		);
-
-		$indexed_step_processors = Util::index_array(
-			$step_processors,
-			function ( $key, $step_processor ) {
-				return $step_processor->get_step_class()::get_step_name();
-			}
-		);
-
-		// validate steps before processing.
-		$this->validate_step_schemas( $indexed_step_processors, $result );
-
-		if ( count( $result->get_messages( 'error' ) ) !== 0 ) {
-			return $results;
-		}
-
 		foreach ( $this->schema->get_steps() as $step_schema ) {
-			$step_processor = $indexed_step_processors[ $step_schema->step ] ?? null;
-			if ( ! $step_processor instanceof StepProcessor ) {
-				$result->add_error( "Unable to create a step processor for {$step_schema->step}" );
-				continue;
-			}
-
-			$results[] = $step_processor->process( $step_schema );
+			$step_importer = new ImportStep( $step_schema, $this->validator );
+			$results[]     = $step_importer->import();
 		}

 		return $results;
 	}
-
-	/**
-	 * Validate the step schemas.
-	 *
-	 * @param array               $indexed_step_processors Array of step processors indexed by step name.
-	 * @param StepProcessorResult $result The result object to add messages to.
-	 *
-	 * @return void
-	 */
-	protected function validate_step_schemas( array $indexed_step_processors, StepProcessorResult $result ) {
-		$step_schemas = array_map(
-			function ( $step_processor ) {
-				return $step_processor->get_step_class()::get_schema();
-			},
-			$indexed_step_processors
-		);
-
-		foreach ( $this->schema->get_steps() as $step_json ) {
-			$step_schema = $step_schemas[ $step_json->step ] ?? null;
-			if ( ! $step_schema ) {
-				$result->add_info( "No schema found for step $step_json->step" );
-				continue;
-			}
-
-			$validate = $this->validator->validate( $step_json, wp_json_encode( $step_schema ) );
-
-			if ( ! $validate->isValid() ) {
-				$result->add_error( "Schema validation failed for step {$step_json->step}" );
-				$errors           = ( new ErrorFormatter() )->format( $validate->error() );
-				$formatted_errors = array();
-				foreach ( $errors as $value ) {
-					$formatted_errors[] = implode( "\n", $value );
-				}
-
-				$result->add_error( implode( "\n", $formatted_errors ) );
-			}
-		}
-	}
 }
diff --git a/packages/php/blueprint/src/ImportStep.php b/packages/php/blueprint/src/ImportStep.php
index 65b1af972a..c7f28ce6f9 100644
--- a/packages/php/blueprint/src/ImportStep.php
+++ b/packages/php/blueprint/src/ImportStep.php
@@ -4,6 +4,7 @@ namespace Automattic\WooCommerce\Blueprint;

 use Opis\JsonSchema\Errors\ErrorFormatter;
 use Opis\JsonSchema\Validator;
+use Automattic\WooCommerce\Blueprint\Logger;

 /**
  * Class ImportStep
@@ -78,31 +79,63 @@ class ImportStep {
 	 * @return StepProcessorResult
 	 */
 	public function import() {
-		$result = StepProcessorResult::success( 'ImportStep' );
+		$result = StepProcessorResult::success( $this->step_definition->step );

-		if ( ! isset( $this->indexed_importers[ $this->step_definition->step ] ) ) {
-			$result->add_warn( "Unable to find an importer for {$this->step_definition->step}" );
+		if ( ! $this->can_import( $result ) ) {
 			return $result;
 		}

 		$importer = $this->indexed_importers[ $this->step_definition->step ];
+		$logger   = new Logger();
+		$logger->start_import( $this->step_definition->step, get_class( $importer ) );

-		// validate importer is a step processor before processing.
-		if ( ! $importer instanceof StepProcessor ) {
-			$result->add_warn( "Importer {$this->step_definition->step} is not a valid step processor" );
-			return $result;
+		$importer_result = $importer->process( $this->step_definition );
+
+		if ( $importer_result->is_success() ) {
+			$logger->complete_import( $this->step_definition->step, $importer_result );
+		} else {
+			$logger->import_step_failed( $this->step_definition->step, $importer_result );
 		}

-		// validate steps before processing.
-		$this->validate_step_schemas( $importer, $result );
+		$result->merge_messages( $importer_result );

-		if ( count( $result->get_messages( 'error' ) ) !== 0 ) {
-			return $result;
+		return $result;
+	}
+
+	/**
+	 * Check if the step can be imported.
+	 *
+	 * @param StepProcessorResult $result The result object to add messages to.
+	 *
+	 * @return bool True if the step can be imported, false otherwise.
+	 */
+	protected function can_import( &$result ) {
+		// Check if the importer exists.
+		if ( ! isset( $this->indexed_importers[ $this->step_definition->step ] ) ) {
+			$result->add_error( 'Unable to find an importer' );
+			return false;
 		}

-		$result->merge_messages( $importer->process( $this->step_definition ) );
+		$importer = $this->indexed_importers[ $this->step_definition->step ];
+		// Validate importer is a step processor before processing.
+		if ( ! $importer instanceof StepProcessor ) {
+			$result->add_error( 'Incorrect importer type' );
+			return false;
+		}

-		return $result;
+		// Validate steps schemas before processing.
+		if ( ! $this->validate_step_schemas( $importer, $result ) ) {
+			$result->add_error( 'Schema validation failed for step' );
+			return false;
+		}
+
+		// Validate step capabilities before processing.
+		if ( ! $importer->check_step_capabilities( $this->step_definition ) ) {
+			$result->add_error( 'User does not have the required capabilities to run step' );
+			return false;
+		}
+
+		return true;
 	}

 	/**
@@ -111,7 +144,7 @@ class ImportStep {
 	 * @param StepProcessor       $importer The importer.
 	 * @param StepProcessorResult $result The result object to add messages to.
 	 *
-	 * @return void
+	 * @return bool True if the step schemas are valid, false otherwise.
 	 */
 	protected function validate_step_schemas( StepProcessor $importer, StepProcessorResult $result ) {
 		$step_schema = call_user_func( array( $importer->get_step_class(), 'get_schema' ) );
@@ -127,6 +160,9 @@ class ImportStep {
 			}

 			$result->add_error( implode( "\n", $formatted_errors ) );
+
+			return false;
 		}
+		return true;
 	}
 }
diff --git a/packages/php/blueprint/src/Importers/ImportActivatePlugin.php b/packages/php/blueprint/src/Importers/ImportActivatePlugin.php
index e475b5cb31..53940502c8 100644
--- a/packages/php/blueprint/src/Importers/ImportActivatePlugin.php
+++ b/packages/php/blueprint/src/Importers/ImportActivatePlugin.php
@@ -45,4 +45,15 @@ class ImportActivatePlugin implements StepProcessor {
 	public function get_step_class(): string {
 		return ActivatePlugin::class;
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @param object $schema The schema to process.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities( $schema ): bool {
+		return current_user_can( 'activate_plugins' );
+	}
 }
diff --git a/packages/php/blueprint/src/Importers/ImportActivateTheme.php b/packages/php/blueprint/src/Importers/ImportActivateTheme.php
index 286c964a39..d046c944a8 100644
--- a/packages/php/blueprint/src/Importers/ImportActivateTheme.php
+++ b/packages/php/blueprint/src/Importers/ImportActivateTheme.php
@@ -4,7 +4,6 @@ namespace Automattic\WooCommerce\Blueprint\Importers;

 use Automattic\WooCommerce\Blueprint\StepProcessor;
 use Automattic\WooCommerce\Blueprint\StepProcessorResult;
-use Automattic\WooCommerce\Blueprint\Steps\ActivatePlugin;
 use Automattic\WooCommerce\Blueprint\Steps\ActivateTheme;
 use Automattic\WooCommerce\Blueprint\UsePluginHelpers;
 use Automattic\WooCommerce\Blueprint\UseWPFunctions;
@@ -49,4 +48,15 @@ class ImportActivateTheme implements StepProcessor {
 	public function get_step_class(): string {
 		return ActivateTheme::class;
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @param object $schema The schema to process.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities( $schema ): bool {
+		return current_user_can( 'switch_themes' );
+	}
 }
diff --git a/packages/php/blueprint/src/Importers/ImportDeactivatePlugin.php b/packages/php/blueprint/src/Importers/ImportDeactivatePlugin.php
index c3e589693b..fd1b1d7ab1 100644
--- a/packages/php/blueprint/src/Importers/ImportDeactivatePlugin.php
+++ b/packages/php/blueprint/src/Importers/ImportDeactivatePlugin.php
@@ -44,4 +44,15 @@ class ImportDeactivatePlugin implements StepProcessor {
 	public function get_step_class(): string {
 		return DeactivatePlugin::class;
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @param object $schema The schema to process.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities( $schema ): bool {
+		return current_user_can( 'deactivate_plugins' );
+	}
 }
diff --git a/packages/php/blueprint/src/Importers/ImportDeletePlugin.php b/packages/php/blueprint/src/Importers/ImportDeletePlugin.php
index a45d5a8bc1..4a8cc2d812 100644
--- a/packages/php/blueprint/src/Importers/ImportDeletePlugin.php
+++ b/packages/php/blueprint/src/Importers/ImportDeletePlugin.php
@@ -44,4 +44,15 @@ class ImportDeletePlugin implements StepProcessor {
 	public function get_step_class(): string {
 		return DeletePlugin::class;
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @param object $schema The schema to process.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities( $schema ): bool {
+		return current_user_can( 'deactivate_plugins' ) && current_user_can( 'delete_plugins' );
+	}
 }
diff --git a/packages/php/blueprint/src/Importers/ImportInstallPlugin.php b/packages/php/blueprint/src/Importers/ImportInstallPlugin.php
index 4c969544d6..166c5c664b 100644
--- a/packages/php/blueprint/src/Importers/ImportInstallPlugin.php
+++ b/packages/php/blueprint/src/Importers/ImportInstallPlugin.php
@@ -171,4 +171,15 @@ class ImportInstallPlugin implements StepProcessor {
 	public function get_step_class(): string {
 		return InstallPlugin::class;
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @param object $schema The schema to process.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities( $schema ): bool {
+		return current_user_can( 'install_plugins' );
+	}
 }
diff --git a/packages/php/blueprint/src/Importers/ImportInstallTheme.php b/packages/php/blueprint/src/Importers/ImportInstallTheme.php
index 4ad2e1836f..4233cf170a 100644
--- a/packages/php/blueprint/src/Importers/ImportInstallTheme.php
+++ b/packages/php/blueprint/src/Importers/ImportInstallTheme.php
@@ -7,7 +7,6 @@ use Automattic\WooCommerce\Blueprint\StepProcessor;
 use Automattic\WooCommerce\Blueprint\StepProcessorResult;
 use Automattic\WooCommerce\Blueprint\Steps\InstallTheme;
 use Automattic\WooCommerce\Blueprint\UseWPFunctions;
-use Plugin_Upgrader;

 /**
  * Class ImportInstallTheme
@@ -144,4 +143,15 @@ class ImportInstallTheme implements StepProcessor {
 	public function get_step_class(): string {
 		return InstallTheme::class;
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @param object $schema The schema to process.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities( $schema ): bool {
+		return current_user_can( 'install_themes' );
+	}
 }
diff --git a/packages/php/blueprint/src/Importers/ImportRunSql.php b/packages/php/blueprint/src/Importers/ImportRunSql.php
index d79b7ad57b..3a2fb889bb 100644
--- a/packages/php/blueprint/src/Importers/ImportRunSql.php
+++ b/packages/php/blueprint/src/Importers/ImportRunSql.php
@@ -4,8 +4,6 @@ namespace Automattic\WooCommerce\Blueprint\Importers;

 use Automattic\WooCommerce\Blueprint\StepProcessor;
 use Automattic\WooCommerce\Blueprint\StepProcessorResult;
-use Automattic\WooCommerce\Blueprint\Steps\ActivatePlugin;
-use Automattic\WooCommerce\Blueprint\Steps\ActivateTheme;
 use Automattic\WooCommerce\Blueprint\Steps\RunSql;
 use Automattic\WooCommerce\Blueprint\UsePluginHelpers;
 use Automattic\WooCommerce\Blueprint\UseWPFunctions;
@@ -49,4 +47,27 @@ class ImportRunSql implements StepProcessor {
 	public function get_step_class(): string {
 		return RunSql::class;
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @param object $schema The schema to process.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities( $schema ): bool {
+		if ( ! current_user_can( 'manage_options' ) ) {
+			return false;
+		}
+
+		if ( ! current_user_can( 'edit_posts' ) ) {
+			return false;
+		}
+
+		if ( ! current_user_can( 'edit_users' ) ) {
+			return false;
+		}
+
+		return true;
+	}
 }
diff --git a/packages/php/blueprint/src/Importers/ImportSetSiteOptions.php b/packages/php/blueprint/src/Importers/ImportSetSiteOptions.php
index 7d0dab845f..53cd70e791 100644
--- a/packages/php/blueprint/src/Importers/ImportSetSiteOptions.php
+++ b/packages/php/blueprint/src/Importers/ImportSetSiteOptions.php
@@ -51,4 +51,15 @@ class ImportSetSiteOptions implements StepProcessor {
 	public function get_step_class(): string {
 		return SetSiteOptions::class;
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @param object $schema The schema to process.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities( $schema ): bool {
+		return current_user_can( 'manage_options' );
+	}
 }
diff --git a/packages/php/blueprint/src/Logger.php b/packages/php/blueprint/src/Logger.php
new file mode 100644
index 0000000000..e50c1d92bf
--- /dev/null
+++ b/packages/php/blueprint/src/Logger.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace Automattic\WooCommerce\Blueprint;
+
+use Automattic\WooCommerce\Blueprint\UseWPFunctions;
+
+/**
+ * Class Logger
+ */
+class Logger {
+	use UseWPFunctions;
+
+	/**
+	 * WooCommerce logger class instance.
+	 *
+	 * @var \WC_Logger_Interface
+	 */
+	private $logger;
+
+	/**
+	 * Constructor.
+	 */
+	public function __construct() {
+		$this->logger = wc_get_logger();
+	}
+
+	/**
+	 * Log a message as a debug log entry.
+	 *
+	 * @param string $message The message to log.
+	 * @param string $level   The log level.
+	 * @param array  $context The context of the log.
+	 */
+	public function log( string $message, string $level = \WC_Log_Levels::DEBUG, $context = array() ) {
+		$this->logger->log(
+			$level,
+			$message,
+			array_merge(
+				array(
+					'source'  => 'wc-blueprint',
+					'user_id' => $this->wp_get_current_user_id(),
+				),
+				$context
+			)
+		);
+	}
+
+	/**
+	 * Log the start of an export operation.
+	 *
+	 * @param array $exporters Array of exporters.
+	 */
+	public function start_export( array $exporters ) {
+		$export_data = $this->get_export_data( $exporters );
+
+		$this->log(
+			sprintf( 'Starting export of %d steps', count( $export_data['steps'] ) ),
+			\WC_Log_Levels::INFO,
+			array(
+				'steps'     => $export_data['steps'],
+				'exporters' => $export_data['exporters'],
+			)
+		);
+	}
+
+	/**
+	 * Log the completion of an export operation.
+	 *
+	 * @param array $exporters Array of exporters.
+	 */
+	public function complete_export( array $exporters ) {
+		$export_data = $this->get_export_data( $exporters );
+
+		$this->log(
+			sprintf( 'Export of %d steps completed', count( $export_data['steps'] ) ),
+			\WC_Log_Levels::INFO,
+			array(
+				'steps'     => $export_data['steps'],
+				'exporters' => $export_data['exporters'],
+			)
+		);
+	}
+
+	/**
+	 * Extract export step names and exporter classes from exporters.
+	 *
+	 * @param array $exporters Array of exporters.
+	 * @return array Associative array with 'steps' and 'exporters' keys.
+	 */
+	private function get_export_data( array $exporters ) {
+		$export_steps     = array();
+		$exporter_classes = array();
+
+		foreach ( $exporters as $exporter ) {
+			$step_name          = method_exists( $exporter, 'get_alias' ) ? $exporter->get_alias() : $exporter->get_step_name();
+			$export_steps[]     = $step_name;
+			$exporter_classes[] = get_class( $exporter );
+		}
+
+		return array(
+			'steps'     => $export_steps,
+			'exporters' => $exporter_classes,
+		);
+	}
+
+	/**
+	 * Log an export step failure.
+	 *
+	 * @param string     $step_name The name of the step that failed.
+	 * @param \Throwable $exception The exception that was thrown.
+	 */
+	public function export_step_failed( string $step_name, \Throwable $exception ) {
+		$this->log(
+			sprintf( 'Export "%s" step failed', $step_name ),
+			\WC_Log_Levels::ERROR,
+			array(
+				'error' => $exception->getMessage(),
+			)
+		);
+	}
+
+	/**
+	 * Log the start of an import step.
+	 *
+	 * @param string $step_name      The name of the step being imported.
+	 * @param string $importer_class The class name of the importer.
+	 */
+	public function start_import( string $step_name, string $importer_class ) {
+		$this->log(
+			sprintf( 'Starting import "%s" step', $step_name ),
+			\WC_Log_Levels::INFO,
+			array(
+				'importer' => $importer_class,
+			)
+		);
+	}
+
+	/**
+	 * Log the successful completion of an import step.
+	 *
+	 * @param string              $step_name The name of the step that was imported.
+	 * @param StepProcessorResult $result    The result of the import.
+	 */
+	public function complete_import( string $step_name, StepProcessorResult $result ) {
+		$this->log(
+			sprintf( 'Import "%s" step completed', $step_name ),
+			\WC_Log_Levels::INFO,
+			array(
+				'messages' => $result->get_messages( 'info' ),
+			)
+		);
+	}
+
+	/**
+	 * Log an import step failure.
+	 *
+	 * @param string              $step_name The name of the step that failed.
+	 * @param StepProcessorResult $result    The result of the import.
+	 */
+	public function import_step_failed( string $step_name, StepProcessorResult $result ) {
+		$this->log(
+			sprintf( 'Import "%s" step failed', $step_name ),
+			\WC_Log_Levels::ERROR,
+			array(
+				'messages' => $result->get_messages( 'error' ),
+			)
+		);
+	}
+}
diff --git a/packages/php/blueprint/src/ResultFormatters/CliResultFormatter.php b/packages/php/blueprint/src/ResultFormatters/CliResultFormatter.php
index 9f063f4474..a85cf49109 100644
--- a/packages/php/blueprint/src/ResultFormatters/CliResultFormatter.php
+++ b/packages/php/blueprint/src/ResultFormatters/CliResultFormatter.php
@@ -49,13 +49,13 @@ class CliResultFormatter {
 			}
 		}

-		$format_items_exist = function_exists( 'format_items' );
+		$format_items_exist = function_exists( '\WP_CLI\Utils\format_items' );

-		if ( $format_items_exist ) {
-			format_items( 'table', $items, $header );
-		} else {
+		if ( ! $format_items_exist ) {
 			throw new \Exception( 'WP CLI Utils not found' );
 		}
+
+		format_items( 'table', $items, $header );
 	}

 	/**
diff --git a/packages/php/blueprint/src/StepProcessor.php b/packages/php/blueprint/src/StepProcessor.php
index a97f62e5f6..a61743c10d 100644
--- a/packages/php/blueprint/src/StepProcessor.php
+++ b/packages/php/blueprint/src/StepProcessor.php
@@ -21,4 +21,12 @@ interface StepProcessor {
 	 * @return string
 	 */
 	public function get_step_class(): string;
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @param object $schema The schema to process.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities( $schema ): bool;
 }
diff --git a/packages/php/blueprint/src/UseWPFunctions.php b/packages/php/blueprint/src/UseWPFunctions.php
index 27e38a1039..be0cae7b0d 100644
--- a/packages/php/blueprint/src/UseWPFunctions.php
+++ b/packages/php/blueprint/src/UseWPFunctions.php
@@ -311,4 +311,13 @@ trait UseWPFunctions {

 		return $wp_filesystem->get_contents( $file_path );
 	}
+
+	/**
+	 * Retrieves the current user's ID.
+	 *
+	 * @return int The current user's ID.
+	 */
+	public function wp_get_current_user_id() {
+		return get_current_user_id();
+	}
 }
diff --git a/packages/php/blueprint/tests/Unit/ExportSchemaTest.php b/packages/php/blueprint/tests/Unit/ExportSchemaTest.php
index 68a0179868..f6b3473d36 100644
--- a/packages/php/blueprint/tests/Unit/ExportSchemaTest.php
+++ b/packages/php/blueprint/tests/Unit/ExportSchemaTest.php
@@ -148,4 +148,20 @@ class ExportSchemaTest extends TestCase {
 		$this->assertCount( 1, $result['steps'] );
 		$this->assertEquals( 'setSiteOptions', $result['steps'][0]['step'] );
 	}
+
+	/**
+	 * Test that it returns a WP_Error when the exporter is not capable.
+	 */
+	public function test_it_returns_wp_error_when_exporter_is_not_capable() {
+		$exporter = Mockery::mock( EmptySetSiteOptionsExporter::class );
+		$exporter->makePartial();
+		$exporter->shouldReceive( 'check_step_capabilities' )
+			->andReturn( false );
+
+		$mock = Mock( ExportSchema::class, array( array( $exporter ) ) );
+		$mock->makePartial();
+
+		$result = $mock->export();
+		$this->assertInstanceOf( WP_Error::class, $result );
+	}
 }
diff --git a/packages/php/blueprint/tests/Unit/ImportStepTest.php b/packages/php/blueprint/tests/Unit/ImportStepTest.php
index ad2714f662..d76f171daf 100644
--- a/packages/php/blueprint/tests/Unit/ImportStepTest.php
+++ b/packages/php/blueprint/tests/Unit/ImportStepTest.php
@@ -2,14 +2,13 @@

 use Automattic\WooCommerce\Blueprint\Tests\stubs\Importers\DummyImporter;
 use Automattic\WooCommerce\Blueprint\Tests\stubs\Steps\DummyStep;
-use PHPUnit\Framework\TestCase;
 use Automattic\WooCommerce\Blueprint\ImportStep;
 use Automattic\WooCommerce\Blueprint\StepProcessorResult;

 /**
  * Class ImportStepTest
  */
-class ImportStepTest extends TestCase {
+class ImportStepTest extends \WP_UnitTestCase {

 	/**
 	 * Tear down Mockery after each test.
@@ -51,21 +50,21 @@ class ImportStepTest extends TestCase {
 	 *
 	 * @return void
 	 */
-	public function test_it_returns_warn_when_it_cannot_find_valid_importer() {
+	public function test_it_returns_error_when_it_cannot_find_valid_importer() {
 		$rand     = wp_rand( 1, 99999999 );
 		$importer = new ImportStep( (object) array( 'step' => 'dummy' . $rand ) );
 		$result   = $importer->import();

-		$this->assertCount( 1, $result->get_messages( 'warn' ) );
-		$this->assertEquals( 'Unable to find an importer for dummy' . $rand, $result->get_messages( 'warn' )[0]['message'] );
+		$this->assertCount( 1, $result->get_messages( 'error' ) );
+		$this->assertEquals( 'Unable to find an importer', $result->get_messages( 'error' )[0]['message'] );
 	}

 	/**
-	 * Test it returns warn when importer is not a step processor.
+	 * Test it returns error when importer is not a step processor.
 	 *
 	 * @return void
 	 */
-	public function test_it_returns_warn_when_importer_is_not_a_step_processor() {
+	public function test_it_returns_error_when_importer_is_not_a_step_processor() {
 		// Create a filter that adds an invalid importer (not implementing StepProcessor).
 		add_filter(
 			'wooblueprint_importers',
@@ -87,8 +86,8 @@ class ImportStepTest extends TestCase {
 		$importer = new ImportStep( (object) array( 'step' => DummyStep::get_step_name() ) );
 		$result   = $importer->import();

-		$this->assertCount( 1, $result->get_messages( 'warn' ) );
-		$this->assertEquals( sprintf( 'Importer %s is not a valid step processor', DummyStep::get_step_name() ), $result->get_messages( 'warn' )[0]['message'] );
+		$this->assertCount( 1, $result->get_messages( 'error' ) );
+		$this->assertEquals( 'Incorrect importer type', $result->get_messages( 'error' )[0]['message'] );
 	}

 	/**
@@ -108,4 +107,21 @@ class ImportStepTest extends TestCase {
 		$this->assertNotEmpty( $result->get_messages( 'error' ) );
 		$this->assertEquals( 'Schema validation failed for step dummy', $result->get_messages( 'error' )[0]['message'] );
 	}
+
+	/**
+	 * Test it returns error when step capabilities are not valid.
+	 *
+	 * @return void
+	 */
+	public function test_it_returns_error_when_step_capabilities_are_not_valid() {
+		// create a user with editor role using wp native function.
+		$user_id = $this->factory->user->create( array( 'role' => 'editor' ) );
+
+		wp_set_current_user( $user_id );
+
+		$importer = new ImportStep( (object) array( 'step' => 'setSiteOptions' ) );
+		$result   = $importer->import();
+		$this->assertNotEmpty( $result->get_messages( 'error' ) );
+		$this->assertEquals( 'User does not have the required capabilities to run step', $result->get_messages( 'error' )[0]['message'] );
+	}
 }
diff --git a/packages/php/blueprint/tests/bootstrap.php b/packages/php/blueprint/tests/bootstrap.php
index 97fdc8f1f9..9f36108740 100644
--- a/packages/php/blueprint/tests/bootstrap.php
+++ b/packages/php/blueprint/tests/bootstrap.php
@@ -19,3 +19,9 @@ require 'vendor/yoast/phpunit-polyfills/phpunitpolyfills-autoload.php';

 // Start up the WP testing environment.
 require "{$_tests_dir}/includes/bootstrap.php";
+
+
+define( 'WOO_BLUEPRINT_TESTS', true );
+
+// Stubs.
+require_once __DIR__ . '/stubs/stubs.php';
diff --git a/packages/php/blueprint/tests/stubs/Exporters/EmptySetSiteOptionsExporter.php b/packages/php/blueprint/tests/stubs/Exporters/EmptySetSiteOptionsExporter.php
index f7596b29d4..fa075e872c 100644
--- a/packages/php/blueprint/tests/stubs/Exporters/EmptySetSiteOptionsExporter.php
+++ b/packages/php/blueprint/tests/stubs/Exporters/EmptySetSiteOptionsExporter.php
@@ -28,4 +28,13 @@ class EmptySetSiteOptionsExporter implements StepExporter {
 	public function get_step_name() {
 		return SetSiteOptions::get_step_name();
 	}
+
+	/**
+	 * Check if the step is capable of being exported.
+	 *
+	 * @return bool True if the step is capable of being exported, false otherwise.
+	 */
+	public function check_step_capabilities(): bool {
+		return true;
+	}
 }
diff --git a/packages/php/blueprint/tests/stubs/Importers/DummyImporter.php b/packages/php/blueprint/tests/stubs/Importers/DummyImporter.php
index 19abe8f70e..6b4559fcf8 100644
--- a/packages/php/blueprint/tests/stubs/Importers/DummyImporter.php
+++ b/packages/php/blueprint/tests/stubs/Importers/DummyImporter.php
@@ -28,4 +28,14 @@ class DummyImporter implements StepProcessor {
 	public function get_step_class(): string {
 		return DummyStep::class;
 	}
+
+	/**
+	 * Check the step capabilities.
+	 *
+	 * @param object $schema The schema to check.
+	 * @return bool True if the step capabilities are valid.
+	 */
+	public function check_step_capabilities( $schema ): bool {
+		return true;
+	}
 }
diff --git a/packages/php/blueprint/tests/stubs/stubs.php b/packages/php/blueprint/tests/stubs/stubs.php
new file mode 100644
index 0000000000..c529280cc3
--- /dev/null
+++ b/packages/php/blueprint/tests/stubs/stubs.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * Stubs for WooCommerce classes and interfaces.
+ */
+
+// This file should only be loaded during test runs to prevent conflicts in development environments.
+// During local development, files are copied to the WooCommerce vendor directory where the autoloader might attempt to load this file.
+if ( defined( 'WOO_BLUEPRINT_TESTS' ) ) {
+
+	if ( ! class_exists( 'WC_Log_Levels', false ) ) {
+		/**
+		 * WC Log Levels Class
+		 */
+		class WC_Log_Levels {
+			const EMERGENCY = 'emergency';
+			const ALERT     = 'alert';
+			const CRITICAL  = 'critical';
+			const ERROR     = 'error';
+			const WARNING   = 'warning';
+			const NOTICE    = 'notice';
+			const INFO      = 'info';
+			const DEBUG     = 'debug';
+		}
+	}
+
+// phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
+	if ( ! interface_exists( 'WC_Logger_Interface' ) ) {
+		/**
+		 * WC Logger Interface
+		 */
+		interface WC_Logger_Interface {
+			/**
+			 * Log message with level.
+			 *
+			 * @param string $level Log level.
+			 * @param string $message Log message.
+			 * @param array  $context Optional. Additional information for log handlers.
+			 */
+			public function log( $level, $message, $context = array() );
+
+			/**
+			 * Add a log entry.
+			 *
+			 * @param string $handle Log handle.
+			 * @param string $message Log message.
+			 * @param string $level Log level.
+			 */
+			public function add( $handle, $message, $level = 'notice' );
+
+			/**
+			 * Add an emergency level message.
+			 *
+			 * @param string $message Log message.
+			 * @param array  $context Log context.
+			 */
+			public function emergency( $message, $context = array() );
+
+			/**
+			 * Add an alert level message.
+			 *
+			 * @param string $message Log message.
+			 * @param array  $context Log context.
+			 */
+			public function alert( $message, $context = array() );
+
+			/**
+			 * Add a critical level message.
+			 *
+			 * @param string $message Log message.
+			 * @param array  $context Log context.
+			 */
+			public function critical( $message, $context = array() );
+
+			/**
+			 * Add an error level message.
+			 *
+			 * @param string $message Log message.
+			 * @param array  $context Log context.
+			 */
+			public function error( $message, $context = array() );
+
+			/**
+			 * Add a warning level message.
+			 *
+			 * @param string $message Log message.
+			 * @param array  $context Log context.
+			 */
+			public function warning( $message, $context = array() );
+
+			/**
+			 * Add a notice level message.
+			 *
+			 * @param string $message Log message.
+			 * @param array  $context Log context.
+			 */
+			public function notice( $message, $context = array() );
+
+			/**
+			 * Add an info level message.
+			 *
+			 * @param string $message Log message.
+			 * @param array  $context Log context.
+			 */
+			public function info( $message, $context = array() );
+
+			/**
+			 * Add a debug level message.
+			 *
+			 * @param string $message Log message.
+			 * @param array  $context Log context.
+			 */
+			public function debug( $message, $context = array() );
+		}
+	}
+
+	if ( ! function_exists( 'wc_get_logger' ) ) {
+		/**
+		 * Mock wc_get_logger function.
+		 *
+		 * @return WC_Logger_Interface
+		 */
+		function wc_get_logger() { // phpcs:ignore Universal.Files.SeparateFunctionsFromOO.Mixed
+			return Mockery::mock( 'WC_Logger_Interface' )->shouldReceive( 'log' )->getMock();
+		}
+	}
+}
diff --git a/plugins/woocommerce/changelog/57294-wooplug-2482-feature-blueprint-require-permission-based-on-the-steps b/plugins/woocommerce/changelog/57294-wooplug-2482-feature-blueprint-require-permission-based-on-the-steps
new file mode 100644
index 0000000000..2da1557574
--- /dev/null
+++ b/plugins/woocommerce/changelog/57294-wooplug-2482-feature-blueprint-require-permission-based-on-the-steps
@@ -0,0 +1,4 @@
+Significance: patch
+Type: enhancement
+
+Add permission validation for import steps
\ No newline at end of file
diff --git a/plugins/woocommerce/client/admin/client/blueprint/settings/slotfill.js b/plugins/woocommerce/client/admin/client/blueprint/settings/slotfill.js
index b675a810b7..727ed3451e 100644
--- a/plugins/woocommerce/client/admin/client/blueprint/settings/slotfill.js
+++ b/plugins/woocommerce/client/admin/client/blueprint/settings/slotfill.js
@@ -50,6 +50,7 @@ const Blueprint = () => {
 	);

 	const exportBlueprint = async ( _steps ) => {
+		setExportError( null );
 		setExportEnabled( false );

 		const linkContainer = document.getElementById(
@@ -65,6 +66,7 @@ const Blueprint = () => {
 					steps: _steps,
 				},
 			} );
+
 			const link = document.createElement( 'a' );
 			let url = null;

@@ -94,14 +96,41 @@ const Blueprint = () => {
 				settings_exported: _steps.settings,
 			} );
 		} catch ( e ) {
-			setExportError( e.message );
-
 			recordEvent( 'blueprint_export_error', {
 				error_message: e.message || 'unknown',
 			} );
-		}

-		setExportEnabled( true );
+			setExportError( e.message );
+
+			switch ( true ) {
+				case e instanceof Error:
+					setExportError( e.message );
+					break;
+				case typeof e === 'string':
+					setExportError( e );
+					break;
+				case e.errors &&
+					e.errors.wooblueprint_insufficient_permissions &&
+					e.errors.wooblueprint_insufficient_permissions.length > 0:
+					setExportError(
+						__(
+							'Sorry, you are not allowed to export the selected settings.',
+							'woocommerce'
+						)
+					);
+					break;
+				default:
+					setExportError(
+						__(
+							'An unknown error occurred while exporting the settings.',
+							'woocommerce'
+						)
+					);
+					break;
+			}
+		} finally {
+			setExportEnabled( true );
+		}
 	};

 	// Handle checkbox change
diff --git a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCCoreProfilerOptions.php b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCCoreProfilerOptions.php
index 60a759e331..271eb00a80 100644
--- a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCCoreProfilerOptions.php
+++ b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCCoreProfilerOptions.php
@@ -66,4 +66,13 @@ class ExportWCCoreProfilerOptions implements StepExporter, HasAlias {
 	public function get_description() {
 		return __( 'Includes onboarding configuration options', 'woocommerce' );
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities(): bool {
+		return current_user_can( 'manage_woocommerce' );
+	}
 }
diff --git a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCPaymentGateways.php b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCPaymentGateways.php
index 7a124e0457..a1aad545f0 100644
--- a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCPaymentGateways.php
+++ b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCPaymentGateways.php
@@ -84,4 +84,14 @@ class ExportWCPaymentGateways implements StepExporter {
 	public function get_description() {
 		return __( 'Includes all settings in WooCommerce | Settings | Payments.', 'woocommerce' );
 	}
+
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities(): bool {
+		return current_user_can( 'manage_woocommerce' );
+	}
 }
diff --git a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCSettings.php b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCSettings.php
index 27c05cf3ca..d280a2cf1a 100644
--- a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCSettings.php
+++ b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCSettings.php
@@ -73,4 +73,13 @@ abstract class ExportWCSettings implements StepExporter, HasAlias {
 	public function get_description() {
 		return __( 'Includes all settings in WooCommerce | Settings | General.', 'woocommerce' );
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities(): bool {
+		return current_user_can( 'manage_woocommerce' );
+	}
 }
diff --git a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCSettingsSiteVisibility.php b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCSettingsSiteVisibility.php
index 784e52f19d..aaaa2d4775 100644
--- a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCSettingsSiteVisibility.php
+++ b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCSettingsSiteVisibility.php
@@ -68,4 +68,13 @@ class ExportWCSettingsSiteVisibility implements StepExporter, HasAlias {
 	public function get_step_name() {
 		return 'setSiteOptions';
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities(): bool {
+		return current_user_can( 'manage_woocommerce' );
+	}
 }
diff --git a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCShipping.php b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCShipping.php
index bb1bc295d7..92c461c729 100644
--- a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCShipping.php
+++ b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCShipping.php
@@ -187,4 +187,13 @@ class ExportWCShipping implements StepExporter, HasAlias {
 			)
 		);
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities(): bool {
+		return current_user_can( 'manage_woocommerce' );
+	}
 }
diff --git a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCTaskOptions.php b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCTaskOptions.php
index a98ac9ad89..23b0c360d0 100644
--- a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCTaskOptions.php
+++ b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCTaskOptions.php
@@ -68,4 +68,13 @@ class ExportWCTaskOptions implements StepExporter, HasAlias {
 	public function get_description() {
 		return __( 'Includes the task configurations for WooCommerce.', 'woocommerce' );
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities(): bool {
+		return current_user_can( 'manage_woocommerce' );
+	}
 }
diff --git a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCTaxRates.php b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCTaxRates.php
index f6ffc3f3a9..74e6ca4d42 100644
--- a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCTaxRates.php
+++ b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCTaxRates.php
@@ -81,4 +81,13 @@ class ExportWCTaxRates implements StepExporter, HasAlias {
 	public function get_alias(): string {
 		return 'setWCTaxRates';
 	}
+
+	/**
+	 * Check if the current user has the required capabilities for this step.
+	 *
+	 * @return bool True if the user has the required capabilities. False otherwise.
+	 */
+	public function check_step_capabilities(): bool {
+		return current_user_can( 'manage_woocommerce' );
+	}
 }
diff --git a/plugins/woocommerce/src/Admin/Features/Blueprint/RestApi.php b/plugins/woocommerce/src/Admin/Features/Blueprint/RestApi.php
index 9e3a9538fb..52e27da5c2 100644
--- a/plugins/woocommerce/src/Admin/Features/Blueprint/RestApi.php
+++ b/plugins/woocommerce/src/Admin/Features/Blueprint/RestApi.php
@@ -58,7 +58,7 @@ class RestApi {
 				array(
 					'methods'             => \WP_REST_Server::CREATABLE,
 					'callback'            => array( $this, 'export' ),
-					'permission_callback' => array( $this, 'check_permission' ),
+					'permission_callback' => array( $this, 'check_export_permission' ),
 					'args'                => array(
 						'steps' => array(
 							'description' => __( 'A list of plugins to install', 'woocommerce' ),
@@ -98,7 +98,7 @@ class RestApi {
 				array(
 					'methods'             => \WP_REST_Server::CREATABLE,
 					'callback'            => array( $this, 'import_step' ),
-					'permission_callback' => array( $this, 'check_permission' ),
+					'permission_callback' => array( $this, 'check_import_permission' ),
 					'args'                => array(
 						'step_definition' => array(
 							'description' => __( 'The step definition to import', 'woocommerce' ),
@@ -113,13 +113,28 @@ class RestApi {
 	}

 	/**
-	 * Check if the current user has permission to perform the request.
+	 * General permission check for export requests.
 	 *
 	 * @return bool|\WP_Error
 	 */
-	public function check_permission() {
-		if ( ! current_user_can( 'install_plugins' ) ) {
-			return new \WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
+	public function check_export_permission() {
+		if ( ! current_user_can( 'manage_woocommerce' ) ) {
+			return new \WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot export WooCommerce Blueprints.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
+		}
+		return true;
+	}
+
+	/**
+	 * General permission check for import requests.
+	 *
+	 * @return bool|\WP_Error
+	 */
+	public function check_import_permission() {
+		if (
+			! current_user_can( 'manage_woocommerce' ) ||
+			! current_user_can( 'manage_options' )
+		) {
+			return new \WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot import WooCommerce Blueprints.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
 		}
 		return true;
 	}
@@ -164,6 +179,10 @@ class RestApi {

 		$data = $exporter->export( $steps );

+		if ( is_wp_error( $data ) ) {
+			return new \WP_REST_Response( $data, 400 );
+		}
+
 		return new \WP_HTTP_Response(
 			array(
 				'data' => $data,
@@ -193,15 +212,15 @@ class RestApi {
 	private function steps_payload_to_blueprint_steps( $steps ) {
 		$blueprint_steps = array();

-		if ( isset( $steps['settings'] ) ) {
+		if ( isset( $steps['settings'] ) && count( $steps['settings'] ) > 0 ) {
 			$blueprint_steps = array_merge( $blueprint_steps, $steps['settings'] );
 		}

-		if ( isset( $steps['plugins'] ) ) {
+		if ( isset( $steps['plugins'] ) && count( $steps['plugins'] ) > 0 ) {
 			$blueprint_steps[] = 'installPlugin';
 		}

-		if ( isset( $steps['themes'] ) ) {
+		if ( isset( $steps['themes'] ) && count( $steps['themes'] ) > 0 ) {
 			$blueprint_steps[] = 'installTheme';
 		}

diff --git a/plugins/woocommerce/tests/php/src/Admin/Features/Blueprint/Stubs/DummyExporter.php b/plugins/woocommerce/tests/php/src/Admin/Features/Blueprint/Stubs/DummyExporter.php
index 3e75ff1c0b..d74ba04e0b 100644
--- a/plugins/woocommerce/tests/php/src/Admin/Features/Blueprint/Stubs/DummyExporter.php
+++ b/plugins/woocommerce/tests/php/src/Admin/Features/Blueprint/Stubs/DummyExporter.php
@@ -1,24 +1,58 @@
 <?php

+declare(strict_types=1);
+
 namespace Automattic\WooCommerce\Tests\Admin\Features\Blueprint\Stubs;

 use Automattic\WooCommerce\Blueprint\Exporters\StepExporter;

+/**
+ * Dummy exporter for testing purposes.
+ */
 class DummyExporter implements StepExporter {

+	/**
+	 * Export the step.
+	 *
+	 * @return null The result of the export.
+	 */
 	public function export() {
 		return null;
 	}

+	/**
+	 * Get the description of the step.
+	 *
+	 * @return string The description of the step.
+	 */
 	public function get_description() {
-	    return 'description';
+		return 'description';
 	}

+	/**
+	 * Get the label of the step.
+	 *
+	 * @return string The label of the step.
+	 */
 	public function get_label() {
 		return 'Dummy';
 	}

+	/**
+	 * Get the name of the step.
+	 *
+	 * @return string The name of the step.
+	 */
 	public function get_step_name() {
 		return 'dummy';
 	}
+
+	/**
+	 * Check if the step is capable of being exported.
+	 *
+	 * @return bool True if the step is capable of being exported, false otherwise.
+	 */
+	public function check_step_capabilities(): bool {
+		return true;
+	}
 }