From 958418cef2d8aa137072c6d518ad0d32cc99668f Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 16 Jul 2026 16:18:52 +0200 Subject: [PATCH 1/6] Release checklist: minor updates Rodrigo did today's release while in a call with me. Here are some small updates to the release checklist (and the README) based on that experience. --- .github/release-checklist.md | 9 +++++++-- README.md | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/release-checklist.md b/.github/release-checklist.md index 234999b496..d4ea63280d 100644 --- a/.github/release-checklist.md +++ b/.github/release-checklist.md @@ -16,6 +16,8 @@ PR for tracking changes for the x.x.x release. Target release date: **DOW MONTH - [ ] PHPCSExtra: check if there have been [releases][phpcsextra-releases] since the last WordPressCS release and check through the changelog to see if there is anything WordPressCS could take advantage of - PR #xxx - [ ] Check if the minimum WP version property needs updating in `MinimumWPVersionTrait::$default_minimum_wp_version` and if so, action it - PR #xxx - [ ] Check if the `minimum_wp_version` and `testVersion` properties in `phpcs.xml.dist.sample` need updating and if so, action it - PR #xxx +- [ ] Check if the PHPCompatibility `testVersion` mentioned in the README needs updating and if so, action it - PR #xxx + :pencil2: Rule of thumb: stay in line with the "support three versions of WP below the current version" guideline, as also applied for the `minimum_wp_version`. - [ ] Check if any of the list based sniffs need updating and if so, action it. :pencil2: Make sure the "last updated" annotation in the docblocks for these lists has also been updated! List based sniffs: @@ -48,6 +50,7 @@ PR for tracking changes for the x.x.x release. Target release date: **DOW MONTH - [ ] Merge this PR. - [ ] Make sure all CI builds are green. - [ ] Tag and create a release against `main` (careful, GH defaults to `develop`!) & copy & paste the changelog to it. + _From within the GH interface: Code tab -> Releases -> "Draft a new release" button at the top of the page._ :pencil2: Check if anything from the link collection at the bottom of the changelog needs to be copied in! - Remove square brackets from all ticket links or make them proper full links (as GH markdown parser doesn't parse these correctly). - Change all contributor links to full inline links (as GH markdown parser on the Releases page doesn't parse these correctly). @@ -55,7 +58,8 @@ PR for tracking changes for the x.x.x release. Target release date: **DOW MONTH - [ ] Close the milestone. - [ ] Open a new milestone for the next release. - [ ] If any open PRs/issues which were milestoned for this release did not make it into the release, update their milestone. -- [ ] Fast-forward `develop` to be equal to `main`. +- [ ] Fast-forward `develop` to be equal to `main` (`git checkout develop && git pull upstream/develop && git merge main`). + :pencil: Branch protection may need to get a temporary exception to allow for pushing the `develop` branch. Please remember to remove the exception once the push has gone through! ### After release @@ -69,7 +73,8 @@ PR for tracking changes for the x.x.x release. Target release date: **DOW MONTH :pencil2: No need to post in the #core-coding-standard channel as that gets an automated release notification anyway. - [ ] Optionally post in #plugin-review if a sniff was added in a release which was requested by the plugin review team. - [ ] Optionally post in #core-docs if significant updates were made to the documentation ruleset. -- [ ] Create a Marketing team ["amplify request"][amplify-request]. +- [ ] Create a Marketing team ["amplify request"][amplify-request]. + Example post: https://github.com/WordPress/marketing/issues/746 - [ ] Submit for the ["Monthly Dev Roundup"][dev-roundup]. [phpcs-releases]: https://github.com/PHPCSStandards/PHP_CodeSniffer/releases diff --git a/README.md b/README.md index c72802d69b..2d3e507414 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ Install either as a separate ruleset and run it separately against your code or ``` -Whichever way you run it, do make sure you set the `testVersion` to run the sniffs against. The `testVersion` determines for which PHP versions you will receive compatibility information. The recommended setting for this at this moment is `7.2-` to support the same PHP versions as WordPress Core supports. +Whichever way you run it, do make sure you set the `testVersion` to run the sniffs against. The `testVersion` determines for which PHP versions you will receive compatibility information. The recommended setting for this at this moment is `7.2-` to support the last three WordPress releases. For more information about setting the `testVersion`, see: * [PHPCompatibility: Sniffing your code for compatibility with specific PHP version(s)](https://github.com/PHPCompatibility/PHPCompatibility#sniffing-your-code-for-compatibility-with-specific-php-versions) From 9466b4bafc90b7f940e36deccee7356a9d2ce4c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 22:13:12 +0000 Subject: [PATCH 2/6] GH Actions: Bump actions/checkout in the action-runners group Bumps the action-runners group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 7.0.0 to 7.0.1 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: action-runners ... Signed-off-by: dependabot[bot] --- .github/workflows/basic-qa.yml | 10 +++++----- .github/workflows/quicktest.yml | 2 +- .github/workflows/unit-tests.yml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/basic-qa.yml b/.github/workflows/basic-qa.yml index 9b489f7a7e..9905d051ad 100644 --- a/.github/workflows/basic-qa.yml +++ b/.github/workflows/basic-qa.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -117,7 +117,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -157,7 +157,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -240,7 +240,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -270,7 +270,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0 + uses: "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1" # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/quicktest.yml b/.github/workflows/quicktest.yml index 5da0c17a35..922b604bb9 100644 --- a/.github/workflows/quicktest.yml +++ b/.github/workflows/quicktest.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 3a9514793c..b883e601c0 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -74,7 +74,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false From e8064a69327f532dbda82339f8612de96737e975 Mon Sep 17 00:00:00 2001 From: Juliette <663378+jrfnl@users.noreply.github.com> Date: Tue, 21 Jul 2026 10:16:50 +0200 Subject: [PATCH 3/6] Add `SECURITY.md` file (#2766) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add `SECURITY.md` file The other day, Rodrigo and me noticed that WPCS does not have a published security policy (via a `SECURITY.md` file). This commit intends to add such a file, which should help inform security researchers how to disclose any findings they may have. The file is placed in the `.github` directory. This will allow for it to be recognized correctly by GitHub, while not cluttering up the project root directory. Ref: https://docs.github.com/en/code-security/how-tos/report-and-fix-vulnerabilities/configure-vulnerability-reporting/add-security-policy --------- Co-authored-by: jrfnl Co-authored-by: Denis Žoljom --- .github/SECURITY.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/SECURITY.md diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000000..c050b27ff6 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,28 @@ +# Security Policy + +## Supported Versions + +The latest minor version of the `3.x` release series is supported for security updates. + +## Reporting a Vulnerability + +The WordPressCS team takes security bugs seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. + +**Please do not report or discuss security vulnerabilities through public GitHub issues, discussions, or pull requests.** + +Issues can be reported privately to the maintainers by opening a [Security vulnerability report]. + +> [!CAUTION] +> Please take note that while the WordPress organisation has a HackerOne program, the WordPress Coding Standards software is not covered by this program. +> Full details of the WordPress Security Policy and the list of covered projects and infrastructure can be found on [HackerOne][WordPress HackerOne]. + +### Preferences + +* Please provide detailed reports with reproducible steps and a clearly defined impact. +* Include the version number of the vulnerable package in your report. +* Fixes are most welcome. + +A private PR can be created from the security report to work on and discuss the patch. + +[Security vulnerability report]: https://github.com/WordPress/WordPress-Coding-Standards/security/advisories/new +[WordPress HackerOne]: https://hackerone.com/wordpress From 267d84e32dffc6585ca7dffa0f87d8819d98a6b5 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sun, 26 Jul 2026 19:52:05 +0200 Subject: [PATCH 4/6] Composer: update minimum version PHPCSUtils + PHPCSExtra PHPCSUtils has just released version 1.2.3, which includes a security fix which is relevant for two sniffs which are included in PHPCSExtra, which has released version 1.5.1 in response. With that in mind, I'm bumping the minimum supported PHPCSUtils and PHPCSExtra versions to encourage users of to upgrade the underlying dependencies. Why does a version bump help ? Well, it only helps when a new version of WordPressCS is released, which is what I intend to do next. Once that's done, it helps as Composer will now show WordPressCS as a direct dependency which is outdated, while PHPCSUtils/PHPCSExtra would only show as an indirect dependency which is outdated, which doesn't have the same urgency for people to update. Refs: * https://github.com/PHPCSStandards/PHPCSUtils/releases/tag/1.2.3 * https://github.com/PHPCSStandards/PHPCSExtra/releases/tag/1.5.1 --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 4961107d8d..4af7f4f87c 100644 --- a/composer.json +++ b/composer.json @@ -22,8 +22,8 @@ "ext-tokenizer": "*", "ext-xmlreader": "*", "squizlabs/php_codesniffer": "^3.13.5", - "phpcsstandards/phpcsutils": "^1.2.2", - "phpcsstandards/phpcsextra": "^1.5.0" + "phpcsstandards/phpcsutils": "^1.2.3", + "phpcsstandards/phpcsextra": "^1.5.1" }, "require-dev": { "phpcompatibility/php-compatibility": "^10.0.0@dev", From 726244460ac97dc9e75d1e32e37d5ad848af2f35 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Wed, 24 Jun 2026 19:28:21 +0000 Subject: [PATCH 5/6] WP/EnqueuedResourceParameters: remove eval() from is_falsy() This PR is a fix for CVE-2026-45293 / [GHSA-3pwp-g2mj-5p3v](https://github.com/WordPress/WordPress-Coding-Standards/security/advisories/GHSA-3pwp-g2mj-5p3v). Replace the eval()-based logic with explicit token-level checks for a small set of literal falsy values: - Boolean false. - An integer or float equal to zero. - A text string with the content `'0'` or `''` (single or double-quoted, heredoc, or nowdoc). - An empty array. Other forms that the previous implementation recognised via eval() are no longer detected, such as a value wrapped in a type cast (e.g. `(int) 0`). Supporting them doesn't justify the risks of using eval(). This commit also clarifies test case comments as the previous version was inaccurate. "0, false or NULL" are not the only values that are not allowed. Also, NULL and missing $ver parameter generate a warning instead of an error. --- .../WP/EnqueuedResourceParametersSniff.php | 127 ++++++++---------- .../EnqueuedResourceParametersUnitTest.1.inc | 86 ++++++++---- .../WP/EnqueuedResourceParametersUnitTest.php | 11 +- 3 files changed, 122 insertions(+), 102 deletions(-) diff --git a/WordPress/Sniffs/WP/EnqueuedResourceParametersSniff.php b/WordPress/Sniffs/WP/EnqueuedResourceParametersSniff.php index 28b4ba8d6b..f821da1751 100644 --- a/WordPress/Sniffs/WP/EnqueuedResourceParametersSniff.php +++ b/WordPress/Sniffs/WP/EnqueuedResourceParametersSniff.php @@ -10,8 +10,11 @@ namespace WordPressCS\WordPress\Sniffs\WP; use PHP_CodeSniffer\Util\Tokens; +use PHPCSUtils\Tokens\Collections; +use PHPCSUtils\Utils\Arrays; use PHPCSUtils\Utils\Numbers; use PHPCSUtils\Utils\PassedParameters; +use PHPCSUtils\Utils\TextStrings; use WordPressCS\WordPress\AbstractFunctionParameterSniff; /** @@ -65,46 +68,17 @@ final class EnqueuedResourceParametersSniff extends AbstractFunctionParameterSni \T_NS_SEPARATOR => \T_NS_SEPARATOR, // Needed to handle fully qualified \false (PHPCS 3.x). ); - /** - * Token codes which are "safe" to accept to determine whether a version would evaluate to `false`. - * - * This array is enriched with several of the PHPCS token arrays in the register() method. - * - * @var array - */ - private $safe_tokens = array( - \T_NULL => \T_NULL, - \T_FALSE => \T_FALSE, - \T_TRUE => \T_TRUE, - \T_LNUMBER => \T_LNUMBER, - \T_DNUMBER => \T_DNUMBER, - \T_CONSTANT_ENCAPSED_STRING => \T_CONSTANT_ENCAPSED_STRING, - \T_START_NOWDOC => \T_START_NOWDOC, - \T_NOWDOC => \T_NOWDOC, - \T_END_NOWDOC => \T_END_NOWDOC, - \T_OPEN_PARENTHESIS => \T_OPEN_PARENTHESIS, - \T_CLOSE_PARENTHESIS => \T_CLOSE_PARENTHESIS, - \T_STRING_CONCAT => \T_STRING_CONCAT, - ); - /** * Returns an array of tokens this test wants to listen for. * * Overloads and calls the parent method to allow for adding additional tokens to the - * $false_tokens and $safe_tokens properties. + * $false_tokens property. * * @return array */ public function register() { $this->false_tokens += Tokens::$emptyTokens; - $this->safe_tokens += Tokens::$emptyTokens; - $this->safe_tokens += Tokens::$assignmentTokens; - $this->safe_tokens += Tokens::$comparisonTokens; - $this->safe_tokens += Tokens::$operators; - $this->safe_tokens += Tokens::$booleanOperators; - $this->safe_tokens += Tokens::$castTokens; - return parent::register(); } @@ -194,6 +168,12 @@ public function process_parameters( $stackPtr, $group_name, $matched_content, $p /** * Determine if a range has a falsy value. * + * Only a limited set of values is recognized as falsy: + * - Boolean false. + * - An integer or float equal to zero. + * - A text string with the content `'0'` or `''` (single or double-quoted, heredoc, or nowdoc). + * - An empty array. + * * @param int $start The position to start looking from. * @param int $end The position to stop looking (inclusive). * @@ -202,7 +182,6 @@ public function process_parameters( $stackPtr, $group_name, $matched_content, $p * couldn't be reliably determined. */ protected function is_falsy( $start, $end ) { - // Find anything excluding the false tokens. $has_non_false = $this->phpcsFile->findNext( $this->false_tokens, $start, ( $end + 1 ), true ); // If no non-false tokens are found, we are good. @@ -210,59 +189,69 @@ protected function is_falsy( $start, $end ) { return true; } - $code_string = ''; - for ( $i = $start; $i <= $end; $i++ ) { - if ( isset( $this->safe_tokens[ $this->tokens[ $i ]['code'] ] ) === false ) { - // Function call/variable or other token which makes it neigh impossible - // to determine whether the actual value would evaluate to false. + $target_ptr = $this->phpcsFile->findNext( Tokens::$emptyTokens, $start, ( $end + 1 ), true ); + + // An array only evaluates to false when it is empty. + if ( isset( Collections::arrayOpenTokensBC()[ $this->tokens[ $target_ptr ]['code'] ] ) ) { + $open_close = Arrays::getOpenClose( $this->phpcsFile, $target_ptr ); + if ( false === $open_close ) { + // Short list assignment, not an array. return false; } - if ( isset( Tokens::$emptyTokens[ $this->tokens[ $i ]['code'] ] ) === true ) { - continue; - } + // Bail if there is any non-empty token in the $ver parameter after the array, as that's a more complex + // expression which can't be reliably evaluated. + $next_after_array = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + ( $open_close['closer'] + 1 ), + ( $end + 1 ), + true + ); - // Make sure that PHP 7.4 numeric literals and PHP 8.1 explicit octals don't cause problems. - if ( \T_LNUMBER === $this->tokens[ $i ]['code'] || \T_DNUMBER === $this->tokens[ $i ]['code'] ) { - $number_info = Numbers::getCompleteNumber( $this->phpcsFile, $i ); - $code_string .= $number_info['decimal']; - $i = $number_info['last_token']; - continue; + if ( false !== $next_after_array ) { + return false; } - // Make sure that when deprecated casts are used in the code under scan and the sniff is run on PHP 8.5, - // the eval() won't cause a deprecation notice, borking the scan of the file. - if ( \PHP_VERSION_ID >= 80500 ) { - if ( \T_INT_CAST === $this->tokens[ $i ]['code'] ) { - $code_string .= '(int)'; - continue; - } + $first_non_empty_in_array = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + ( $open_close['opener'] + 1 ), + $open_close['closer'], + true + ); - if ( \T_DOUBLE_CAST === $this->tokens[ $i ]['code'] ) { - $code_string .= '(float)'; - continue; - } + return ( false === $first_non_empty_in_array ); + } - if ( \T_BOOL_CAST === $this->tokens[ $i ]['code'] ) { - $code_string .= '(bool)'; - continue; - } + // Check if it is a '0' or '' string. + if ( isset( Collections::textStringStartTokens()[ $this->tokens[ $target_ptr ]['code'] ] ) ) { + if ( \T_DOUBLE_QUOTED_STRING === $this->tokens[ $target_ptr ]['code'] ) { + // No need to examine as it will never match/can't be determined. + return false; + } - if ( \T_BINARY_CAST === $this->tokens[ $i ]['code'] ) { - $code_string .= '(string)'; - continue; - } + $valid_tokens = array( \T_CONSTANT_ENCAPSED_STRING ) + Tokens::$heredocTokens + Tokens::$emptyTokens; + if ( false !== $this->phpcsFile->findNext( $valid_tokens, $start, ( $end + 1 ), true ) ) { + // Bail if the $ver parameter is more than a single text string. + return false; } - $code_string .= $this->tokens[ $i ]['content']; + $content = TextStrings::getCompleteTextString( $this->phpcsFile, $target_ptr ); + + return '0' === $content || '' === $content; } - if ( '' === $code_string ) { + // The int/float check below only handles a single literal token, so bail if there is more than one non-empty token. + if ( false !== $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $target_ptr + 1 ), ( $end + 1 ), true ) ) { return false; } - // Evaluate the argument to figure out the outcome is false or not. - // phpcs:ignore Squiz.PHP.Eval -- No harm here. - return ( false === eval( "return (bool) $code_string;" ) ); + // Check if it is an int or float equal to zero. + if ( \T_LNUMBER === $this->tokens[ $target_ptr ]['code'] || \T_DNUMBER === $this->tokens[ $target_ptr ]['code'] ) { + $number_info = Numbers::getCompleteNumber( $this->phpcsFile, $target_ptr ); + + return 0.0 === (float) $number_info['decimal']; + } + + return false; } } diff --git a/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.1.inc b/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.1.inc index 8592a34121..f1bb016792 100644 --- a/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.1.inc +++ b/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.1.inc @@ -3,15 +3,15 @@ wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ) ); // Warning - missing $ver, Warning - In Footer is set to a falsy (default) value. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), /* comment */ '1.1.1', true ); // OK. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), /* comment */ '0' /* another comment */, true ); // Error - 0, false or NULL are not allowed. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), /* comment */ '0' /* another comment */, true ); // Error - a falsy value is not allowed for the $ver parameter. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '1.1.1', $in_footer ); // OK, the value is set. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 0, true ); // Error - 0, false or NULL are not allowed. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), false, true ); // Error - 0, false or NULL are not allowed. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), null, true ); // Error - 0, false or NULL are not allowed. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 0.0, true ); // Error - 0, false or NULL are not allowed. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 00.00, true ); // Error - 0, false or NULL are not allowed. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 0x0, true ); // Error - 0, false or NULL are not allowed. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 0, true ); // Error - a falsy value is not allowed for the $ver parameter. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), false, true ); // Error - a falsy value is not allowed for the $ver parameter. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), null, true ); // Warning - $ver is NULL. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 0.0, true ); // Error - a falsy value is not allowed for the $ver parameter. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 00.00, true ); // Error - a falsy value is not allowed for the $ver parameter. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 0x0, true ); // Error - a falsy value is not allowed for the $ver parameter. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 100.001, true ); // OK. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 0x1, true ); // Hex number, OK. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 052, true ); // Octal number, OK. @@ -19,7 +19,7 @@ wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 0.1, true ); // OK. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 1.0, true ); // OK. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 2 * 8, true ); // OK. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '0', true ); // Error - 0, false or NULL are not allowed. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '0', true ); // Error - a falsy value is not allowed for the $ver parameter. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '0.0.0', true ); // OK. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '0.1.0', true ); // OK. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '0' . '0', true ); // OK. @@ -51,14 +51,14 @@ wp_register_style( 'someScript-js' ); // OK. wp_enqueue_style( 'someScript-js' ); // OK. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (bool) 1, true ); // OK. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (bool) 0, true ); // Error - 0, false or NULL are not allowed. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (bool) 0, true ); // OK - a falsy value behind a cast is not flagged. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (binary) 123, true ); // OK. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), b'0', true ); // Error - 0, false or NULL are not allowed. +// Safeguard handling of arithmetic operations. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), 0 + 1, true ); // OK. // Safeguard support for PHP 8.0+ named parameters. wp_register_script( - ver : 0, // Error - 0, false or NULL are not allowed. + ver : 0, // Error - a falsy value is not allowed for the $ver parameter. in_footer: false, src : 'https://example.com/someScript.js', handle : 'someScript-js', @@ -73,39 +73,39 @@ wp_register_script( 'someScript-js', 'https://example.com/someScript.js', array( 'jquery' ), - // Error - 0, false or NULL are not allowed. + // Warning - $ver is NULL. null, true, ); // Safeguard handling of PHP 7.4 numeric literals with underscores. -wp_register_script( 'someScript-js', $url, [], 0_0.0_0, true ); // Error - 0, false or NULL are not allowed. +wp_register_script( 'someScript-js', $url, [], 0_0.0_0, true ); // Error - a falsy value is not allowed for the $ver parameter. // Safeguard handling of PHP 8.1 explicit octals. -wp_register_script( 'someScript-js', $url, [], 0o0, true ); // Error - 0, false or NULL are not allowed. +wp_register_script( 'someScript-js', $url, [], 0o0, true ); // Error - a falsy value is not allowed for the $ver parameter. + + + + + + + + -// Safeguard against PHP 8.5 deprecation of non-standard cast names. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (boolean) 1, true ); // OK. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (boolean) 0, true ); // Error - 0, false or NULL are not allowed. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (integer) 1, true ); // OK. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (integer) 0, true ); // Error - 0, false or NULL are not allowed. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (double) 1, true ); // OK. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (double) 0, true ); // Error - 0, false or NULL are not allowed. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (binary) 0, true ); // Error - 0, false or NULL are not allowed. // Safeguard handling of non-lowercase `null`. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), NULL, true ); // Warning - 0, false or NULL are not allowed. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), NULL, true ); // Warning - $ver is NULL. /* * Safeguard handling of fully qualified \true, \false and \null. * Also safeguard that adding T_NS_SEPARATOR to $false_tokens doesn't cause false positives due to problems in is_falsy(). */ -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \FALSE, \true ); // Error - 0, false or NULL are not allowed. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \null, \TRUE ); // Warning - 0, false or NULL are not allowed. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \Null, true ); // Warning - 0, false or NULL are not allowed. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \FALSE, \true ); // Error - a falsy value is not allowed for the $ver parameter. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \null, \TRUE ); // Warning - $ver is NULL. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \Null, true ); // Warning - $ver is NULL. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \true, \False ); // Ok. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \get_version(), \null ); // OK. @@ -118,3 +118,35 @@ MyNamespace\wp_register_style( 'style-name', 'https://example.com/style.css' ); \MyNamespace\wp_enqueue_style( 'style-name', 'https://example.com/style.css' ); // Ok. namespace\wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '1.1.0' ); // The sniff should start flagging this once it can resolve relative namespaces (once it does, it should be "Warning - missing $in_footer"). namespace\Sub\wp_enqueue_style( 'style-name', 'https://example.com/style.css' ); // Ok. + +// Safeguard handling of an array passed as the $ver parameter. Only an empty array evaluates to false. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), [ '1.0.0' ], true ); // OK. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), [] + [ 1 ], true ); // OK - the array is part of a larger expression. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), [ $a ] = array( 1, 2, 3 ), true ); // OK - short list assignment, not an array. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), array(), true ); // Error - a falsy value is not allowed for the $ver parameter. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), [ /* comment */ ], true ); // Error - a falsy value is not allowed for the $ver parameter. + +// Safeguard handling of a heredoc/nowdoc passed as the $ver parameter. Only an empty or "0" body evaluates to false. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), <<<'EOD' +1.0.0 +EOD +, true ); // OK. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), << 1, 14 => 1, 22 => 1, - 54 => 1, - 57 => 1, 61 => 1, 82 => 1, 85 => 1, - 89 => 1, - 92 => 1, - 95 => 1, - 97 => 1, 106 => 1, + 126 => 1, + 127 => 1, + 141 => 1, + 144 => 1, + 150 => 1, ); case 'EnqueuedResourceParametersUnitTest.2.inc': From 1696dc8f0d5995e110c2ef4b60ccd09103041960 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 16 Jul 2026 17:30:13 +0200 Subject: [PATCH 6/6] Changelog for the release of WordPressCS 3.4.1 The release will be tagged momentarily. --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fa867d25a..f297683b29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,27 @@ This project adheres to [Semantic Versioning](https://semver.org/) and [Keep a C _No documentation available about unreleased changes as of yet._ +## [3.4.1] - 2026-07-27 + +**This is a security release and all users are advised to update their WordPressCS install as soon as possible.** + +### Changed +- The minimum required `PHPCSUtils` version to 1.2.3 (was 1.2.2). [#2770] +- The minimum required `PHPCSExtra` version to 1.5.1 (was 1.5.0). [#2770] +- Various housekeeping, including documentation improvements. + +### Fixed +- **SECURITY FIX**: Running the `WordPress.WP.EnqueuedResourceParameters` sniff over untrusted PHP code, for example, in a CI pipeline that lints pull requests, or on a developer machine reviewing third-party code, could lead to arbitrary command execution on the scanning host. [#2771] + This affects users of the `WordPress` and `WordPress-Extra` rulesets. The `WordPress-Core` ruleset and the `WordPress-Docs` ruleset are not affected. + For more details, see the [security advisory][sec-1]. + Thanks to [@FORIMOC] for responsibly disclosing the vulnerability. + +[sec-1]: https://github.com/WordPress/WordPress-Coding-Standards/security/advisories/GHSA-3pwp-g2mj-5p3v + +[#2770]: https://github.com/WordPress/WordPress-Coding-Standards/pull/2770 +[#2771]: https://github.com/WordPress/WordPress-Coding-Standards/pull/2771 + + ## [3.4.0] - 2026-07-16 We're happy to welcome [@rodrigoprimo] as co-maintainer of WordPressCS as of this release. @@ -1797,6 +1818,7 @@ Initial tagged release. [PHPCompatibility]: https://github.com/PHPCompatibility/PHPCompatibility [Unreleased]: https://github.com/WordPress/WordPress-Coding-Standards/compare/main...HEAD +[3.4.1]: https://github.com/WordPress/WordPress-Coding-Standards/compare/3.4.0...3.4.1 [3.4.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/3.3.0...3.4.0 [3.3.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/3.2.0...3.3.0 [3.2.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/3.1.0...3.2.0 @@ -1842,6 +1864,7 @@ Initial tagged release. [@dd32]: https://github.com/dd32 [@desrosj]: https://github.com/desrosj [@dingo-d]: https://github.com/dingo-d +[@FORIMOC]: https://github.com/FORIMOC [@fredden]: https://github.com/fredden [@GaryJones]: https://github.com/GaryJones [@gogdzl]: https://github.com/gogdzl