Repair client secrets damaged by 2.0.0 and 2.0.1 - #7
Merged
Conversation
2.0.2 stopped the settings sanitizer adding an encryption layer on every write, but it did nothing for sites that had already been damaged. Those installs hold cipher text a single decrypt cannot recover, so they fail to authenticate with nothing on screen to explain why — the operator would have to guess that the stored credential itself is the problem. The upgrade routine now unwraps the value back to plain text and re-stores it correctly. Encryption::decrypt_deep() peels layers with a bounded ceiling, and Encryption::depth() reports how many are present. If the value cannot be recovered it is cleared rather than left broken, and either outcome is reported in an admin notice that appears once. The stored token is also dropped so the next request re-authenticates with the repaired secret instead of a stale one. Verified against a site running the real v2.0.1 release, corrupted through 2.0.1's own code path rather than by hand-crafting the value: BEFORE version=2.0.1 depth=3 client_secret()='pwp2:Tnd7t4TV3YLRFwvRF' AFTER version=2.0.3 depth=1 client_secret()='THE-REAL-SECRET' Four unit tests cover the repair, the two no-op cases and the unrecoverable path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
2.0.2 stopped the settings sanitizer adding an encryption layer on every write — but it did nothing for sites already damaged. Those installs hold cipher text a single decrypt cannot recover, so they fail to authenticate with nothing on screen to explain why. The operator would have to guess that the stored credential itself is the problem.
What this does
The upgrade routine unwraps the value back to plain text and re-stores it correctly.
Encryption::decrypt_deep()peels layers with a bounded ceiling;Encryption::depth()reports how many are present.If it can't be recovered, the secret is cleared rather than left broken, and either outcome surfaces in a one-time admin notice. The stored token is dropped too, so the next request re-authenticates with the repaired secret instead of a stale one.
Verified against a real damaged site
Installed the actual v2.0.1 release, corrupted the secret through 2.0.1's own code path — not by hand-crafting the value — then upgraded:
Tests
4 new unit tests: the repair itself, the two no-op cases (healthy secret, empty secret), and the unrecoverable path. 86 tests, 350 assertions, PHPCS clean.
One nuance worth recording
While reproducing this I confirmed the damage comes from internal writes —
Settings::set()andmigrate_legacy()— not from the settings form itself. A normal form submit passes raw$_POSTthroughupdate_option(), so the sanitizer runs exactly once and encrypts correctly. It's the plugin's own subsequent writes that re-fed already-encrypted cipher text through it.🤖 Generated with Claude Code