Repository navigation
Security: harden serialized data validation in the SQL editor (2.6.8) - #406
Conversation
`Serialized_Data::run()` validates whether a value being saved from the database editor is well-formed serialized PHP. It only needs the shape of the data, never the objects it describes, but it called `unserialize()` with no `allowed_classes` restriction, so validating a row would construct whatever classes the value named. Pass `allowed_classes => false`. Objects then decode to `__PHP_Incomplete_Class`, which is not `false`, so the existing validity test is unchanged for legitimate data -- verified across 18 inputs (valid scalars, arrays, objects, nested objects, enums, `b:0;`, and three malformed payloads): every save/block verdict is identical to 2.6.7, and the only behavioural difference is that no constructor, `__wakeup()` or `__destruct()` runs. This completes the sweep started in 2.6.7, which hardened `recursive_unserialize_replace()` but missed this call site. Reported by Raphael P. Cigana via WPScan. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Kilo Code Review could not run — your account is out of credits. Add credits or switch to a free model to enable reviews on this change. |
|
Updated at Docs-only again — the fix file and its two QA PASS + code-review PASS verdicts above are unaffected. Still human: — Engineer (agent-os) |
Two review findings, neither affecting behaviour: - `allowed_classes => false` does NOT gate PHP 8.1 enums: an `E:` value still resolves to the real case, and WP's `is_serialized()` accepts that token. Measured, and confirmed harmless -- an enum may not declare __wakeup/__destruct/__construct/__serialize/__unserialize, so it cannot carry a gadget. The comment said the gate was total; it now names the exception so the next reader does not reason from a false guarantee. - Added an `== Upgrade Notice ==` section, which wp.org surfaces in the update nag. Worth having on a security release. Token stream of the PHP file is unchanged (307 tokens before and after), so the code edit is comment-only. All 31 harness verdicts still identical to 2.6.7; php -l and PHPCS clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
QA: PASS ✅
Repro-before-fix: yes. Every case was driven on stock 2.6.7 first and behaved identically. On top of the UI legs I ran a 44-case serialized corpus (nested/deep arrays, objects of known + unknown classes, private/protected props, references, Also: plugin activates cleanly, editor + checkbox render, file editor ( — QA |
|
QA: PASS ✅ — independent second run, corroborating the verdict above, with one release-gating finding it did not cover. All five save cases reproduced independently (stock-first baseline, then head): object rows save, array rows save, corrupt data still blocked with the notice and the row unchanged, plain text saves, unticked checkbox saves. A 42-case corpus through the real
This is reachable because the plugin declares no Suggested: add — QA |
unserialize()'s allowed_classes argument (added in this PR for the security fix) needs PHP 7.0+; on PHP 5.6 the call errors and run() treats that as invalid data, so the database editor would refuse to save any legitimately-serialized row. Measured 3/3 save->block flip on PHP 5.6.40, 0/16 on 7.4/8.2/8.3. Nothing previously declared a PHP floor, so wp.org would still offer this update to a PHP-5.6 site. Also updates readme Tested up to to 7.1, the WordPress version QA verified this release against. QA finding on PR #406. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Owner instruction: keep the public changelog generic (no mechanism details) and drop the reporter credit line from the public entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Owner instruction. It only existed to carry the removed 'Security release' line. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Hardening release for the SQL editor's serialized-data validation check.
Serialized_Data::run()(includes/Extension/SQL/Tests/class-serialized-data.php) confirms that a value being saved from the database editor is well-formed serialized PHP. It needs only the shape of the data, never the objects that data describes — but it calledunserialize()without anallowed_classesrestriction, so validation constructed whatever classes the value named.This passes
allowed_classes => false, the same restrictionrecursive_unserialize_replace()already carries since 2.6.7. That earlier sweep missed this call site; this completes it.Reported responsibly by Raphael P. Cigana via WPScan. Detail is held until a fixed version is published.
Why validation still behaves identically
With
allowed_classes => false, an object decodes to__PHP_Incomplete_Classrather thanfalse, so thefalse === $test_datavalidity test is unaffected.Verified by executing the real
run()method against WordPress's realis_serialized(), across 18 inputs, A/B against the unpatched 2.6.7 code:__wakeup,__destruct(3 cases)Cases covered: plain text; serialized string / int / bool / null / array / empty array;
b:0;;stdClass; an object of a class that exists; an object of a class that does not; an object nested in an array; an object nested in an object; a PHP 8.1 enum (E:token);C:(Serializable) format; and three malformed payloads (truncated array, wrong string length, wrong element count). The three malformed payloads still correctly block the save; everything else still saves.The unpatched leg firing
__wakeup/__destructis the positive control — it proves the harness can actually observe instantiation, so "none fired" on the patched leg is a measurement rather than a blind spot.Also audited
Every other deserialization site in the plugin was reviewed. Findings are on the internal ticket; nothing else required a change in this PR.
Version
2.6.7 → 2.6.8 (plugin header,
docs/readme.txtStable tag,docs/readme.txt+docs/changelog.txtchangelogs).Checks
php -lclean on both changed PHP files (mirrors the CI syntax step).composer run check-cs(PHPCS/WPCS) clean on both changed PHP files.docs/readme.txtis CRLF; the edit preserves it, so the diff is 3 added lines rather than a whole-file rewrite.Notes for the merger
[0-9]+.[0-9]+*publishes to WordPress.org viatrunk-release-tag.yml. The wp.org release, the public version number and the disclosure timing are Vikas's call — see the ticket./code-review ultra <this PR#>before merge.Engineer (agent-os)