[wp-trac] [WordPress Trac] #58764: Incorrect return type in `WP_Rewrite::using_index_permalinks()`.
WordPress Trac
noreply at wordpress.org
Sat Jul 8 10:08:29 UTC 2023
#58764: Incorrect return type in `WP_Rewrite::using_index_permalinks()`.
--------------------------+-----------------------------
Reporter: costdev | Owner: (none)
Type: defect (bug) | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Permalinks | Version: 2.7
Severity: normal | Keywords: 2nd-opinion
Focuses: |
--------------------------+-----------------------------
== Overview
In [8899], [https://github.com/WordPress/wordpress-develop/blob/6.2/src
/wp-includes/class-wp-rewrite.php#L357-L373
WP_Rewrite::using_index_permalinks()] was documented to return `bool`.
However, its actual return type is `int|false`.
A guard explicitly returns `false`, however the final return statement
returns the result of a `preg_match()` call.
Per [https://www.php.net/manual/en/function.preg-match.php the PHP
manual]:
> `preg_match()` returns 1 if the pattern matches given subject, 0 if it
does not, or false on failure.
== Docs change, or `bool` cast?
=== Docs change
We could change the return type to:
{{{#!php
@return int|false 1 if permalink links are enabled and index.php is in the
URL,
0 if permalinks are enabled but there was a failure,
false if permalinks are disabled.
}}}
However, I think this is an overly complicated return for a method called
`using_index_permalinks()` that suggests, and is currently documented as,
a simple `bool` return type.
=== `bool` cast: Is there a backward compatibility concern?
Yes, but a very small one.
WP Directory shows:
- [https://wpdirectory.net/search/01H4T8ZA3EYAHNCJH57EKTSYH2 all 135
plugins] and [https://wpdirectory.net/search/01H4TEEJ598PDEK1WCRJ4C3SGW
all 138 themes] that call this method use a loose check on the result,
treating `0` and `false` as "No, it does not use index permalinks". These
are safe from a `(bool)` cast.
- It's possible that someone out there is using `1 ===
$wp_rewrite->using_index_permalinks()`, but as the method is documented to
return `bool`, they're most likely using a loose check after an assumed
attempt of `true ===` would have always failed, as the method currently
''never'' returns `true`.
=== When does `preg_match()` return `false`?
Per PHP-src, a call to [https://github.com/php/php-
src/blob/c6b9db2131deda7ca0683a6006fe9ae8dd767f51/ext/pcre/php_pcre.c#L1465
preg_match] eventually leads to [https://github.com/php/php-
src/blob/master/ext/pcre/php_pcre.c#L589C26-L589C58
pcre_get_compiled_regex_cache_ex].
The following failure conditions are listed:
- [https://github.com/php/php-src/blob/master/ext/pcre/php_pcre.c#L642
Empty regular expression]
- [https://github.com/php/php-src/blob/master/ext/pcre/php_pcre.c#L654
Delimiter must not be alphanumeric, backslash, or NUL]
- [https://github.com/php/php-src/blob/master/ext/pcre/php_pcre.c#L698 No
ending delimiter '%c' found]
- [https://github.com/php/php-src/blob/master/ext/pcre/php_pcre.c#L700 No
ending matching delimiter '%c' found]
- [https://github.com/php/php-src/blob/master/ext/pcre/php_pcre.c#L750
Unknown modifier '%c']
- [https://github.com/php/php-src/blob/master/ext/pcre/php_pcre.c#L752 NUL
is not a valid modifier]
- [https://github.com/php/php-src/blob/master/ext/pcre/php_pcre.c#L764 The
/e modifier is no longer supported, use preg_replace_callback instead]
- [https://github.com/php/php-src/blob/master/ext/pcre/php_pcre.c#L779
Failed to generate locale character tables]
- [https://github.com/php/php-src/blob/master/ext/pcre/php_pcre.c#L801
Compilation failed: %s at offset %zu]
- [https://github.com/php/php-src/blob/master/ext/pcre/php_pcre.c#L852
Internal pcre2_pattern_info() error %d]
- [https://github.com/php/php-src/blob/master/ext/pcre/php_pcre.c#L862
Internal pcre_pattern_info() error %d]
=== Which of these failures could happen here?
In the context of `WP_Rewrite::using_index_permalinks()`, there is only
one possible failure: `WP_Rewrite::$index` contains the delimiter, `#`.
[https://3v4l.org/M94EH 3v4l].
The `index` property is set to `'index.php'` by default. However, it's a
`public` property and could therefore be directly overridden, or set in a
subclass of `WP_Rewrite`.
WP Directory shows:
- [https://wpdirectory.net/search/01H4TBP4HXJZQ8G58BGB6A0QFE 0 plugins]
directly assign a value to this property.
- [https://wpdirectory.net/search/01H4TBT9RAKWGY41CCPD320RBA 0 themes]
directly assign a value to this property.
- [https://wpdirectory.net/search/01H4TBW9JQNVX9Q580Z0FPD6DH 1 plugin]
extends the `WP_Rewrite` class, but does not set a value for the property.
- [https://wpdirectory.net/search/01H4TC0P3CYKZ27FK2KBGJQSP5 0 themes]
extend the `WP_Rewrite` class.
=== Can we prevent the potential failure?
Yes, by using `preg_quote( $this->index, '#' )`. [https://3v4l.org/CYsS7
3v4l].
=== ''Should'' we prevent the potential failure?
As demonstrated by the failure, the regex is currently susceptible to
special characters. When the default value of `$this->index`
(`'index.php'`) is concatenated, the resulting pattern is
`'#^/*index.php#'`.
See the `.`? The `preg_match()` call is currently vulnerable to `index[any
character except newline]php`. [https://3v4l.org/LQSOG 3v4l].
Given this, it's possible that a developer out there has set a custom
`$index` value that contains a regex pattern. However, I think that's very
unlikely.
Contrarily, the flawed regex has the potential to cause false positives
if, for example, the permalink structure starts with `/index2php`. While
that's also unlikely, using `preg_quote()` here would prevent that false
positive.
-----
== Call for opinions
1. Should we just change the documented return type and description? If
so, what makes sense here?
2. OR should we cast the result to `bool`?
3. ADDITIONALLY should we use `preg_quote()` here to prevent the potential
failure?
--
Ticket URL: <https://core.trac.wordpress.org/ticket/58764>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list