missing_auth
Users::save() INSERT/UPDATEs {$wpdb->prefix}wlr_optin_users by user_email with no ownership check. Four unauthenticated (or customer) writers call updateUserOptInStatus() with an attacker-chosen email: (1) saveCheckoutFormData on woocommerce_checkout_create_order uses the order billing_email plus POST accept_wployalty_membership (unchecked box is absent → status 0). Guest checkout is enabled by default and WooCommerce does not stop a guest from using a registered customer’s email as billing_email. (2) checkBlockCheckoutEarning on woocommerce_store_api_checkout_update_customer_from_request reads $customer->get_billing_email() and extensions.wlopt_checkout_block.wlr_optin during Store API place-order (permission_callback __return_true, cart token only). (3) updateOrderFromRequest json_decodes the raw Store API body and uses billing_address.email. (4) registerUserHandler on user_register loads the new account email then immediately replaces it with Input::get('email') from $_REQUEST, so an extra email=victim@… field on wp-login registration or checkout create-account writes the victim’s row while the order/account stays on the attacker. Contrast: the shortcode AJAX correctly binds to get_login_user_email(). Observable: victim optin_status=0; preventWPLoyaltyMembership then hides loyalty UI and blocks earning/redemption for that email on subsequent requests.
App/Controller/Site/Main.php:355-386,476-485,540-581
RecommendationBind opt-in writes to a proven identity: for logged-in users always use get_current_user_id() / account email (ignore billing_email and $_REQUEST['email']). For guests, INSERT a row for the order/session email only when no wp_users / wlr_optin_users row already exists for that address; never UPDATE another customer’s optin_status from checkout or registration. In registerUserHandler use get_userdata($user_id)->user_email and drop Input::get('email').