From 3ae1108f99826e66b27aa3c0bd57c5913fcf35ab Mon Sep 17 00:00:00 2001 From: Oleksandr Mykhailenko Date: Thu, 9 Jul 2026 10:25:44 +0400 Subject: [PATCH 1/2] Security fix: prevent unauthenticated arbitrary list subscription (2.2.1) Add nonce verification and server-side list address allowlist validation to the add_list AJAX action. Previously the nopriv action accepted any list address and email without authentication or CSRF protection, allowing an unauthenticated attacker to enrol arbitrary addresses into the site owner's Mailgun lists using stored API credentials as a confused deputy. Reported by Pedro Pinho. Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 3 +++ mailgun.php | 20 ++++++++++++++++---- readme.md | 5 ++++- readme.txt | 5 ++++- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e013b7e..481f96a 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ Changelog ========= +2.2.1 (2026-07-09) +- Security fix: add nonce verification and server-side list address validation to the `add_list` AJAX action to prevent unauthenticated arbitrary list subscription (reported by Pedro Pinho) + 2.1.9 (2025-06-24) - Added fallback option. Merge PR for widget diff --git a/mailgun.php b/mailgun.php index aca1222..1ccc389 100755 --- a/mailgun.php +++ b/mailgun.php @@ -3,7 +3,7 @@ * Plugin Name: Mailgun * Plugin URI: http://wordpress.org/extend/plugins/mailgun/ * Description: Mailgun integration for WordPress - * Version: 2.2.0 + * Version: 2.2.1 * Requires PHP: 7.4 * Requires at least: 5.6 * Author: Mailgun @@ -317,11 +317,22 @@ public function getTrackingSettings(): array * @throws JsonException */ public function add_list(): void { + if ( ! isset( $_POST['mailgun_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['mailgun_nonce'] ) ), 'mailgun_subscribe' ) ) { + wp_send_json( [ 'status' => 403, 'message' => 'Invalid request.' ], 403 ); + return; + } + $name = sanitize_text_field( $_POST['name'] ?? null ); $email = sanitize_text_field( $_POST['email'] ?? null ); $list_addresses = []; - foreach ( $_POST['addresses'] as $address => $val ) { - $list_addresses[ sanitize_text_field( $address ) ] = sanitize_text_field( $val ); + + $valid_lists = array_column( $this->get_lists(), 'address' ); + + foreach ( ( $_POST['addresses'] ?? [] ) as $address => $val ) { + $sanitized_address = sanitize_text_field( $address ); + if ( in_array( $sanitized_address, $valid_lists, true ) ) { + $list_addresses[ $sanitized_address ] = sanitize_text_field( $val ); + } } if ( ! empty( $list_addresses ) ) { @@ -430,7 +441,8 @@ public function list_form( string $list_address, array $args = []): void { - + +