diff --git a/SECURITY.md b/SECURITY.md index 7bab642..60d0616 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -25,7 +25,7 @@ explanation rather than silence. ## Scope -This is a pre-release feature plugin (`0.4.0`). The API is in WordPress core trunk for 7.2, and +This is a pre-release feature plugin (`0.5.0`). The API is in WordPress core trunk for 7.2, and `src/` follows it. There is no supported-version matrix yet: `main` is the only supported branch, and fixes land there. diff --git a/docs/changelog/0-5-0.md b/docs/changelog/0-5-0.md new file mode 100644 index 0000000..5123c3c --- /dev/null +++ b/docs/changelog/0-5-0.md @@ -0,0 +1,95 @@ +--- +title: "0.5.0" +description: "WP_SECRETS_KEY is required, the provider, store and keyring are abstract classes, and src/ follows core trunk through 7.2 beta 5. Two breaking changes: check both before updating." +date: 2026-11-10 +--- + +# 0.5.0 + +Released 2026-11-10. In this release `src/` and the shared test helpers match WordPress core trunk +as of [r65390](https://core.trac.wordpress.org/changeset/65390), byte for byte: what ships +in 7.2 beta 5. + +## Before you update + +This release has two breaking changes. Both match core from 7.2 beta 5. + +**1. `WP_SECRETS_KEY` is required.** Without it, the Secrets API is unavailable: every call that +needs the key returns `secret_key_unavailable`. Until now the key was derived from `LOGGED_IN_KEY` +and `LOGGED_IN_SALT` when `WP_SECRETS_KEY` was not defined. Rotating the salts then locked every +stored secret ([ADR 0016](../decisions/0016-require-wp-secrets-key.md)). + +- A site that already defines `WP_SECRETS_KEY` needs to do nothing. +- A site with no stored secrets only needs to add a key. Site Health shows a generated `define()` + line to paste into `wp-config.php`, and so does `wp secret generate-key`. +- A site that stored secrets under the fallback must move them **before** updating, or they read + as `secret_key_unavailable` until it does: + 1. Set `WP_SECRETS_KEY` to a new value, from `wp secret generate-key`. + 2. Set `WP_SECRETS_KEY_PREVIOUS` to the old `LOGGED_IN_KEY . LOGGED_IN_SALT`, the two values + joined together. + 3. Run `wp secret rotate`. + 4. Remove `WP_SECRETS_KEY_PREVIOUS`. + + Nothing is lost if you update first: the root key is left as it was, and the same steps make + the secrets readable again afterward. + +**2. A `secrets.php` drop-in uses `extends`, not `implements`.** `WP_Secrets_Provider`, +`WP_Secrets_Store` and `WP_Secrets_Keyring` are abstract classes now, not interfaces, so a later +release can add a method without breaking every drop-in +([ADR 0017](../decisions/0017-abstract-classes-for-the-extension-points.md)). Change `implements` +to `extends` in your drop-in. Nothing else in the contract changes. + +A drop-in that still uses `implements` is **a fatal error on every request**, WP-CLI included. It +does not fail closed: PHP reports it while compiling the drop-in, before WordPress can catch it. +If you copied one of the [examples](../reference/examples.md), take the new version. They extend +the classes. + +## Ported from core + +| Changeset | Change | Props | +|---|---|---| +| [r65272](https://core.trac.wordpress.org/changeset/65272) | The tests follow core's PHPUnit guidelines: a summary, `@covers` and a message for each assertion. The docblocks no longer refer to the feature plugin. | peterwilsoncc | +| [r65373](https://core.trac.wordpress.org/changeset/65373) | A failed read of the root key no longer replaces it, which made every stored secret unreadable. Only a write creates a root key. [#66413](https://core.trac.wordpress.org/ticket/66413) | aryanjasala, dhrupo, ericmann | +| [r65375](https://core.trac.wordpress.org/changeset/65375) | A secret name can't end in a newline. Secret values and key material are marked `#[\SensitiveParameter]`, so they stay out of stack traces. [#66414](https://core.trac.wordpress.org/ticket/66414) | dhrupo, aryanjasala, ericmann | +| [r65377](https://core.trac.wordpress.org/changeset/65377) | Summaries, `@covers` and messages for r65375's tests. | – | +| [r65378](https://core.trac.wordpress.org/changeset/65378) | The `secrets.php` drop-in can set its overrides as plain variables, such as `$wp_secrets_keyring = new My_Keyring();`. The plugin's own drop-in loader does the same. | ericmann, jorbin | +| [r65385](https://core.trac.wordpress.org/changeset/65385) | `WP_SECRETS_KEY` is required. A key that is empty or only whitespace, the sample placeholder, or equal to a key or salt is not usable. New: `wp_is_secrets_api_available()` and `WP_Secrets_Config_Key_Provider::get_key_status()`. | ericmann, peterwilsoncc, Clorith, dhrupo, wildworks, johnbillion, jorbin, digitalpritam | +| [r65387](https://core.trac.wordpress.org/changeset/65387) | Site Health reports a key that can't unlock the stored secrets, and secrets that can't be listed, as problems instead of good. It gives advice that fits a provider drop-in and a legacy-form key. [#66380](https://core.trac.wordpress.org/ticket/66380) | nofearinc, ericmann, ravipatel, dhrupo, Clorith, jorbin, peterwilsoncc | +| [r65390](https://core.trac.wordpress.org/changeset/65390) | The provider, store and keyring are abstract classes. | ericmann, jorbin | + +peterwilsoncc committed r65272, and whyisjake committed the rest. + +Three changes from the same period are in core only, because they edit core files the plugin can't +change. WordPress 7.2 has them: + +- [r65374](https://core.trac.wordpress.org/changeset/65374) lists `secrets.php` under Plugins > + Drop-ins and in Site Health Info. Props nofearinc, ericmann. +- r65375 masks `_wp_secrets_root_key` on Settings > All Settings, where saving it changed made + every stored secret unreadable. On WordPress 7.1 or earlier, don't edit that field. +- [r65389](https://core.trac.wordpress.org/changeset/65389) has the installer write + `WP_SECRETS_KEY` into `wp-config.php`, with a placeholder in `wp-config-sample.php`. + [#66399](https://core.trac.wordpress.org/ticket/66399). Props ericmann, peterwilsoncc, + digitalpritam, ravipatel, Clorith, dhrupo. + +## Changed + +- **Drop-ins should mark their own sensitive parameters.** Attributes aren't inherited, so a + keyring's `wrap()` and a provider's `set()` add `#[\SensitiveParameter]` themselves. The + examples and [`drop-in-example.php`](../reference/drop-in-example.php) do. On PHP 7.4 the + attribute line is a comment. +- **CI tests the plugin where it runs.** The full suite runs on WordPress 7.1 and the 6.8 floor. + On trunk, which ships the API, a separate job checks only that the plugin stands down. + +## In review in core + +Each of these will be ported once committed. + +| Ticket | Change | PR | +|---|---|---| +| [#66187](https://core.trac.wordpress.org/ticket/66187) | The Site Health checks move into `WP_Site_Health` and `WP_Debug_Data`. | [15881](https://github.com/WordPress/wordpress-develop/pull/15881) | +| [#66424](https://core.trac.wordpress.org/ticket/66424) | A failed read of a secret's record no longer reads as a missing secret. | [15329](https://github.com/WordPress/wordpress-develop/pull/15329) | + +WP-CLI's `wp config create` will generate the key as well +([wp-cli/config-command#235](https://github.com/wp-cli/config-command/pull/235)). Moving the +`wp secret` commands into WP-CLI is proposed in +[wp-cli/entity-command#677](https://github.com/wp-cli/entity-command/issues/677). diff --git a/docs/spec/scope.md b/docs/spec/scope.md index 5417e04..b912e00 100644 --- a/docs/spec/scope.md +++ b/docs/spec/scope.md @@ -15,7 +15,7 @@ scope", and "Timeline" in the [proposal][proposal]. ## As built -**The plugin.** `secrets-api.php` declares version `0.4.0`, `Requires at least: 6.8`, and +**The plugin.** `secrets-api.php` declares version `0.5.0`, `Requires at least: 6.8`, and `Requires PHP: 7.4`. **Core-bound versus plugin-only.** Everything under `src/` is written to be copied into diff --git a/secrets-api.php b/secrets-api.php index faa2e89..5280758 100644 --- a/secrets-api.php +++ b/secrets-api.php @@ -3,7 +3,7 @@ * Plugin Name: Secrets API * Plugin URI: https://github.com/ericmann/secrets-api * Description: Feature plugin for the WordPress Secrets API proposed for 7.2. Encrypted, versioned credential storage with pluggable storage and keyring back ends. - * Version: 0.4.0 + * Version: 0.5.0 * Requires at least: 6.8 * Requires PHP: 7.4 * License: GPL-2.0-or-later @@ -25,7 +25,7 @@ /** * Plugin version. */ -define( 'WP_SECRETS_API_PLUGIN_VERSION', '0.4.0' ); +define( 'WP_SECRETS_API_PLUGIN_VERSION', '0.5.0' ); /** * The WordPress version expected to ship the Secrets API in core.