Commit 67d039b5455 for woocommerce

commit 67d039b5455f81b53a027b4a37f6ce1e98e3d3ec
Author: Michal Iwanow <4765119+mcliwanow@users.noreply.github.com>
Date:   Wed Nov 4 13:50:38 2026 +0100

    Restrict the product CSV importer to recognised product fields (#70631)

    process_item() now passes only the product's data keys to set_props().

    Co-authored-by: Chris Lilitsas <1105590+xristos3490@users.noreply.github.com>

diff --git a/plugins/woocommerce/changelog/fix-product-import-column-mapping b/plugins/woocommerce/changelog/fix-product-import-column-mapping
new file mode 100644
index 00000000000..e023a23e8f5
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-product-import-column-mapping
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Improve validation of the column mapping used by the product CSV importer.
diff --git a/plugins/woocommerce/includes/import/abstract-wc-product-importer.php b/plugins/woocommerce/includes/import/abstract-wc-product-importer.php
index 78a5d6dbee4..11e14657104 100644
--- a/plugins/woocommerce/includes/import/abstract-wc-product-importer.php
+++ b/plugins/woocommerce/includes/import/abstract-wc-product-importer.php
@@ -184,7 +184,23 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
 			try {
 				// Prevent getting "variation_invalid_id" error message from Variation Data Store.
 				if ( ProductType::VARIATION === $data['type'] ) {
-					$was_variation = $id && 'product_variation' === get_post_type( $id );
+					$post_type = $id ? get_post_type( $id ) : false;
+
+					// The wp_update_post() below rewrites the post type, so refuse an ID that is
+					// not already a product or variation to avoid converting an arbitrary post.
+					if ( $post_type && ! in_array( $post_type, array( 'product', 'product_variation' ), true ) ) {
+						return new WP_Error(
+							'woocommerce_product_csv_importer_invalid_id',
+							/* translators: %d: product ID */
+							sprintf( __( 'Invalid product ID %d.', 'woocommerce' ), $id ),
+							array(
+								'id'     => $id,
+								'status' => 401,
+							)
+						);
+					}
+
+					$was_variation = 'product_variation' === $post_type;

 					$id = wp_update_post(
 						array(
@@ -378,7 +394,7 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
 				unset( $data['cogs_value'] );
 			}

-			$result = $object->set_props( array_diff_key( $data, array_flip( array( 'meta_data', 'raw_image_id', 'raw_gallery_image_ids', 'raw_attributes' ) ) ) );
+			$result = $object->set_props( array_intersect_key( $data, array_flip( $object->get_data_keys() ) ) );

 			if ( is_wp_error( $result ) ) {
 				throw new Exception( $result->get_error_message() );
diff --git a/plugins/woocommerce/tests/php/includes/importer/class-wc-product-csv-importer-test.php b/plugins/woocommerce/tests/php/includes/importer/class-wc-product-csv-importer-test.php
index 5f925f4d2ff..a9f504170a9 100644
--- a/plugins/woocommerce/tests/php/includes/importer/class-wc-product-csv-importer-test.php
+++ b/plugins/woocommerce/tests/php/includes/importer/class-wc-product-csv-importer-test.php
@@ -3198,4 +3198,77 @@ class WC_Product_CSV_Importer_Test extends \WC_Unit_Test_Case {
 		$this->assertSame( array( 'Premium', 'Basic' ), $product->get_attributes()['edition']->get_options() );
 		$this->assertSame( array( 'edition' => 'Premium' ), $product->get_default_attributes() );
 	}
+
+	/**
+	 * @testdox Only recognised product fields from the column mapping are applied to the imported product.
+	 * @testWith ["post_type"]
+	 *           ["post_author"]
+	 *           ["ID"]
+	 *           ["unknown_prop"]
+	 *
+	 * @param string $key Unrecognised key the first column is mapped to.
+	 */
+	public function test_import_applies_only_recognised_product_fields( string $key ): void {
+		$page_id = self::factory()->post->create(
+			array(
+				'post_type'  => 'page',
+				'post_title' => 'About us',
+			)
+		);
+
+		$file = get_temp_dir() . 'product-import-' . wp_generate_uuid4() . '.csv';
+		file_put_contents( $file, "target,name\n{$page_id},Imported name\n" ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Temporary CSV fixture.
+
+		try {
+			$importer = new WC_Product_CSV_Importer(
+				$file,
+				array(
+					'parse'           => true,
+					'update_existing' => false,
+					'mapping'         => array(
+						'target' => $key,
+						'name'   => 'name',
+					),
+				)
+			);
+			$data     = $importer->import();
+		} finally {
+			wp_delete_file( $file );
+		}
+
+		$this->assertCount( 1, $data['imported'], 'The row should import as its own product' );
+		$this->assertEmpty( $data['failed'] );
+		$this->assertNotContains( $page_id, $data['imported'], 'The imported product should not reuse the page ID' );
+		$this->assertSame( 'About us', get_the_title( $page_id ), 'The page title should be unchanged' );
+		$this->assertSame( 'page', get_post_type( $page_id ), 'The page should keep its post type' );
+	}
+
+	/**
+	 * @testdox get_product_object() only treats an existing product or variation as the variation to update.
+	 */
+	public function test_get_product_object_requires_product_id_for_variation(): void {
+		$page_id  = self::factory()->post->create( array( 'post_type' => 'page' ) );
+		$importer = new class( __DIR__ . '/sample.csv' ) extends WC_Product_CSV_Importer {
+			/**
+			 * Expose the protected method under test.
+			 *
+			 * @param array $data Row data.
+			 * @return WC_Product|WP_Error
+			 */
+			public function product_object_for( array $data ) {
+				return $this->get_product_object( $data );
+			}
+		};
+
+		$result = $importer->product_object_for(
+			array(
+				'type' => ProductType::VARIATION,
+				'id'   => $page_id,
+			)
+		);
+
+		$this->assertWPError( $result );
+		$this->assertSame( 'woocommerce_product_csv_importer_invalid_id', $result->get_error_code() );
+		$this->assertSame( 'page', get_post_type( $page_id ), 'The page should keep its post type' );
+	}
 }