Skip to content

Block PHP object injection when decoding term descriptions - #990

Merged
GaryJones merged 1 commit into
developfrom
vipplug-9-deserialisation-hardening
Jun 8, 2026
Merged

Block PHP object injection when decoding term descriptions#990
GaryJones merged 1 commit into
developfrom
vipplug-9-deserialisation-hardening

Conversation

@GaryJones

Copy link
Copy Markdown
Contributor

Summary

Edit Flow stores pseudo term-meta (custom-status position and viewability, user-group membership, and similar) as a base64-encoded serialized array tucked into a term's description field, and read it back through the shared EF_Module::get_unencoded_description() helper using maybe_unserialize( base64_decode( ... ) ). Because maybe_unserialize() will happily instantiate any object encoded in the payload, that helper was a PHP object-injection surface, and the callers only inspect the result with is_array() after the unserialise has already run, so they could not prevent an object being constructed.

The decode is now gated with is_serialized() and uses unserialize() with array( 'allowed_classes' => false ), so no object can ever be instantiated. A plain, unencoded description still passes through unchanged, and the encode side is untouched, so existing stored data continues to read correctly with no migration.

An alternative was to migrate the format to JSON, but allowed_classes => false fully neutralises the object-injection threat with zero back-compat risk and without leaving a permanent legacy read path behind, so that is the route taken here.

Test plan

  • composer cs and parallel-lint pass on the changed files.
  • Two integration tests added and passing: a legitimate encoded array round-trips unchanged, and a serialized object payload is not instantiated as its class. The full ModuleTest class passes.

Fixes VIPPLUG-9.

Edit Flow stores pseudo term-meta as a base64-encoded serialized array in
term descriptions and read it back with maybe_unserialize(), which would
instantiate any object present in the payload. The callers only check the
result with is_array() afterwards, so they could not prevent the object
from being constructed first.

Gate the decode with is_serialized() and unserialize with
allowed_classes => false, so no object is ever instantiated while a plain
description still passes through untouched. The encode side is unchanged,
so existing stored data keeps working without a migration.

Fixes VIPPLUG-9.
@GaryJones
GaryJones requested a review from a team as a code owner June 8, 2026 16:29
@GaryJones GaryJones added this to the Next milestone Jun 8, 2026
@GaryJones GaryJones self-assigned this Jun 8, 2026
@GaryJones
GaryJones merged commit e2ff1c6 into develop Jun 8, 2026
10 checks passed
@GaryJones
GaryJones deleted the vipplug-9-deserialisation-hardening branch June 8, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant