Commit 33d5065ceca for woocommerce
commit 33d5065ceca66aa2b862b243fd5f2f186e48f812
Author: Michal Iwanow <4765119+mcliwanow@users.noreply.github.com>
Date: Wed Oct 7 14:06:44 2026 +0200
Remove non-allowlisted query parameters from analytics URLs (#69526)
* Remove non-allowlisted query parameters from analytics URLs
Co-authored-by: Thomas Roberts <5656702+opr@users.noreply.github.com>
* Remove a stale PHPStan baseline entry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Thomas Roberts <5656702+opr@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
diff --git a/packages/php/woocommerce-analytics/changelog/fix-analytics-url-query-strings b/packages/php/woocommerce-analytics/changelog/fix-analytics-url-query-strings
new file mode 100644
index 00000000000..672a953d2aa
--- /dev/null
+++ b/packages/php/woocommerce-analytics/changelog/fix-analytics-url-query-strings
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fixed
+
+Remove non-allowlisted query parameters and fragments from analytics document location and referrer properties.
diff --git a/packages/php/woocommerce-analytics/composer.json b/packages/php/woocommerce-analytics/composer.json
index 079ed0b87e8..64f59e92a55 100644
--- a/packages/php/woocommerce-analytics/composer.json
+++ b/packages/php/woocommerce-analytics/composer.json
@@ -10,7 +10,8 @@
"automattic/jetpack-connection": "^8.1 || ^9.0",
"automattic/jetpack-constants": "^3.0 || ^4.0",
"automattic/jetpack-device-detection": "^3.4 || ^4.0",
- "automattic/block-delimiter": "^0.3 || ^0.4"
+ "automattic/block-delimiter": "^0.3 || ^0.4",
+ "automattic/tracks-shared-utils": "^1.0"
},
"require-dev": {
"yoast/phpunit-polyfills": "^4.0.0",
diff --git a/packages/php/woocommerce-analytics/composer.lock b/packages/php/woocommerce-analytics/composer.lock
index de77e78c2c3..0ae2f7bcdea 100644
--- a/packages/php/woocommerce-analytics/composer.lock
+++ b/packages/php/woocommerce-analytics/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "60ed9225503f99d3adb184cf6d7223a5",
+ "content-hash": "e78b6681ee9e0c0a47559de3dd00738b",
"packages": [
{
"name": "automattic/block-delimiter",
@@ -621,6 +621,50 @@
"source": "https://github.com/Automattic/jetpack-status/tree/v6.4.0"
},
"time": "2026-08-19T19:42:20+00:00"
+ },
+ {
+ "name": "automattic/tracks-shared-utils",
+ "version": "v1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Automattic/tracks-shared-utils.git",
+ "reference": "6828997db5c76b1e810535c1d2729355b095aa91"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Automattic/tracks-shared-utils/zipball/6828997db5c76b1e810535c1d2729355b095aa91",
+ "reference": "6828997db5c76b1e810535c1d2729355b095aa91",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpunit/phpunit": "^8.5 || ^9.6",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "php/src/functions.php"
+ ],
+ "psr-4": {
+ "Automattic\\TracksSharedUtils\\": "php/src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Shared allowlist and sanitization for URLs sent to Tracks.",
+ "homepage": "https://github.com/Automattic/tracks-shared-utils",
+ "support": {
+ "issues": "https://github.com/Automattic/tracks-shared-utils/issues",
+ "source": "https://github.com/Automattic/tracks-shared-utils"
+ },
+ "time": "2026-09-25T19:43:46+00:00"
}
],
"packages-dev": [
diff --git a/packages/php/woocommerce-analytics/package.json b/packages/php/woocommerce-analytics/package.json
index c99f112a73b..13e17b0d638 100644
--- a/packages/php/woocommerce-analytics/package.json
+++ b/packages/php/woocommerce-analytics/package.json
@@ -28,6 +28,7 @@
"watch": "pnpm build --watch"
},
"dependencies": {
+ "@automattic/tracks-shared-utils": "^1.0.0",
"debug": "4.4.3"
},
"devDependencies": {
diff --git a/packages/php/woocommerce-analytics/src/class-wc-analytics-tracking.php b/packages/php/woocommerce-analytics/src/class-wc-analytics-tracking.php
index 48a303b0253..b858d9ddfea 100644
--- a/packages/php/woocommerce-analytics/src/class-wc-analytics-tracking.php
+++ b/packages/php/woocommerce-analytics/src/class-wc-analytics-tracking.php
@@ -14,6 +14,8 @@ use Automattic\Jetpack\Device_Detection;
use Automattic\Jetpack\Device_Detection\User_Agent_Info;
use Automattic\Woocommerce_Analytics;
use WP_Error;
+use function Automattic\TracksSharedUtils\sanitize_url;
+use function Automattic\TracksSharedUtils\url_props;
/**
* WooCommerce Analytics Tracking class
@@ -609,8 +611,9 @@ class WC_Analytics_Tracking {
$event_properties = array_slice( $event_properties, 0, self::MAX_CLIENT_PROPERTIES_PER_EVENT, true );
}
- $values = array();
- $costs = array();
+ $values = array();
+ $costs = array();
+ $url_properties = array_fill_keys( url_props(), true );
foreach ( $event_properties as $key => $value ) {
// Dropped, not truncated: two long names could truncate to the same key.
@@ -618,6 +621,13 @@ class WC_Analytics_Tracking {
continue;
}
+ if ( isset( $url_properties[ $key ] ) ) {
+ if ( ! is_string( $value ) ) {
+ continue;
+ }
+ $value = sanitize_url( $value );
+ }
+
// Arrays are flattened later by get_properties(); bound their members too.
if ( is_array( $value ) ) {
$value = array_map(
@@ -894,17 +904,17 @@ class WC_Analytics_Tracking {
'_via_ua' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? $clean( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
'_via_ip' => self::get_user_ip_address(),
'_lg' => isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ? substr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ), 0, 5 ) : '',
- '_dr' => isset( $_SERVER['HTTP_REFERER'] ) ? $clean( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+ '_dr' => isset( $_SERVER['HTTP_REFERER'] ) && is_string( $_SERVER['HTTP_REFERER'] ) ? sanitize_url( sanitize_text_field( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) ) : '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
);
// Build the document location URL.
- $uri = isset( $_SERVER['REQUEST_URI'] ) ? $clean( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
- $host = isset( $_SERVER['HTTP_HOST'] ) ? $clean( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
- $data['_dl'] = isset( $_SERVER['REQUEST_SCHEME'] ) ? $clean( wp_unslash( $_SERVER['REQUEST_SCHEME'] ) ) . '://' . $host . $uri : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+ $uri = isset( $_SERVER['REQUEST_URI'] ) && is_string( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+ $host = isset( $_SERVER['HTTP_HOST'] ) && is_string( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+ $data['_dl'] = isset( $_SERVER['REQUEST_SCHEME'] ) && is_string( $_SERVER['REQUEST_SCHEME'] ) ? sanitize_url( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_SCHEME'] ) ) . '://' . $host . $uri ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
// Add _via_ref (referrer) for backward compatibility.
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
- $data['_via_ref'] = isset( $_SERVER['HTTP_REFERER'] ) ? $clean( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : '';
+ $data['_via_ref'] = isset( $_SERVER['HTTP_REFERER'] ) && is_string( $_SERVER['HTTP_REFERER'] ) ? sanitize_url( sanitize_text_field( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) ) : '';
// Headers are caller-supplied, and the referer lands here twice. Uncapped, one
// long Referer pushes the finished URL past MAX_PIXEL_URL_LENGTH and costs the
diff --git a/packages/php/woocommerce-analytics/src/client/analytics.ts b/packages/php/woocommerce-analytics/src/client/analytics.ts
index a95e96daac0..a58b2749436 100644
--- a/packages/php/woocommerce-analytics/src/client/analytics.ts
+++ b/packages/php/woocommerce-analytics/src/client/analytics.ts
@@ -2,6 +2,7 @@
* External dependencies
*/
import debugFactory from 'debug';
+import { sanitizeUrl } from '@automattic/tracks-shared-utils';
/**
* Internal dependencies
*/
@@ -202,10 +203,10 @@ export class Analytics {
eventProperties._sy = sy !== undefined ? sy : 0;
if ( document.location !== undefined ) {
- eventProperties._dl = document.location.toString();
+ eventProperties._dl = sanitizeUrl( document.location.toString() );
}
if ( document.referrer !== undefined ) {
- eventProperties._dr = document.referrer;
+ eventProperties._dr = sanitizeUrl( document.referrer );
}
};
diff --git a/packages/php/woocommerce-analytics/tests/php/WC_Analytics_Tracking_Reserved_Props_Test.php b/packages/php/woocommerce-analytics/tests/php/WC_Analytics_Tracking_Reserved_Props_Test.php
index 09762b2d232..423198d7804 100644
--- a/packages/php/woocommerce-analytics/tests/php/WC_Analytics_Tracking_Reserved_Props_Test.php
+++ b/packages/php/woocommerce-analytics/tests/php/WC_Analytics_Tracking_Reserved_Props_Test.php
@@ -128,6 +128,22 @@ class WC_Analytics_Tracking_Reserved_Props_Test extends BaseTestCase {
$this->assertNotContains( '_dr', $reserved );
}
+ /**
+ * @testdox Request-derived URLs retain only shared allowlisted query parameters.
+ */
+ public function test_server_details_retain_only_shared_allowlisted_query_parameters(): void {
+ $_SERVER['REQUEST_SCHEME'] = 'https';
+ $_SERVER['HTTP_HOST'] = 'example.com';
+ $_SERVER['REQUEST_URI'] = '/wp-json/wc/v3/products?utm_source=google&orderby=price&aff=partner&utm_campaign=spring&per_page=20';
+ $_SERVER['HTTP_REFERER'] = 'https://example.com/wp-admin/admin.php?page=wc-admin&utm_medium=referral#activity';
+
+ $details = WC_Analytics_Tracking::get_server_details();
+
+ $this->assertSame( 'https://example.com/wp-json/wc/v3/products?utm_source=google&aff=partner&utm_campaign=spring', $details['_dl'] );
+ $this->assertSame( 'https://example.com/wp-admin/admin.php?page=wc-admin&utm_medium=referral', $details['_dr'] );
+ $this->assertSame( 'https://example.com/wp-admin/admin.php?page=wc-admin&utm_medium=referral', $details['_via_ref'] );
+ }
+
/**
* The strip removes reserved names and leaves everything else — including
* arbitrary event-specific properties — untouched.
@@ -694,9 +710,9 @@ class WC_Analytics_Tracking_Reserved_Props_Test extends BaseTestCase {
}
/**
- * Keep typical client payloads unchanged.
+ * @testdox Keep typical client payloads while retaining only attribution parameters.
*/
- public function test_a_realistic_client_payload_is_not_capped(): void {
+ public function test_a_realistic_client_payload_retains_only_attribution_parameters(): void {
$properties = array(
'pi' => 731,
'pn' => 'Some Reasonably Long Product Name With Words',
@@ -705,10 +721,13 @@ class WC_Analytics_Tracking_Reserved_Props_Test extends BaseTestCase {
'pp' => 115.81,
'_lg' => 'en-GB',
'_dl' => 'https://example.com/product/some-reasonably-long-product-slug/?utm_source=x',
- '_dr' => 'https://example.com/shop/page/3/',
+ '_dr' => 'https://example.com/shop/page/3/?ref=email#products',
);
+ $expected = $properties;
+ $expected['_dl'] = 'https://example.com/product/some-reasonably-long-product-slug/?utm_source=x';
+ $expected['_dr'] = 'https://example.com/shop/page/3/?ref=email';
- $this->assertSame( $properties, WC_Analytics_Tracking::sanitize_client_properties( $properties ) );
+ $this->assertSame( $expected, WC_Analytics_Tracking::sanitize_client_properties( $properties ) );
}
/**
@@ -874,9 +893,9 @@ class WC_Analytics_Tracking_Reserved_Props_Test extends BaseTestCase {
}
/**
- * Trim long referrers without dropping the event.
+ * @testdox Strip long referrer query strings without dropping the event.
*/
- public function test_a_long_referer_costs_its_own_tail_not_the_event(): void {
+ public function test_a_long_referer_query_is_stripped_without_dropping_the_event(): void {
$_COOKIE['tk_ai'] = 'test-visitor-id-1234567890ab';
$_SERVER['HTTP_REFERER'] = 'https://example.com/?q=' . str_repeat( 'a', 5000 );
$this->reset_pixel_batch_queue();
@@ -888,22 +907,24 @@ class WC_Analytics_Tracking_Reserved_Props_Test extends BaseTestCase {
$props = $this->get_queued_pixel_props();
$this->assertSame( '42', $props['pi'] ?? null, 'The event payload must survive intact.' );
- $this->assertStringEndsWith( '…', $props['_dr'] ?? '', 'The referer is what gets trimmed.' );
+ $this->assertSame( 'https://example.com/', $props['_dr'] ?? null );
+ $this->assertSame( 'https://example.com/', $props['_via_ref'] ?? null );
$this->reset_pixel_batch_queue();
}
/**
- * Preserve common ad-click landing URLs.
+ * @testdox Preserve allowlisted parameters in common ad-click landing URLs.
*/
- public function test_an_ad_click_landing_url_survives_untouched(): void {
- $url = 'https://example.com/product-category/clothing/mens-shirts/?utm_source=google&utm_medium=cpc&utm_campaign=spring&gclid=Cj0KCQjw1viWBhD0ARIsAAM_oKnLQ8example1234567890abcdefghij&fbclid=IwAR2example1234567890abcdefghijklmnop';
+ public function test_an_ad_click_landing_url_preserves_allowlisted_parameters(): void {
+ $url = 'https://example.com/product-category/clothing/mens-shirts/?utm_source=google&utm_medium=cpc&utm_campaign=spring&color=blue&gclid=Cj0KCQjw1viWBhD0ARIsAAM_oKnLQ8example1234567890abcdefghij&fbclid=IwAR2example1234567890abcdefghijklmnop';
+ $expected = 'https://example.com/product-category/clothing/mens-shirts/?utm_source=google&utm_medium=cpc&utm_campaign=spring&gclid=Cj0KCQjw1viWBhD0ARIsAAM_oKnLQ8example1234567890abcdefghij&fbclid=IwAR2example1234567890abcdefghijklmnop';
$this->assertGreaterThan( 200, mb_strlen( $url ), 'A fixture under the old cap would prove nothing.' );
$sanitized = WC_Analytics_Tracking::sanitize_client_properties( array( '_dl' => $url ) );
- $this->assertSame( $url, $sanitized['_dl'] ?? null );
+ $this->assertSame( $expected, $sanitized['_dl'] ?? null );
}
/**
diff --git a/plugins/woocommerce/changelog/fix-analytics-url-query-strings b/plugins/woocommerce/changelog/fix-analytics-url-query-strings
new file mode 100644
index 00000000000..702b59a56c5
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-analytics-url-query-strings
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Remove non-allowlisted query parameters and fragments from analytics document location and referrer properties.
diff --git a/plugins/woocommerce/composer.json b/plugins/woocommerce/composer.json
index 9f7422d0a20..4754e88ad7e 100644
--- a/plugins/woocommerce/composer.json
+++ b/plugins/woocommerce/composer.json
@@ -50,6 +50,7 @@
"automattic/jetpack-config": "3.0.0",
"automattic/jetpack-connection": "^6.11.1",
"automattic/jetpack-constants": "^3.0.1",
+ "automattic/tracks-shared-utils": "^1.0",
"composer/installers": "^1.9",
"maxmind-db/reader": "^1.11",
"opis/json-schema": "*",
diff --git a/plugins/woocommerce/composer.lock b/plugins/woocommerce/composer.lock
index bcba9a2d236..23afdb9b503 100644
--- a/plugins/woocommerce/composer.lock
+++ b/plugins/woocommerce/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "7de5869e1b3a961f31d4e8427317c288",
+ "content-hash": "d04c0430452f13dd4fe7bc11bce08ce8",
"packages": [
{
"name": "automattic/block-delimiter",
@@ -664,6 +664,50 @@
},
"time": "2025-10-13T20:12:54+00:00"
},
+ {
+ "name": "automattic/tracks-shared-utils",
+ "version": "v1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Automattic/tracks-shared-utils.git",
+ "reference": "6828997db5c76b1e810535c1d2729355b095aa91"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Automattic/tracks-shared-utils/zipball/6828997db5c76b1e810535c1d2729355b095aa91",
+ "reference": "6828997db5c76b1e810535c1d2729355b095aa91",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpunit/phpunit": "^8.5 || ^9.6",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "php/src/functions.php"
+ ],
+ "psr-4": {
+ "Automattic\\TracksSharedUtils\\": "php/src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Shared allowlist and sanitization for URLs sent to Tracks.",
+ "homepage": "https://github.com/Automattic/tracks-shared-utils",
+ "support": {
+ "issues": "https://github.com/Automattic/tracks-shared-utils/issues",
+ "source": "https://github.com/Automattic/tracks-shared-utils"
+ },
+ "time": "2026-09-25T19:43:46+00:00"
+ },
{
"name": "composer/installers",
"version": "v1.12.0",
diff --git a/plugins/woocommerce/includes/tracks/class-wc-tracks.php b/plugins/woocommerce/includes/tracks/class-wc-tracks.php
index d2395ad9aff..5c9a8f06366 100644
--- a/plugins/woocommerce/includes/tracks/class-wc-tracks.php
+++ b/plugins/woocommerce/includes/tracks/class-wc-tracks.php
@@ -64,11 +64,11 @@ class WC_Tracks {
$data['_via_ua'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? wc_clean( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';
$data['_via_ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? wc_clean( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
$data['_lg'] = isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ? wc_clean( wp_unslash( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) : '';
- $data['_dr'] = isset( $_SERVER['HTTP_REFERER'] ) ? wc_clean( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : '';
+ $data['_dr'] = isset( $_SERVER['HTTP_REFERER'] ) && is_string( $_SERVER['HTTP_REFERER'] ) ? \Automattic\TracksSharedUtils\sanitize_url( sanitize_text_field( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) ) : '';
- $uri = isset( $_SERVER['REQUEST_URI'] ) ? wc_clean( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
- $host = isset( $_SERVER['HTTP_HOST'] ) ? wc_clean( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '';
- $data['_dl'] = isset( $_SERVER['REQUEST_SCHEME'] ) ? wc_clean( wp_unslash( $_SERVER['REQUEST_SCHEME'] ) ) . '://' . $host . $uri : '';
+ $uri = isset( $_SERVER['REQUEST_URI'] ) && is_string( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
+ $host = isset( $_SERVER['HTTP_HOST'] ) && is_string( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '';
+ $data['_dl'] = isset( $_SERVER['REQUEST_SCHEME'] ) && is_string( $_SERVER['REQUEST_SCHEME'] ) ? \Automattic\TracksSharedUtils\sanitize_url( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_SCHEME'] ) ) . '://' . $host . $uri ) : '';
return $data;
}
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 948d44c9e2b..c0c3b363487 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -31509,12 +31509,6 @@ parameters:
count: 1
path: includes/tracks/class-wc-tracks-footer-pixel.php
- -
- message: '#^Binary operation "\." between array\|string and ''\://'' results in an error\.$#'
- identifier: binaryOp.invalid
- count: 1
- path: includes/tracks/class-wc-tracks.php
-
-
message: '#^Method WC_Tracks\:\:track_woocommerce_allow_tracking_toggled\(\) has no return type specified\.$#'
identifier: missingType.return
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-tracks-test.php b/plugins/woocommerce/tests/php/includes/class-wc-tracks-test.php
index 1f49fda0f65..a42f10004f2 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-tracks-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-tracks-test.php
@@ -18,6 +18,27 @@ class WC_Tracks_Test extends \WC_Unit_Test_Case {
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-event.php';
}
+ /**
+ * @testdox Server details retain only shared allowlisted query parameters.
+ */
+ public function test_server_details_retain_only_shared_allowlisted_query_parameters(): void {
+ $server_snapshot = $_SERVER;
+
+ try {
+ $_SERVER['REQUEST_SCHEME'] = 'https';
+ $_SERVER['HTTP_HOST'] = 'example.com';
+ $_SERVER['REQUEST_URI'] = '/wp-json/wc/v3/products?utm_source=google&orderby=price&aff=partner&utm_campaign=spring&per_page=20';
+ $_SERVER['HTTP_REFERER'] = 'https://example.com/wp-admin/admin.php?page=wc-admin&utm_medium=referral#activity';
+
+ $details = WC_Tracks::get_server_details();
+
+ $this->assertSame( 'https://example.com/wp-json/wc/v3/products?utm_source=google&aff=partner&utm_campaign=spring', $details['_dl'] );
+ $this->assertSame( 'https://example.com/wp-admin/admin.php?page=wc-admin&utm_medium=referral', $details['_dr'] );
+ } finally {
+ $_SERVER = $server_snapshot;
+ }
+ }
+
/**
* Test that custom event properties are returned when passed.
*/
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index dd58ae2839f..99690fdf591 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2484,6 +2484,9 @@ importers:
packages/php/woocommerce-analytics:
dependencies:
+ '@automattic/tracks-shared-utils':
+ specifier: ^1.0.0
+ version: 1.0.0
debug:
specifier: 4.4.3
version: 4.4.3(supports-color@9.4.0)
@@ -4432,6 +4435,9 @@ packages:
react-dom: ^18.2.0
redux: ^4.2.1
+ '@automattic/tracks-shared-utils@1.0.0':
+ resolution: {integrity: sha512-yHXhAmfEK64a8LE1kweoKGgIW5XWe96mbTF99XD5lgRQcZVvWOSqiauQ7Q48OQVXu9K+isWLSXYxk5nGbdN+FA==, tarball: https://registry.npmjs.org/@automattic/tracks-shared-utils/-/tracks-shared-utils-1.0.0.tgz}
+
'@automattic/typography@1.0.0':
resolution: {integrity: sha512-TnT+vPaNUXQYwDsPCPxhNY0d4LnOKvrb0SizUCC5iybo5sfOlX/rYalGDyz6nPQDF0EBaQwMf7qhVsflFR0cBg==}
@@ -19625,6 +19631,8 @@ snapshots:
- '@types/react'
- supports-color
+ '@automattic/tracks-shared-utils@1.0.0': {}
+
'@automattic/typography@1.0.0': {}
'@automattic/viewport-react@1.0.1(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':