fix(products): Add comprehensive data sanitization
Products module had NO sanitization - fixed to match Orders/Coupons/Customers Issue: ❌ No sanitization in create_product ❌ No sanitization in update_product ❌ Direct assignment of raw user input ❌ Potential XSS and injection vulnerabilities ❌ Inconsistent with other modules Changes Made: 1. Created Sanitization Helpers (Lines 23-65): ✅ sanitize_text() - Text fields (name, SKU) ✅ sanitize_textarea() - Descriptions (allows newlines) ✅ sanitize_number() - Prices, dimensions (removes non-numeric) ✅ sanitize_slug() - URL slugs (uses sanitize_title) 2. Fixed create_product() (Lines 278-317): ✅ Name → sanitize_text() ✅ Slug → sanitize_slug() ✅ Status → sanitize_key() ✅ Description → sanitize_textarea() ✅ Short description → sanitize_textarea() ✅ SKU → sanitize_text() ✅ Regular price → sanitize_number() ✅ Sale price → sanitize_number() ✅ Weight → sanitize_number() ✅ Length → sanitize_number() ✅ Width → sanitize_number() ✅ Height → sanitize_number() 3. Fixed update_product() (Lines 377-398): ✅ Same sanitization as create ✅ All text fields sanitized ✅ All numeric fields sanitized ✅ Status fields use sanitize_key() Sanitization Logic: Text Fields: - sanitize_text_field() + trim() - Prevents XSS attacks - Example: '<script>alert(1)</script>' → '' Textarea Fields: - sanitize_textarea_field() + trim() - Allows newlines for descriptions - Prevents XSS but keeps formatting Numbers: - Remove non-numeric except . and - - Example: 'abc123.45' → '123.45' - Example: '10,000' → '10000' Slugs: - sanitize_title() - Creates URL-safe slugs - Example: 'Product Name!' → 'product-name' Module Audit Results: ✅ Orders: FIXED (comprehensive sanitization) ✅ Coupons: GOOD (already has sanitization) ✅ Customers: GOOD (already has sanitization) ✅ Products: FIXED (added comprehensive sanitization) All modules now have consistent, secure data handling!
This commit is contained in:
@@ -20,6 +20,50 @@ use WC_Product_Variation;
|
||||
|
||||
class ProductsController {
|
||||
|
||||
/**
|
||||
* Sanitize text field
|
||||
*/
|
||||
private static function sanitize_text($value) {
|
||||
if (!isset($value) || $value === '') {
|
||||
return '';
|
||||
}
|
||||
$sanitized = sanitize_text_field($value);
|
||||
return trim($sanitized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize textarea (allows newlines)
|
||||
*/
|
||||
private static function sanitize_textarea($value) {
|
||||
if (!isset($value) || $value === '') {
|
||||
return '';
|
||||
}
|
||||
$sanitized = sanitize_textarea_field($value);
|
||||
return trim($sanitized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize numeric value
|
||||
*/
|
||||
private static function sanitize_number($value) {
|
||||
if (!isset($value) || $value === '') {
|
||||
return '';
|
||||
}
|
||||
// Remove non-numeric except decimal point and minus
|
||||
$sanitized = preg_replace('/[^0-9.-]/', '', $value);
|
||||
return $sanitized !== '' ? $sanitized : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize slug
|
||||
*/
|
||||
private static function sanitize_slug($value) {
|
||||
if (!isset($value) || $value === '') {
|
||||
return '';
|
||||
}
|
||||
return sanitize_title($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register REST API routes
|
||||
*/
|
||||
@@ -231,24 +275,24 @@ class ProductsController {
|
||||
$product = new WC_Product_Simple();
|
||||
}
|
||||
|
||||
// Set basic data
|
||||
$product->set_name($data['name']);
|
||||
// Set basic data - sanitize all inputs
|
||||
$product->set_name(self::sanitize_text($data['name']));
|
||||
if (!empty($data['slug'])) {
|
||||
$product->set_slug($data['slug']);
|
||||
$product->set_slug(self::sanitize_slug($data['slug']));
|
||||
}
|
||||
$product->set_status($data['status'] ?? 'publish');
|
||||
$product->set_description($data['description'] ?? '');
|
||||
$product->set_short_description($data['short_description'] ?? '');
|
||||
$product->set_status(sanitize_key($data['status'] ?? 'publish'));
|
||||
$product->set_description(self::sanitize_textarea($data['description'] ?? ''));
|
||||
$product->set_short_description(self::sanitize_textarea($data['short_description'] ?? ''));
|
||||
|
||||
if (!empty($data['sku'])) {
|
||||
$product->set_sku($data['sku']);
|
||||
$product->set_sku(self::sanitize_text($data['sku']));
|
||||
}
|
||||
|
||||
if (!empty($data['regular_price'])) {
|
||||
$product->set_regular_price($data['regular_price']);
|
||||
$product->set_regular_price(self::sanitize_number($data['regular_price']));
|
||||
}
|
||||
if (!empty($data['sale_price'])) {
|
||||
$product->set_sale_price($data['sale_price']);
|
||||
$product->set_sale_price(self::sanitize_number($data['sale_price']));
|
||||
}
|
||||
|
||||
$product->set_manage_stock($data['manage_stock'] ?? false);
|
||||
@@ -258,18 +302,18 @@ class ProductsController {
|
||||
}
|
||||
$product->set_stock_status($data['stock_status'] ?? 'instock');
|
||||
|
||||
// Optional fields
|
||||
// Optional fields - sanitize dimensions
|
||||
if (!empty($data['weight'])) {
|
||||
$product->set_weight($data['weight']);
|
||||
$product->set_weight(self::sanitize_number($data['weight']));
|
||||
}
|
||||
if (!empty($data['length'])) {
|
||||
$product->set_length($data['length']);
|
||||
$product->set_length(self::sanitize_number($data['length']));
|
||||
}
|
||||
if (!empty($data['width'])) {
|
||||
$product->set_width($data['width']);
|
||||
$product->set_width(self::sanitize_number($data['width']));
|
||||
}
|
||||
if (!empty($data['height'])) {
|
||||
$product->set_height($data['height']);
|
||||
$product->set_height(self::sanitize_number($data['height']));
|
||||
}
|
||||
|
||||
// Virtual and downloadable
|
||||
@@ -330,15 +374,15 @@ class ProductsController {
|
||||
return new WP_Error('product_not_found', __('Product not found', 'woonoow'), ['status' => 404]);
|
||||
}
|
||||
|
||||
// Update basic data
|
||||
if (isset($data['name'])) $product->set_name($data['name']);
|
||||
if (isset($data['slug'])) $product->set_slug($data['slug']);
|
||||
if (isset($data['status'])) $product->set_status($data['status']);
|
||||
if (isset($data['description'])) $product->set_description($data['description']);
|
||||
if (isset($data['short_description'])) $product->set_short_description($data['short_description']);
|
||||
if (isset($data['sku'])) $product->set_sku($data['sku']);
|
||||
if (isset($data['regular_price'])) $product->set_regular_price($data['regular_price']);
|
||||
if (isset($data['sale_price'])) $product->set_sale_price($data['sale_price']);
|
||||
// Update basic data - sanitize all inputs
|
||||
if (isset($data['name'])) $product->set_name(self::sanitize_text($data['name']));
|
||||
if (isset($data['slug'])) $product->set_slug(self::sanitize_slug($data['slug']));
|
||||
if (isset($data['status'])) $product->set_status(sanitize_key($data['status']));
|
||||
if (isset($data['description'])) $product->set_description(self::sanitize_textarea($data['description']));
|
||||
if (isset($data['short_description'])) $product->set_short_description(self::sanitize_textarea($data['short_description']));
|
||||
if (isset($data['sku'])) $product->set_sku(self::sanitize_text($data['sku']));
|
||||
if (isset($data['regular_price'])) $product->set_regular_price(self::sanitize_number($data['regular_price']));
|
||||
if (isset($data['sale_price'])) $product->set_sale_price(self::sanitize_number($data['sale_price']));
|
||||
|
||||
if (isset($data['manage_stock'])) {
|
||||
$product->set_manage_stock($data['manage_stock']);
|
||||
@@ -347,11 +391,11 @@ class ProductsController {
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['stock_status'])) $product->set_stock_status($data['stock_status']);
|
||||
if (isset($data['weight'])) $product->set_weight($data['weight']);
|
||||
if (isset($data['length'])) $product->set_length($data['length']);
|
||||
if (isset($data['width'])) $product->set_width($data['width']);
|
||||
if (isset($data['height'])) $product->set_height($data['height']);
|
||||
if (isset($data['stock_status'])) $product->set_stock_status(sanitize_key($data['stock_status']));
|
||||
if (isset($data['weight'])) $product->set_weight(self::sanitize_number($data['weight']));
|
||||
if (isset($data['length'])) $product->set_length(self::sanitize_number($data['length']));
|
||||
if (isset($data['width'])) $product->set_width(self::sanitize_number($data['width']));
|
||||
if (isset($data['height'])) $product->set_height(self::sanitize_number($data['height']));
|
||||
|
||||
// Categories
|
||||
if (isset($data['categories'])) {
|
||||
|
||||
Reference in New Issue
Block a user