diff --git a/.github/workflows/wporg-validator.yml b/.github/workflows/wporg-validator.yml new file mode 100644 index 0000000..e1dd30f --- /dev/null +++ b/.github/workflows/wporg-validator.yml @@ -0,0 +1,13 @@ +# On push, run the action-wporg-validator workflow. +name: WP.org Validator +on: [push] +jobs: + wporg-validation: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: WP.org Validator + uses: pantheon-systems/action-wporg-validator@1.0.0 + with: + type: plugin diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9383386..dec78be 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ We prefer to squash commits (i.e. avoid merge PRs) from a feature branch into `d `default` should be stable and usable, though possibly a few commits ahead of the public release on wp.org. -The `release` branch matches the latest stable release deployed to [wp.org](wp.org). +The `release` branch matches the latest stable release deployed to [wp.org](https://wordpress.org/). ## Testing @@ -33,13 +33,13 @@ The behat tests require a Pantheon site with Redis enabled. Once you've created 1. From `default`, checkout a new branch `release_X.Y.Z`. 1. Make a release commit: - * Drop the `-dev` from the version number in `README.md`, `readme.txt`, and `wp-redis.php`. - * Update the "Latest" heading in the changelog to the new version number with the date + * In `README.md`, `readme.txt`, and `wp-redis.php`, remove the `-dev` from the version number. For the README files. the version number must be updated both at the top of the document as well as the changelog. + * Add the date to the `** X.Y.X **` heading in the changelogs in README.md, readme.txt, and any other appropriate location. * Commit these changes with the message `Release X.Y.Z` * Push the release branch up. 1. Open a Pull Request to merge `release_X.Y.Z` into `release`. Your PR should consist of all commits to `default` since the last release, and one commit to update the version number. The PR name should also be `Release X.Y.Z`. 1. After all tests pass and you have received approval from a [CODEOWNER](./CODEOWNERS), merge the PR into `release`. "Rebase and merge" is preferred in this case. _Never_ squash to `release`. -1. Pull `release` locally, create a new tag (based on version number from previous steps), and push up. The tag should _only_ be the version number. It _should not_ be prefixed `v` (i.e. `X.Y.Z`, not `vX.Y.X`). +1. Locally, pull the `release` branch, create a new tag (based on version number from previous steps), and push up. The tag should _only_ be the version number. It _should not_ be prefixed `v` (i.e. `X.Y.Z`, not `vX.Y.X`). * `git tag X.Y.Z` * `git push --tags` 1. Confirm that the necessary assets are present in the newly created tag, and test on a WP install if desired. @@ -47,10 +47,12 @@ The behat tests require a Pantheon site with Redis enabled. Once you've created 1. Wait for the [_Release wp-redis plugin to wp.org_ action](https://github.com/pantheon-systems/wp-redis/actions/workflows/wordpress-plugin-deploy.yml) to finish deploying to the WordPress.org plugin repository. If all goes well, users with SVN commit access for that plugin will receive an emailed diff of changes. 1. Check WordPress.org: Ensure that the changes are live on [the plugin repository](https://wordpress.org/plugins/wp-redis/). This may take a few minutes. 1. Following the release, prepare the next dev version with the following steps: - * `git checkout develop` - * `git rebase master` + * `git checkout release` + * `git pull origin release` + * `git checkout default` + * `git rebase release` * Update the version number in all locations, incrementing the version by one patch version, and add the `-dev` flag (e.g. after releasing `1.2.3`, the new verison will be `1.2.4-dev`) - * Add a new `** Latest **` heading to the changelog + * Add a new `** X.Y.X-dev **` heading to the changelog * `git add -A .` * `git commit -m "Prepare X.Y.X-dev"` - * `git push origin develop` + * `git push origin default` diff --git a/README.md b/README.md index 4fd8a53..0a86541 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ **Tags:** cache, plugin, redis **Requires at least:** 3.0.1 **Tested up to:** 6.2 -**Stable tag:** 1.4.2 +**Stable tag:** 1.4.3 **License:** GPLv2 or later **License URI:** http://www.gnu.org/licenses/gpl-2.0.html @@ -104,6 +104,13 @@ There's a known issue with WordPress `alloptions` cache design. Specifically, a ## Changelog ## +### 1.4.3 (June 26, 2023) ### +* Bug fix: Fixes assumption that CACHE_PORT & CACHE_PASSWORD are Set. [[428](https://github.com/pantheon-systems/wp-redis/pull/428)] (props @timnolte) +* Adds WP.org validation GitHub action [[#435](https://github.com/pantheon-systems/wp-redis/pull/435)] +* Bug fix: Fixes incorrect order of `array_replace_recursive` and other issues [[434](https://github.com/pantheon-systems/wp-redis/pull/434)] (props @timnolte) +* Bug fix: Replace use of wp_strip_all_tags in object-cache.php [[434](https://github.com/pantheon-systems/wp-redis/pull/434)] (props @timnolte) +* Bug fix: Don't strip tags from the cache password. [[434](https://github.com/pantheon-systems/wp-redis/pull/434)] (props @timnolte) + ### 1.4.2 (May 15, 2023) ### * Bug fix: Removes exception loop caused by `esc_html` in `_exception_handler()` [[421](https://github.com/pantheon-systems/wp-redis/pull/421)] diff --git a/behat.yml b/behat.yml index ff5ce58..12dad86 100644 --- a/behat.yml +++ b/behat.yml @@ -3,10 +3,11 @@ default: suites: default: paths: - - tests/behat + - tests/behat/ contexts: - Behat\MinkExtension\Context\MinkContext - PantheonSystems\PantheonWordPressUpstreamTests\Behat\AdminLogIn + - behat\features\bootstrap\WpRedisFeatureContext extensions: Behat\MinkExtension: # base_url set by ENV diff --git a/bin/behat-prepare.sh b/bin/behat-prepare.sh index 71d377c..5ba9ad9 100755 --- a/bin/behat-prepare.sh +++ b/bin/behat-prepare.sh @@ -30,9 +30,6 @@ set -ex terminus env:create $TERMINUS_SITE.dev $TERMINUS_ENV terminus env:wipe $SITE_ENV --yes -# Enable Redis -terminus redis:enable $TERMINUS_SITE - ### # Get all necessary environment details. ### diff --git a/cli.php b/cli.php index bfb2e03..a183635 100644 --- a/cli.php +++ b/cli.php @@ -41,7 +41,7 @@ public function cli() { $cmd = WP_CLI\Utils\esc_cmd( 'redis-cli -h %s -p %s -a %s -n %s', $redis_server['host'], $redis_server['port'], $redis_server['auth'], $redis_server['database'] ); $process = WP_CLI\Utils\proc_open_compat( $cmd, [ STDIN, STDOUT, STDERR ], $pipes ); $r = proc_close( $process ); - exit( (int) $r ); + exit( (int) $r ); // phpcs:ignore WordPressDotOrg.sniffs.OutputEscaping.UnescapedOutputParameter } /** diff --git a/composer.json b/composer.json index 9bc37b1..a5f0929 100644 --- a/composer.json +++ b/composer.json @@ -28,5 +28,9 @@ "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } + }, + "autoload": { + "psr-4": { "behat\\features\\bootstrap\\": "tests/behat/features/bootstrap/" } } + } diff --git a/object-cache.php b/object-cache.php index 05c6526..f2fd428 100644 --- a/object-cache.php +++ b/object-cache.php @@ -986,7 +986,7 @@ public function stats() { $out[] = '
  • Group: ' . esc_html( $group ) . ' - ( ' . number_format( strlen( serialize( $cache ) ) / 1024, 2 ) . 'k )
  • '; } $out[] = ''; - echo implode( PHP_EOL, $out ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo implode( PHP_EOL, $out ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped,WordPressDotOrg.sniffs.OutputEscaping.UnescapedOutputParameter } /** @@ -1238,29 +1238,37 @@ public function check_client_dependencies() { * with defaults applied. */ public function build_client_parameters( $redis_server ) { + // Default Redis port. + $port = 6379; + // Default Redis database number. + $database = 0; + if ( empty( $redis_server ) ) { // Attempt to automatically load Pantheon's Redis config from the env. if ( isset( $_SERVER['CACHE_HOST'] ) ) { $redis_server = [ - 'host' => wp_strip_all_tags( $_SERVER['CACHE_HOST'] ), - 'port' => isset( $_SERVER['CACHE_PORT'] ) ? wp_strip_all_tags( $_SERVER['CACHE_PORT'] ) : 0, - 'auth' => isset( $_SERVER['CACHE_PASSWORD'] ) ? wp_strip_all_tags( $_SERVER['CACHE_PASSWORD'] ) : '', - 'database' => isset( $_SERVER['CACHE_DB'] ) ? wp_strip_all_tags( $_SERVER['CACHE_DB'] ) : 0, + // Don't use WP methods to sanitize the host due to plugin loading issues with other caching methods. + // @phpcs:ignore WordPressVIPMinimum.Functions.StripTags.StripTagsOneParameter,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + 'host' => strip_tags( $_SERVER['CACHE_HOST'] ), + 'port' => ! empty( $_SERVER['CACHE_PORT'] ) ? intval( $_SERVER['CACHE_PORT'] ) : $port, + // Don't attempt to sanitize passwords as this can break authentication. + // @phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + 'auth' => ! empty( $_SERVER['CACHE_PASSWORD'] ) ? $_SERVER['CACHE_PASSWORD'] : null, + 'database' => ! empty( $_SERVER['CACHE_DB'] ) ? intval( $_SERVER['CACHE_DB'] ) : $database, ]; } else { $redis_server = [ 'host' => '127.0.0.1', - 'port' => 6379, - 'database' => 0, + 'port' => $port, + 'database' => $database, ]; } } if ( file_exists( $redis_server['host'] ) && 'socket' === filetype( $redis_server['host'] ) ) { // unix socket connection. // port must be null or socket won't connect. + unset( $redis_server['port'] ); $port = null; - } else { // tcp connection. - $port = ! empty( $redis_server['port'] ) ? $redis_server['port'] : 6379; } $defaults = [ @@ -1272,7 +1280,7 @@ public function build_client_parameters( $redis_server ) { // 1s timeout, 100ms delay between reconnections. // merging the defaults with the original $redis_server enables any custom parameters to get sent downstream to the redis client. - return array_replace_recursive( $redis_server, $defaults ); + return array_replace_recursive( $defaults, $redis_server ); } /** @@ -1470,9 +1478,9 @@ protected function _exception_handler( $exception ) { try { $this->last_triggered_error = 'WP Redis: ' . $exception->getMessage(); // Be friendly to developers debugging production servers by triggering an error. - + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error,WordPress.Security.EscapeOutput.OutputNotEscaped - trigger_error( $this->last_triggered_error, E_USER_WARNING ); + trigger_error( $this->last_triggered_error, E_USER_WARNING ); } catch ( PHPUnit_Framework_Error_Warning $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch // PHPUnit throws an Exception when `trigger_error()` is called. To ensure our tests (which expect Exceptions to be caught) continue to run, we catch the PHPUnit exception and inspect the RedisException message. } diff --git a/readme.txt b/readme.txt index b71c011..c8a2ec4 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: getpantheon, danielbachhuber, mboynes, Outlandish Josh, jspellman, Tags: cache, plugin, redis Requires at least: 3.0.1 Tested up to: 6.2 -Stable tag: 1.4.2 +Stable tag: 1.4.3 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -102,6 +102,13 @@ There's a known issue with WordPress `alloptions` cache design. Specifically, a == Changelog == += 1.4.3 (June 26, 2023) = +* Bug fix: Fixes assumption that CACHE_PORT & CACHE_PASSWORD are Set. [[428](https://github.com/pantheon-systems/wp-redis/pull/428)] (props @tnolte) +* Adds WP.org validation GitHub action [[#435](https://github.com/pantheon-systems/wp-redis/pull/435)] +* Bug fix: Fixes incorrect order of `array_replace_recursive` and other issues [[434](https://github.com/pantheon-systems/wp-redis/pull/434)] (props @timnolte) +* Bug fix: Replace use of wp_strip_all_tags in object-cache.php [[434](https://github.com/pantheon-systems/wp-redis/pull/434)] (props @timnolte) +* Bug fix: Don't strip tags from the cache password. [[434](https://github.com/pantheon-systems/wp-redis/pull/434)] (props @timnolte) + = 1.4.2 (May 15, 2023) = * Bug fix: Removes exception loop caused by `esc_html` in `_exception_handler()` [[421](https://github.com/pantheon-systems/wp-redis/pull/421)] @@ -237,4 +244,4 @@ There's a known issue with WordPress `alloptions` cache design. Specifically, a == Upgrade Notice == = 1.4.0 = -WP Redis 1.4.0 adds support for the `flush_runtime` and `flush_group` functions. If you've copied `object-cache.php` and made your own changes, be sure to copy these additions over as well. \ No newline at end of file +WP Redis 1.4.0 adds support for the `flush_runtime` and `flush_group` functions. If you've copied `object-cache.php` and made your own changes, be sure to copy these additions over as well. diff --git a/tests/behat/features/bootstrap/WpRedisFeatureContext.php b/tests/behat/features/bootstrap/WpRedisFeatureContext.php new file mode 100644 index 0000000..16cc68f --- /dev/null +++ b/tests/behat/features/bootstrap/WpRedisFeatureContext.php @@ -0,0 +1,35 @@ + sanitize_text_field( $_SERVER['CACHE_HOST'] ), - 'port' => sanitize_text_field( $_SERVER['CACHE_PORT'] ), - 'auth' => sanitize_text_field( $_SERVER['CACHE_PASSWORD'] ), - 'database' => sanitize_text_field( $_SERVER['CACHE_DB'] ), + // Don't use WP methods to sanitize the host due to plugin loading issues with other caching methods. + // @phpcs:ignore WordPressVIPMinimum.Functions.StripTags.StripTagsOneParameter,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + 'host' => strip_tags( $_SERVER['CACHE_HOST'] ), + 'port' => ! empty( $_SERVER['CACHE_PORT'] ) ? intval( $_SERVER['CACHE_PORT'] ) : $port, + // Don't attempt to sanitize passwords as this can break authentication. + // @phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + 'auth' => ! empty( $_SERVER['CACHE_PASSWORD'] ) ? $_SERVER['CACHE_PASSWORD'] : null, + 'database' => ! empty( $_SERVER['CACHE_DB'] ) ? intval( $_SERVER['CACHE_DB'] ) : $database, ]; } else { $redis_server = [ 'host' => '127.0.0.1', - 'port' => 6379, - 'database' => 0, + 'port' => $port, + 'database' => $database, ]; } } @@ -73,7 +81,9 @@ function wp_redis_get_info() { } else { $uptime_in_days .= ' days'; } - $database = ! empty( $redis_server['database'] ) ? $redis_server['database'] : 0; + if ( ! empty( $redis_server['database'] ) ) { + $database = $redis_server['database']; + } $key_count = 0; if ( isset( $info[ 'db' . $database ] ) && preg_match( '#keys=([\d]+)#', $info[ 'db' . $database ], $matches ) ) { $key_count = $matches[1];