Skip to content

fix(site-exporter): scope main-site archives and fail unsafe existing-site imports #1691

Description

@superdav42

Summary

Main-site export archives can include tables belonging to other network sites. The importer then rewrites every source base-prefix table name, allowing unrelated source-site tables to be created under the target-site prefix. Existing-site imports can also emit database errors yet finish with a success message.

Evidence

  • A main-site archive contained source tables for multiple subsites (prefix_3_*, prefix_4_*, and similar), not only source main-site tables.
  • Importing that archive into an existing target site created 5,898 target-prefixed tables, including names derived from unrelated source-site prefixes.
  • The import logged 125 table already exists errors but reported successful completion.
  • The target required manual quarantine and a table-scoped recovery.

Files to modify

  • inc/site-exporter/mu-migration/includes/commands/class-mu-migration-export.php — derive the export table set from the requested blog only.
  • inc/site-exporter/mu-migration/includes/commands/class-mu-migration-import.php — validate source tables before prefix rewriting; make existing-site overwrite explicit; fail on database errors.
  • Tests covering the two command classes above — add ZIP/SQL fixtures for a main site plus at least two subsites.

Reference pattern

The importer currently maps a source database prefix to the target blog prefix before importing. Preserve that mapping only for the selected source blog's tables. Do not treat every table beginning with the network base prefix as belonging to the main site: that prefix also begins every subsite table name.

Implementation guidance

  1. Build an explicit source-table allowlist during export. For a main-site export, include main-site and supported per-site plugin tables, but exclude base_prefix + <numeric_blog_id> + _* tables and network/global tables.
  2. Do not rely only on the default wp db tables --scope=blog result when it omits custom per-site plugin tables. Define and test a classifier that preserves supported main-site custom tables while excluding other site prefixes.
  3. Store or derive the allowlist at import time and reject archives that contain unexpected site-prefixed tables.
  4. If --blog_id targets an existing site, require an explicit overwrite flag. With that flag, replace only allowlisted target tables; otherwise fail before extraction or database work.
  5. Treat any SQL import error as a failed import. Do not emit success, delete queue state, or remove recovery artifacts after database errors.
  6. Keep fresh-target and ordinary subsite imports working unchanged.

Acceptance criteria

  • A main-site export from a network with at least two subsites contains no table or data from another subsite.
  • A fresh-target import creates only tables mapped to that target blog.
  • An existing-target import fails safely unless explicit overwrite is provided.
  • Explicit overwrite affects only the validated target table set and fails on any SQL error.
  • No success message is emitted after a database error.
  • Existing subsite export/import behavior remains covered.

Verification

  1. Add unit or integration fixtures containing main-site, two-subsite, and network/global table names.
  2. Assert generated archive and transformed import SQL contain only allowlisted table names.
  3. Assert a simulated SQL failure returns non-zero and preserves recovery state.
  4. Run the relevant PHP test suite and static analysis.

Related work

Related to closed network-bundle work #1149 and #1150, but this is a regression/safety defect in the single-site main-site export/import path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingorigin:interactiveCreated by interactive user sessionpriority:highHigh severity — significant quality issuestatus:in-reviewPR open, awaiting review/mergetier:thinkingRoute at the thinking workload tier

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions