Commit 6fdf0963b85 for woocommerce

commit 6fdf0963b8559985ecf4a465e07a5de7f373c3c2
Author: Justin P <228780+layoutd@users.noreply.github.com>
Date:   Fri Sep 4 10:43:42 2026 +0200

    Harden wcAnalytics config global and webpack public path assignment (#68346)

diff --git a/packages/php/woocommerce-analytics/changelog/wooa7s-2042-config-global-hardening b/packages/php/woocommerce-analytics/changelog/wooa7s-2042-config-global-hardening
new file mode 100644
index 00000000000..84aa4c4bcf3
--- /dev/null
+++ b/packages/php/woocommerce-analytics/changelog/wooa7s-2042-config-global-hardening
@@ -0,0 +1,4 @@
+Significance: patch
+Type: security
+
+Always initialize the front-end `window.wcAnalytics` object from scratch and only accept a string as the webpack public path.
diff --git a/packages/php/woocommerce-analytics/src/class-universal.php b/packages/php/woocommerce-analytics/src/class-universal.php
index bed286783c6..7a466211d6a 100644
--- a/packages/php/woocommerce-analytics/src/class-universal.php
+++ b/packages/php/woocommerce-analytics/src/class-universal.php
@@ -82,7 +82,8 @@ class Universal {
 		?>
 		<script type="text/javascript">
 			(function() {
-				window.wcAnalytics = window.wcAnalytics || {};
+				// Always start from a fresh object: a `||` fallback would keep a same-named DOM element alive.
+				window.wcAnalytics = {};
 				const wcAnalytics = window.wcAnalytics;

 				// Set the assets URL for webpack to find the split assets.
diff --git a/packages/php/woocommerce-analytics/src/client/public-path.ts b/packages/php/woocommerce-analytics/src/client/public-path.ts
index 8af63d875d9..91cae49e2c8 100644
--- a/packages/php/woocommerce-analytics/src/client/public-path.ts
+++ b/packages/php/woocommerce-analytics/src/client/public-path.ts
@@ -6,8 +6,11 @@
  * Unfortunately we can't set `publicPath: 'auto'` because WordPress.com Simple's JS concatenation breaks it (and other plugins that do JS concatenation probably would too).
  * @see https://webpack.js.org/guides/public-path/#on-the-fly
  */
-if ( typeof window === 'object' && window.wcAnalytics?.assets_url ) {
+const assetsUrl = typeof window === 'object' ? window.wcAnalytics?.assets_url : undefined;
+
+// Only a string may become the public path: a same-named DOM element is truthy and stringifies to its href.
+if ( typeof assetsUrl === 'string' ) {
 	// @ts-expect-error: __webpack_public_path__ is set globally by webpack, ignore TS2304
 	// eslint-disable-next-line no-global-assign
-	__webpack_public_path__ = window.wcAnalytics.assets_url;
+	__webpack_public_path__ = assetsUrl;
 }
diff --git a/packages/php/woocommerce-analytics/tests/php/Universal_Page_Output_Test.php b/packages/php/woocommerce-analytics/tests/php/Universal_Page_Output_Test.php
index 5229e5c7d9a..298f2b71dc1 100644
--- a/packages/php/woocommerce-analytics/tests/php/Universal_Page_Output_Test.php
+++ b/packages/php/woocommerce-analytics/tests/php/Universal_Page_Output_Test.php
@@ -312,6 +312,18 @@ class Universal_Page_Output_Test extends BaseTestCase {
 		);
 	}

+	/**
+	 * Test that the page output always starts `window.wcAnalytics` from a fresh
+	 * object. A `window.wcAnalytics || {}` fallback keeps a same-named DOM element
+	 * alive, and the assignments that follow silently fail to land on it.
+	 */
+	public function test_page_output_initializes_config_from_a_fresh_object(): void {
+		$output = $this->render_analytics_data();
+
+		$this->assertStringContainsString( 'window.wcAnalytics = {};', $output );
+		$this->assertStringNotContainsString( 'window.wcAnalytics ||', $output );
+	}
+
 	/**
 	 * Test that the server-fired pixel path keeps its request-derived
 	 * properties. That path runs on uncached requests and is the only place