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 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/.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 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 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) 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': 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",