Skip to content

Import accepts a CSV whose header belongs to a different entity, silently coercing values and disabling products #40822

@damienwebdev

Description

@damienwebdev

Preconditions and environment

  • Magento Open Source 2.4.7-p9. Defect is in the per-entity import validation paths and is independent of catalog content.
  • PHP 8.3.
  • Admin access to System → Data Transfer → Import with permissions to import at least the "Products" and "Stock Sources" entities.
  • At least four enabled simple products to target with the test CSV (any SKUs will do).

Steps to reproduce

  1. Author a CSV whose header matches the Stock Sources entity schema, populated with rows targeting four products. Save as stock_sources_payload.csv:

    source_code,sku,status,quantity
    default,<sku-1>,0,0
    default,<sku-2>,0,0
    default,<sku-3>,0,0
    default,<sku-4>,0,0
  2. Confirm the four products' current state before the import:

    SELECT cpe.entity_id, cpe.sku,
           cpei.value AS status_eav
    FROM   catalog_product_entity cpe
    JOIN   catalog_product_entity_int cpei
      ON   cpei.entity_id = cpe.entity_id
      AND  cpei.attribute_id = (SELECT attribute_id FROM eav_attribute
                                WHERE entity_type_id = 4 AND attribute_code = 'status')
      AND  cpei.store_id = 0
    WHERE  cpe.sku IN ('<sku-1>','<sku-2>','<sku-3>','<sku-4>');
    -- expect: status_eav = 1 (Enabled) for all rows
  3. Admin → System → Data Transfer → Import. Set Entity Type = Products (deliberately wrong — the CSV is shaped for Stock Sources). Upload stock_sources_payload.csv. Run the validation step, then submit the import.

  4. Magento reports the import succeeded.

  5. Re-query the same products:

    SELECT cpe.entity_id, cpe.sku,
           cpei.value AS status_eav
    FROM   catalog_product_entity cpe
    JOIN   catalog_product_entity_int cpei
      ON   cpei.entity_id = cpe.entity_id
      AND  cpei.attribute_id = (SELECT attribute_id FROM eav_attribute
                                WHERE entity_type_id = 4 AND attribute_code = 'status')
      AND  cpei.store_id = 0
    WHERE  cpe.sku IN ('<sku-1>','<sku-2>','<sku-3>','<sku-4>');
    
    SELECT * FROM cataloginventory_stock_item     WHERE product_id IN (…);
    SELECT * FROM cataloginventory_stock_status   WHERE product_id IN (…);
    SELECT * FROM inventory_source_item           WHERE sku IN ('<sku-1>',…,'<sku-4>') AND source_code='default';

Expected result

The importer detects that the uploaded CSV's column set does not match the selected entity's writable columns, and either:

  • Rejects the import with a per-row validation error identifying that source_code and quantity are not valid Products-entity columns; or
  • Surfaces an actionable warning before commit (e.g. "Columns source_code, quantity will be ignored; the status column will be coerced to product EAV status; the value 0 will be mapped to Disabled (2). Proceed?") and only proceeds on explicit confirmation.

No row is written until the operator confirms an unambiguous mapping.

Actual result

The Products importer accepts the CSV and silently:

  • Interprets the status column as the product EAV status attribute. The CSV value 0 is not a valid product-status enum value (Magento accepts 1 = Enabled, 2 = Disabled); it is coerced to Disabled (2), disabling each row's product.
  • Ignores source_code entirely (not a Products-entity column).
  • Ignores quantity (the Products importer expects stock fields under qty, is_in_stock, etc.; the quantity column is unrecognized and silently dropped).
  • Reports success. The success message provides no indication that the CSV's columns were partially honored and partially ignored, or that values were coerced.

In step 5, the four products' status_eav column has flipped from 1 to 2. Stock tables are unchanged.

The symmetric inverse reproduces if the CSV is shaped for the Products entity and the operator selects Stock Sources at import time: Stock Sources fields are written for any matching columns, Products-only columns are ignored, no warning is issued.

Additional information

Severity in practice. A merchant attempting to mark inventory out-of-stock can instead silently disable products. Disabled products are removed from the catalog, sitemaps, product feeds, and (over time) from search-engine indices. The mistake is invisible from the import result and difficult to attribute without inspecting the EAV status column directly, since the operator's mental model is "I set stock to zero" — the catalog disappearance presents as a stock issue rather than a status issue.

The error class is "wrong entity selected at import time" — a single misclick in a dropdown. There is no audit trail surfacing the coercion. Recovery requires identifying the affected SKUs, re-enabling them via a corrected Products-entity import or admin edits, and (in the worst case) waiting for search-engine re-indexing.

Suggested fix. Add per-entity header validation to Magento\ImportExport\Model\Import and the entity-specific validateData() paths. When the uploaded CSV's column set has no overlap with the selected entity's writable columns — or contains columns from a different entity's known schema — reject the import or require explicit confirmation before any rows are written. A minimal implementation can compare the CSV header against a per-entity allowed-column manifest registered in DI; columns not in the manifest become validation warnings (or hard errors, per Adobe's preference), and columns from a known other entity's manifest become a "wrong entity selected?" guidance message.

Release note

Validate CSV column headers at import time against the selected entity's schema. The Products import now warns when a CSV contains columns from a different entity's schema (e.g. Stock Sources columns source_code / quantity), and the Stock Sources import does likewise for Products-only columns, preventing silent value coercion and accidental product disabling.

Triage and priority

  • Severity: S0 - Affects critical data or functionality and leaves users without workaround.
  • Severity: S1 - Affects critical data or functionality and forces users to employ a workaround.
  • Severity: S2 - Affects non-critical data or functionality and forces users to employ a workaround.
  • Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.
  • Severity: S4 - Affects aesthetics, professional look and feel, “quality” or “usability”.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status

    Ready for Confirmation

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions