Repository navigation
Port r65373 through r65390 from core - #20
Merged
Merged
Conversation
Ports r65373 from core trunk. WP_Secrets_Key_Manager::get_root_key() generated a new root key whenever get_site_option() returned false, which also happens when the query fails. On a single site the new key then overwrote the stored one, and every existing secret became unreadable. A false result is now checked against the database before a key is generated, and a failed read returns WP_Error without writing anything. Reading a secret no longer creates a root key. The first root key is inserted with INSERT IGNORE on a single site; on multisite every request uses the oldest row, and a request that lost the race deletes its own. https://core.trac.wordpress.org/changeset/65373 https://core.trac.wordpress.org/ticket/66413 Props aryanjasala, dhrupo, ericmann.
Ports r65375 from core trunk. wp_secrets_validate_name() accepted a name with a trailing newline, because $ also matches before a final newline; the pattern now ends with \z. The parameters that receive a secret value or key material are marked #[\SensitiveParameter], so they stay out of stack traces. Attributes are not inherited, so keyrings and providers in a drop-in need to add it themselves. The third fix in r65375 masks _wp_secrets_root_key on Settings > All Settings. It changes wp-admin/options.php, which the plugin cannot, so it is not ported. https://core.trac.wordpress.org/changeset/65375 https://core.trac.wordpress.org/ticket/66414 Props dhrupo, aryanjasala, ericmann.
Ports r65377 from core trunk, which gives the tests added in r65375 a summary, @Covers and a message for each assertion, as the other Secrets API tests have since r65272. https://core.trac.wordpress.org/changeset/65377 https://core.trac.wordpress.org/ticket/66414
Ports r65378 from core trunk to the plugin's drop-in loader. The drop-in is required inside wp_secrets_api_load_dropin(), so a plain assignment such as $wp_secrets_keyring = new My_Keyring(); set a local variable, and the override was ignored without a word. The loader now declares the three overrides global before the require, as core's wp_load_secrets_dropin() does and as require_wp_db() does for db.php. Assigning through $GLOBALS, as the examples do, works as before. https://core.trac.wordpress.org/changeset/65378 https://core.trac.wordpress.org/ticket/66187 Props ericmann, jorbin.
src/ stays byte for byte with core trunk, so two rules from this repository's stricter setup are relaxed for the code r65373 added, as for earlier ports: - PHPCS: WordPress-Docs rejects a docblock paragraph that opens with a function name. Core's ruleset does not include WordPress-Docs, so the sniff is excluded for src/. - PHPStan: the WordPress stubs type $wpdb->sitemeta as string|null and want a literal-string for wpdb::prepare(). The root key's direct writes are added to the ignores that already cover the option store.
r65375 marks the provider's set() $value and the keyring's wrap() $key_material with #[\SensitiveParameter]. Attributes are not inherited, so the three example drop-ins and docs/reference/drop-in-example.php add it to their own implementations. The spec pages describe the ported behavior: only a write creates the root key, a failed read of it no longer leads to a new one (r65373), the drop-in can set its overrides as plain variables (r65378), and implementations add the attribute themselves (r65375).
This was referenced Nov 10, 2026
Merged
Ports r65385 from core trunk, the change ADR 0016 decided. Without WP_SECRETS_KEY, the default keyring derived its key from LOGGED_IN_KEY . LOGGED_IN_SALT, so rotating the salts locked every stored secret, and an unfilled or translated sample placeholder could become the key. The fallback is gone: without a usable WP_SECRETS_KEY, every call that needs the key returns secret_key_unavailable. A key is not usable when it is empty or only whitespace, the wp-config-sample.php placeholder, or equal to any of the authentication keys and salts. Site Health shows a generated define() line, and reads the key's state from the new get_key_status(). wp_is_secrets_api_available() reports whether the API can be used, including whether the key can unwrap the stored root key. Beyond the straight port: - tests/bootstrap.php defines the fixed test key, as core's test bootstrap now does, because a test library from before 7.2 doesn't. - Three plugin-only WP-CLI tests that run isolated define the key themselves. - get_auth_constant_values() joins the PHPStan ignores: it calls constant() twice, so level 10 can't narrow the value. Core's level doesn't report it. - docs/spec/envelope-encryption.md and docs/reference/host-managed-keys.md no longer describe the fallback. A site that stored secrets under the fallback keeps them. Set WP_SECRETS_KEY to a new key and WP_SECRETS_KEY_PREVIOUS to the old LOGGED_IN_KEY . LOGGED_IN_SALT, then run wp secret rotate. https://core.trac.wordpress.org/changeset/65385 https://core.trac.wordpress.org/ticket/66187 Props ericmann, peterwilsoncc, Clorith, dhrupo, wildworks, johnbillion, jorbin, digitalpritam.
Ports r65387 from core trunk. A key that cannot unwrap the stored root key is now critical instead of good. For the default keyring the result says WP_SECRETS_KEY was changed or removed, and for a drop-in keyring it shows that keyring's own error. When the secrets cannot be listed, the undecryptable test is critical and the rotation test is recommended, instead of both reading the WP_Error as an empty list. A broken keyring is detected by its class, not by matching a translated word. When a drop-in replaces the provider, the key source test says the provider manages its own keys, and Site Health Info marks the keyring class and key source as not used. A key that is not in the recommended form is no longer described as one to regenerate once secrets are stored, and the undecryptable result no longer says the values can never be recovered. docs/spec/providers-and-keyrings.md describes what Site Health shows for a provider drop-in. https://core.trac.wordpress.org/changeset/65387 https://core.trac.wordpress.org/ticket/66380 Props nofearinc, ericmann, ravipatel, dhrupo, Clorith, jorbin, peterwilsoncc.
Ports r65390 from core trunk (ADR 0017). WP_Secrets_Provider, WP_Secrets_Store and WP_Secrets_Keyring become abstract classes instead of interfaces, so a later release can add a method with a default implementation without breaking every drop-in. Their files move from interface-*.php to class-*.php. A drop-in changes `implements` to `extends`. One that still uses `implements` is a fatal error on every request, WP-CLI included, not a fail-closed API: PHP reports it while compiling the drop-in, before the try/catch around the require can run. Beyond the straight port: - The plugin's bootstrap requires the renamed files, and its drop-in loader's docblock describes the classes and the new fatal. - Secrets_API_Prototype_Fallback_Store, the three examples, docs/reference/drop-in-example.php and host-managed-keys.md extend the classes. - The spec pages, the README, the examples' READMEs and specs, and the smoke test's wording follow. The smoke test records a drop-in that still uses `implements` as a fatal, alongside the existing incomplete-class case. - ADR 0017 records that the old form is a fatal, not a fail-closed error. https://core.trac.wordpress.org/changeset/65390 https://core.trac.wordpress.org/ticket/66187 Props ericmann, jorbin.
The stand-down job paired core from the nightly zip with a test suite from wordpress-develop trunk. The nightly is built about once a day, so after 15744 the suite's mocks extended classes the nightly still declared as interfaces, and the bootstrap died. trunk now installs core's src/ from the same tarball as the test suite, as core runs its own tests. nightly still means the nightly zip. The smoke test's new implements case checks only for "Fatal error", because the nightly smoke leg can run on a nightly from before the classes, where the same drop-in is a different fatal.
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.
Ports seven changesets from core trunk, so
src/matches trunk through r65390, which is 7.2 beta 5. One commit per changeset, each with its props.\z). Secret values and key material are#[\SensitiveParameter].@coversand messages for r65375's tests.wp_secrets_api_load_dropin().WP_SECRETS_KEYis required. The salt fallback is gone (ADR 0016).wp_is_secrets_api_available()andget_key_status()are new.implementstoextends. One that doesn't is a fatal error, not a fail-closed API.Not ported, because they change core admin files the plugin can't touch, and core 7.2 has them:
secrets.phpin_get_dropins(), which has no filter._wp_secrets_root_keyon Settings > All Settings, inwp-admin/options.php.setup-config.phpwriteWP_SECRETS_KEY, with a placeholder inwp-config-sample.php.Beyond the straight port:
src/. Its direct$wpdbwrites join the PHPStan ignores that already cover the option store (stub types stricter than core's).src/stays byte for byte with trunk.docs/reference/drop-in-example.phpmark their ownwrap()andset()parameters.envelope-encryption.mdandproviders-and-keyrings.mddescribe the new root-key write, the plain-variable globals and the attribute.tests/bootstrap.phpdefines the fixed test key, as core's test bootstrap now does, because a test library from before 7.2 doesn't. Three plugin-only WP-CLI tests that run isolated define the key themselves.get_auth_constant_values()joins the PHPStan ignores: it callsconstant()twice, so level 10 can't narrow the value.envelope-encryption.mdandhost-managed-keys.mdno longer describe the fallback, andproviders-and-keyrings.mdsays what Site Health shows for a provider drop-in.src/. The bootstrap requires the renamed files, and the fallback store, the three examples,drop-in-example.phpandhost-managed-keys.mdextend the classes. The specs, README and smoke test wording follow, and ADR 0017 records thatimplementsis a fatal. The smoke test records that fatal as a new case.trunkinstalls core from wordpress-develop. The stand-down job took core from the nightly zip and the tests from wordpress-develop trunk. After r65390, the tests' mocks extended classes that the day-old nightly still declared as interfaces, and the bootstrap died.bin/install-wp-tests.shnow installs core'ssrc/fortrunkfrom the same tarball as the tests.nightlystill means the nightly zip.docs/reference/is regenerated.Testing
Run locally against WordPress 7.1.4, PHP 8.5:
mainhas 509.make reference-checkpasses.src/was compared with trunk at the matching changeset, and is identical.Props aryanjasala, dhrupo, jorbin, peterwilsoncc, Clorith, wildworks, johnbillion, digitalpritam, nofearinc, ravipatel.