Skip to content

Commit ceda3b9

Browse files
authored
Merge pull request #297 from plausible/fix_speed_module
Improved: hardened Proxy Speed Module, to make sure it doesn't disable plugins on non-proxy requests.
2 parents 8c36f00 + 4c416a3 commit ceda3b9

2 files changed

Lines changed: 63 additions & 25 deletions

File tree

mu-plugin/plausible-proxy-speed-module.php

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,26 @@
44
* Description: Speeds up Plausible Analytics' proxy for avoiding ad blockers.
55
* Plugin URI: https://plausible.io
66
* Author: Plausible HQ
7-
* Version: 1.0.0
7+
* Version: 1.0.1
88
* Author URI: https://plausible.io
99
*
1010
* Text Domain: plausible-analytics
1111
*/
1212

1313
class PlausibleProxySpeed {
1414
/**
15-
* Is current request a request to our proxy?
15+
* Is the current request a request to our proxy?
1616
*
1717
* @var bool
1818
*/
19-
private $is_proxy_request = false;
19+
private $is_proxy_request;
2020

2121
/**
2222
* Current request URI.
2323
*
2424
* @var string
2525
*/
26-
private $request_uri = '';
26+
private $request_uri;
2727

2828
/**
2929
* Build properties.
@@ -38,27 +38,42 @@ public function __construct() {
3838
}
3939

4040
/**
41-
* Helper method to retrieve Request URI. Checks several globals.
41+
* Helper method to retrieve Request URI.
4242
*
43-
* @return mixed
43+
* @return string
4444
*/
4545
private function get_request_uri() {
46-
return $_SERVER[ 'REQUEST_URI' ];
46+
return $_SERVER['REQUEST_URI'] ?? '';
4747
}
4848

4949
/**
50-
* Check if current request is a proxy request.
50+
* Check if the current request is a proxy request.
51+
*
52+
* The namespace must appear as a path segment under the REST prefix
53+
* (e.g. /wp-json/<namespace>[/...]). Substring matches in query
54+
* strings, fragments, or unrelated path segments are rejected.
5155
*
5256
* @return bool
5357
*/
5458
private function is_proxy_request() {
55-
$namespace = get_option( 'plausible_analytics_proxy_resources' )[ 'namespace' ] ?? '';
59+
$namespace = get_option( 'plausible_analytics_proxy_resources' )['namespace'] ?? '';
5660

5761
if ( ! $namespace ) {
5862
return false;
5963
}
6064

61-
return strpos( $this->request_uri, $namespace ) !== false;
65+
$path = parse_url( $this->request_uri, PHP_URL_PATH );
66+
67+
if ( ! is_string( $path ) || $path === '' ) {
68+
return false;
69+
}
70+
71+
$expected = function_exists( 'rest_url' )
72+
? untrailingslashit( (string) wp_parse_url( rest_url( trim( $namespace, '/' ) ), PHP_URL_PATH ) )
73+
: '/wp-json/' . trim( $namespace, '/' );
74+
75+
return $path === $expected
76+
|| str_starts_with( $path, $expected . '/' );
6277
}
6378

6479
/**
@@ -73,6 +88,10 @@ private function init() {
7388
/**
7489
* Filter the list of active plugins for custom endpoint requests.
7590
*
91+
* Uses basename() exact-match comparison instead of strpos(), so a
92+
* plugin file path can only match if its filename is exactly in the
93+
* allowlist.
94+
*
7695
* @param array $active_plugins The list of active plugins.
7796
*
7897
* @return array The filtered list of active plugins.
@@ -86,11 +105,8 @@ public function filter_active_plugins( $active_plugins ) {
86105
$filtered_plugins = [];
87106

88107
foreach ( $active_plugins as $plugin ) {
89-
foreach ( $allowed_plugin_files as $allowed_plugin_file ) {
90-
if ( strpos( $plugin, $allowed_plugin_file ) !== false ) {
91-
$filtered_plugins[] = $plugin;
92-
break;
93-
}
108+
if ( in_array( basename( $plugin ), $allowed_plugin_files, true ) ) {
109+
$filtered_plugins[] = $plugin;
94110
}
95111
}
96112

src/Admin/Upgrades.php

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ class Upgrades {
2626
/**
2727
* Constructor for Upgrades.
2828
*
29-
* @return void
3029
* @since 1.3.0
3130
* @access public
31+
* @return void
3232
*/
3333
public function __construct() {
3434
add_action( 'init', [ $this, 'run' ] );
@@ -38,13 +38,13 @@ public function __construct() {
3838
* Register routines for upgrades.
3939
* This is intended for automatic upgrade routines having less resource intensive tasks.
4040
*
41+
* @since 1.3.0
42+
* @access public
4143
* @return void
4244
*
4345
* @throws Exception
4446
*
4547
* @codeCoverageIgnore
46-
* @since 1.3.0
47-
* @access public
4848
*/
4949
public function run() {
5050
$plausible_analytics_version = get_option( 'plausible_analytics_version' );
@@ -98,17 +98,21 @@ public function run() {
9898
$this->upgrade_to_254();
9999
}
100100

101+
if ( version_compare( $plausible_analytics_version, '2.5.8', '<' ) ) {
102+
$this->upgrade_to_258();
103+
}
104+
101105
// Add required upgrade routines for future versions here.
102106
}
103107

104108
/**
105109
* Upgrade routine for 1.2.5
106110
* Cleans Custom Domain related options from database, as it was removed in this version.
107111
*
108-
* @return void
109-
* @codeCoverageIgnore
110112
* @since 1.2.5
111113
* @access public
114+
* @return void
115+
* @codeCoverageIgnore
112116
*/
113117
public function upgrade_to_125() {
114118
$old_settings = Helpers::get_settings();
@@ -138,13 +142,12 @@ public function upgrade_to_125() {
138142
/**
139143
* Get rid of the previous "example.com" default for self_hosted_domain.
140144
*
145+
* @since 1.2.6
141146
* @return void
142147
* @codeCoverageIgnore
143-
* @since 1.2.6
144148
*/
145149
public function upgrade_to_126() {
146150
$old_settings = Helpers::get_settings();
147-
$new_settings = $old_settings;
148151

149152
if ( ! empty( $old_settings['self_hosted_domain'] ) && strpos( $old_settings['self_hosted_domain'], 'example.com' ) !== false ) {
150153
Helpers::update_setting( 'self_hosted_domain', '' );
@@ -265,11 +268,11 @@ public function upgrade_to_210() {
265268
/**
266269
* If EDD is active and Ecommerce is enabled, create goals after updating the plugin.
267270
*
271+
* @since v2.3.0
272+
*
268273
* @return void
269274
*
270275
* @codeCoverageIgnore because all we'd be doing is testing the Plugins API.
271-
* @since v2.3.0
272-
*
273276
*/
274277
public function upgrade_to_230() {
275278
$settings = Helpers::get_settings();
@@ -367,7 +370,7 @@ public function upgrade_to_254() {
367370
return;
368371
}
369372

370-
// Show CE notice if self-hosted domain is set, otherwise show Cloud notice.
373+
// Show CE notice if Self-hosted Domain is set, otherwise show Cloud notice.
371374
if ( ! empty( $self_hosted_domain ) ) {
372375
add_action( 'admin_notices', [ $this, 'show_ce_api_token_notice' ] );
373376

@@ -377,6 +380,25 @@ public function upgrade_to_254() {
377380
add_action( 'admin_notices', [ $this, 'show_cloud_api_token_notice' ] );
378381
}
379382

383+
/**
384+
* Updates the Proxy Module if Proxy is enabled.
385+
*
386+
* @return void
387+
*
388+
* @codeCoverageIgnore
389+
*/
390+
public function upgrade_to_258() {
391+
$proxy_enabled = Helpers::proxy_enabled();
392+
393+
if ( $proxy_enabled ) {
394+
$installer = new Module();
395+
396+
$installer->install();
397+
}
398+
399+
update_option( 'plausible_analytics_version', '2.5.8' );
400+
}
401+
380402
/**
381403
* Display a notice to CE users that haven't entered an API token yet.
382404
*

0 commit comments

Comments
 (0)