Skip to content

Harden unserialize() with allowed_classes => ['stdClass'] and add override hook#230

Merged
swissspidy merged 5 commits into
mainfrom
copilot/add-hook-allowed-classes
May 19, 2026
Merged

Harden unserialize() with allowed_classes => ['stdClass'] and add override hook#230
swissspidy merged 5 commits into
mainfrom
copilot/add-hook-allowed-classes

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 27, 2026

wp search-replace called unserialize() without restricting class instantiation, allowing arbitrary magic methods to execute on data retrieved from the database. This adds ['allowed_classes' => ['stdClass']] as the default, blocking instantiation of arbitrary user-defined classes while still allowing stdClass — a built-in PHP type with no magic methods that WordPress uses extensively for theme mods, widget data, and other serialized options.

Changes

  • src/WP_CLI/SearchReplacer.php: Passes ['allowed_classes' => ['stdClass']] to unserialize() by default. The value is resolved once per SearchReplacer instance via a new filter hook:

    WP_CLI::add_hook( 'search_replace_unserialize_options', function() {
        return [ 'allowed_classes' => [ 'stdClass', 'MyPlugin\SafeClass' ] ];
    } );
  • features/search-replace.feature:

    • Adds a scenario verifying the default behavior blocks user-defined class instantiation and the hook restores it for specific classes. The scenario uses a custom MyClass object stored directly via SQL to demonstrate that custom classes are converted to __PHP_Incomplete_Class by default (warning + 0 replacements), and the hook can allow them (1 replacement).
    • Updates the three PHP-version-specific mysqli_result scenarios — previously each PHP version triggered a different error path during object iteration/deserialization; with custom classes blocked by default, mysqli_result is never instantiated on any version, so all three now expect the same "Skipping an uninitialized class" warning and 1 replacement (from the malformed cereal_isation_2 entry which falls through to raw string replacement).

Copilot AI linked an issue Apr 27, 2026 that may be closed by this pull request
@github-actions github-actions Bot added command:search-replace Related to 'search-replace' command scope:testing Related to testing labels Apr 27, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copilot AI changed the title [WIP] Add hook to provide allowed_classes for unserialize usage Harden unserialize() with allowed_classes => false and add override hook Apr 27, 2026
Copilot AI requested a review from swissspidy April 27, 2026 11:51
@swissspidy

This comment was marked as resolved.

This comment was marked as resolved.

@swissspidy

This comment was marked as resolved.

This comment was marked as resolved.

@swissspidy

This comment was marked as resolved.

This comment was marked as resolved.

Copilot AI changed the title Harden unserialize() with allowed_classes => false and add override hook Harden unserialize() with allowed_classes => ['stdClass'] and add override hook Apr 28, 2026
@swissspidy swissspidy added this to the 2.1.12 milestone May 19, 2026
@swissspidy swissspidy marked this pull request as ready for review May 19, 2026 12:18
@swissspidy swissspidy requested a review from a team as a code owner May 19, 2026 12:18
Copilot AI review requested due to automatic review settings May 19, 2026 12:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens wp search-replace by preventing unserialize() from instantiating arbitrary classes by default (mitigating object-injection / magic-method execution risks) while providing a hook to allow specific safe classes when needed.

Changes:

  • Default unserialize() behavior is restricted via allowed_classes => ['stdClass'].
  • Introduces a search_replace_unserialize_options hook to override the unserialize() options.
  • Updates Behat scenarios to assert the new default behavior and the override behavior, and consolidates mysqli_result-related scenarios.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/WP_CLI/SearchReplacer.php Adds default unserialize() restrictions and a hook-based override mechanism.
features/search-replace.feature Adds/updates scenarios to verify restricted unserialize behavior and hook-based opt-in.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/WP_CLI/SearchReplacer.php
@swissspidy swissspidy merged commit db2d3e1 into main May 19, 2026
66 checks passed
@swissspidy swissspidy deleted the copilot/add-hook-allowed-classes branch May 19, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

command:search-replace Related to 'search-replace' command scope:testing Related to testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add hook to provide allowed_classes for unserialize() usage

3 participants