Skip to content

v3.6.0: Security, Performance & Compatibility Issues β€” Code Review FindingsΒ #3900

@betayazilim

Description

@betayazilim

πŸ”Ž Isolate the bug

  • I have confirmed this occurs in the most recent version of WordPress, WooCommerce, and Meta for WooCommerce.
  • I have confirmed this occurs when only WooCommerce and Meta for WooCommerce are active and when using a default WordPress or WooCommerce theme.

✍️ Describe the bug

After a thorough code review of Meta for WooCommerce v3.6.0, I found several security, performance, and compatibility issues. These are not runtime bugs per se, but code-level issues that can cause security vulnerabilities, page cache invalidation, and WooCommerce compatibility gaps. Grouped by severity below.


πŸ”΄ High Severity

1. Unsanitized $_POST Input β€” Potential Stored XSS

File: facebook-commerce.php:1079

// Line 1076 sanitizes for plain text:
$woo_product->set_description( sanitize_text_field( wp_unslash( $_POST[ self::FB_PRODUCT_DESCRIPTION ] ) ) );
// But line 1079 passes raw input for rich text:
$woo_product->set_rich_text_description( $_POST[ self::FB_PRODUCT_DESCRIPTION ] );

The set_rich_text_description() method receives completely unsanitized POST data. The phpcs sanitization and unslash warnings are explicitly suppressed with phpcs:disable comments. This should use wp_kses_post() at minimum to prevent stored XSS.

2. Frontend setcookie() Breaks Page Cache

File: facebook-commerce-events-tracker.php:143

public function param_builder_server_setup() {
    $cookie_to_set = self::get_param_builder()->getCookiesToSet();
    if ( ! headers_sent() ) {
        foreach ( $cookie_to_set as $cookie ) {
            setcookie( $cookie->name, $cookie->value, ... );
        }
    }
}

This sets cookies on every frontend page load via PHP (server-side). This prevents WP Rocket, LiteSpeed Cache, Cloudflare, and any other page cache from caching the response β€” because the Set-Cookie header makes each response unique. For sites relying on full-page caching (most production WooCommerce stores), this silently disables caching site-wide.

Suggestion: Set cookies via JavaScript instead (client-side), or defer to an AJAX endpoint, so the HTML response remains cacheable.

3. Missing WooCommerce Block Compatibility Declarations

File: facebook-for-woocommerce.php:32-40

The plugin declares HPOS compatibility but does not declare compatibility with:

  • cart_checkout_blocks β€” WooCommerce checkout blocks have been default since WC 8.3+
  • product_block_editor β€” the new product editing experience
// Currently only this is declared:
FeaturesUtil::declare_compatibility( 'custom_order_tables', plugin_basename( __FILE__ ), true );

// Missing:
FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', plugin_basename( __FILE__ ), true );
FeaturesUtil::declare_compatibility( 'product_block_editor', plugin_basename( __FILE__ ), true );

WooCommerce shows incompatibility warnings in the admin for plugins that don't declare these.

4. readme.txt Stable Tag Version Mismatch

File: readme.txt:6

The plugin header says Version: 3.6.0, but readme.txt says Stable tag: 3.5.18. This is 12 versions behind and can cause confusion for WordPress.org update mechanisms and version checks.


🟑 Medium Severity

5. SQL Queries Without $wpdb->prepare()

File: includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php:115, 182

Two SQL queries execute directly on wp_postmeta without using $wpdb->prepare():

// Line 115
return (int) $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

// Line 182
return $wpdb->get_results( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

While these queries don't use user input, $wpdb->prepare() is a defense-in-depth best practice and the phpcs overrides suggest awareness of the issue.

6. Missing Nonce Verification on State-Changing GET Parameter

File: facebook-commerce.php:383

if ( isset( $_GET['remove_sticky'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    $this->remove_sticky_message();
}

The remove_sticky GET parameter triggers a state change without nonce verification, making it vulnerable to CSRF.

7. Direct error_log() Instead of wc_get_logger()

Multiple files use error_log() directly instead of WooCommerce's logging API:

  • includes/Admin/Settings_Screens/Localization_Settings_Trait.php:119, 129, 165
  • includes/API/Plugin/InitializeRestAPI.php:100
  • includes/ProductAttributeMapper.php:849
  • includes/fbproduct.php:1756

This bypasses WooCommerce's structured logging (WC > Status > Logs), making debugging harder for store admins.

8. Redundant get_post_meta() Calls

File: includes/Admin.php:1034-1042

Seven product attributes each use a ternary pattern that calls get_post_meta() 2-3 times:

$fb_brand = get_post_meta( $post->ID, FB_BRAND, true )
    ? get_post_meta( $post->ID, FB_BRAND, true )           // called again if truthy!
    : get_post_meta( $post->ID, '_wc_facebook_enhanced_...', true );

This results in 14-21 DB queries where 7 would suffice. Should assign to a variable first:

$fb_brand = get_post_meta( $post->ID, FB_BRAND, true )
    ?: get_post_meta( $post->ID, '_wc_facebook_enhanced_...', true );

🟒 Low Severity

9. Bare die() in AJAX Handler

File: includes/AJAX.php:98

} catch ( PluginException $exception ) {
    die();
}

Should use wp_send_json_error() or wp_die() for proper WordPress AJAX response handling.

10. Inline Script Echo

File: includes/fbutils.php:107

echo '<script>' . implode( PHP_EOL, $legacy_events ) . '</script>';

Should use wp_add_inline_script() for proper WordPress script management and CSP header compatibility.

πŸšΆβ€β™€οΈ Steps to reproduce

  1. Install Meta for WooCommerce v3.6.0 on WordPress 6.9 with WooCommerce 10.5
  2. For Issue Woocommerce "Save Settings" button hidden when in debug modeΒ #1: Edit any WooCommerce product with the Facebook product description field populated β€” raw HTML is stored without sanitization
  3. For Issue Support for add to cart pixel on ajax add to cartΒ #2: Load any frontend page and inspect response headers β€” Set-Cookie header is present on every response, preventing page caching
  4. For Issue Add to cart pixel not fired when item is added to cart from product pageΒ #3: Go to WooCommerce > Settings > Advanced > Features β€” the plugin will show as not declaring block compatibility
  5. For Issue Allow multiple product purchase on Facebook Page or option to disable add to cart URLΒ #4: Compare readme.txt stable tag (3.5.18) with plugin header version (3.6.0)

βœ”οΈ Expected behavior

  1. Rich text description should be sanitized with wp_kses_post() or equivalent before storage
  2. CAPI cookies should be set via JavaScript (client-side) to avoid breaking server-side page caching
  3. Plugin should declare cart_checkout_blocks and product_block_editor compatibility
  4. readme.txt stable tag should match the plugin header version
  5. All SQL queries should use $wpdb->prepare() even when not handling user input
  6. State-changing actions should verify nonces
  7. Logging should use wc_get_logger() for WooCommerce integration

πŸ—ƒ Logs

Environment
  • WordPress: 6.9
  • WooCommerce: 10.5
  • Meta for WooCommerce: 3.6.0
  • PHP: 8.3
  • Cache Stack: WP Rocket + Cloudflare + Object Cache Pro (Redis)
  • HPOS: Enabled

All findings are from static code review of v3.6.0 source files.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions