PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.4.2
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.4.2
1.4.2 1.4.1 1.4.0 1.3.7 1.3.6 1.3.5 1.3.4 1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 All 36 releases
← All changes | includes/class-settings-manager.php +60 -1 1.3.2 → 1.4.2 View file →
@@ -172,9 +172,19 @@
172 172 * @param mixed $actual Value of the gating constant, or null.
173 173 * @param string $on Gating constant name.
174 174 */
175 175 $actual = apply_filters( 'xspeed_constant_gate_value', $actual, $on );
176 - if ( $actual !== ( $gate['is'] ?? null ) ) {
176 + $want = $gate['is'] ?? null;
177 + /*
178 + * `case_insensitive` is for gates on another plugin's
179 + * convention that is read that way at its source: Redis
180 + * Object Cache matches WP_REDIS_SCHEME with strcasecmp(), so
181 + * `UNIX` is a socket there and has to be one here. (#566)
182 + */
183 + $match = ! empty( $gate['case_insensitive'] ) && is_string( $actual ) && is_string( $want )
184 + ? 0 === strcasecmp( $actual, $want )
185 + : $actual === $want;
186 + if ( ! $match ) {
177 187 continue;
178 188 }
179 189 }
180 190 return $name;
@@ -1086,8 +1096,26 @@
1086 1096 if ( self::is_secret_field( $key, $spec ) && array_key_exists( $key, $settings ) ) {
1087 1097 $settings[ $key ] = self::mask_secret_value( (string) $settings[ $key ] );
1088 1098 }
1089 1099 }
1100 +
1101 + /*
1102 + * Preserved keys are carried through by `get()` and have no schema, so
1103 + * this loop never saw them — a module preserving a credential would
1104 + * print it here in full. The MCP module preserves its pairing token,
1105 + * which is what made that concrete.
1106 + *
1107 + * Matched by NAME, since a key with no spec has no declared type. A
1108 + * scalar only: `mask_secret_value` takes a string, and a preserved key
1109 + * can hold an array (the REST-cache `rules`), which is not a secret
1110 + * and must not be flattened into one.
1111 + */
1112 + foreach ( $module->preserved_keys() as $key ) {
1113 + if ( array_key_exists( $key, $settings ) && is_scalar( $settings[ $key ] ) && self::is_secret_key( $key ) ) {
1114 + $settings[ $key ] = self::mask_secret_value( (string) $settings[ $key ] );
1115 + }
1116 + }
1117 +
1090 1118 return $settings;
1091 1119 }
1092 1120
1093 1121 /**
@@ -1746,8 +1774,10 @@
1746 1774 $u = esc_url_raw( (string) $item );
1747 1775 if ( $u ) {
1748 1776 $out[] = $u;
1749 1777 }
1778 + } elseif ( 'path' === $item_type ) {
1779 + $out[] = self::sanitize_path_pattern( (string) $item );
1750 1780 } else {
1751 1781 $out[] = sanitize_text_field( (string) $item );
1752 1782 }
1753 1783 }
@@ -1820,8 +1850,37 @@
1820 1850 if ( isset( $parts['user'] ) || isset( $parts['pass'] ) ) {
1821 1851 return false;
1822 1852 }
1823 1853 return true;
1854 + }
1855 +
1856 + /**
1857 + * sanitize_text_field() for a URL path pattern, minus one step: it keeps
1858 + * percent-encoded octets.
1859 + *
1860 + * sanitize_text_field() deletes every `%XX`, so an excluded URL pasted as
1861 + * `/%e7%ac%ac%e5%8d%81/` (how WordPress spells a Chinese slug) was saved
1862 + * as `//`, and `/%e8%b3%bc%e7%89%a9%e8%bb%8a` as `/`, which "contains"-
1863 + * matches every page and turned the page cache off for the whole site.
1864 + * The `%` is swapped for a private-use character while the rest of
1865 + * sanitize_text_field() runs (tags, line breaks, invalid UTF-8), then
1866 + * swapped back.
1867 + *
1868 + * Escapes are stored in lower case, the spelling the page cache matches
1869 + * paths in. An entry copied from Chrome's address bar arrives as
1870 + * `/%E8%81%AF…/`; stored as typed, it never matched.
1871 + */
1872 + private static function sanitize_path_pattern( string $value ): string {
1873 + $mark = "\u{E000}";
1874 + $value = str_replace( $mark, '', $value );
1875 + $value = str_replace( $mark, '%', sanitize_text_field( str_replace( '%', $mark, $value ) ) );
1876 + return (string) preg_replace_callback(
1877 + '/%[0-9a-fA-F]{2}/',
1878 + static function ( array $m ): string {
1879 + return strtolower( $m[0] );
1880 + },
1881 + $value
1882 + );
1824 1883 }
1825 1884
1826 1885 /**
1827 1886 * Whether a URL looks like an image — used to gate `media` fields so a