Published : June 14, 2026

CVE-2026-49774: RD Station <= 5.6.0 Authenticated (Contributor+) Remote Code Execution PoC, Patch Analysis & Rule

Severity High (CVSS 8.8)
CWE 94
Vulnerable Version 5.6.0
Patched Version 5.7.2
Disclosed June 3, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-49774:

This vulnerability is an authenticated Remote Code Execution vulnerability in the RD Station plugin for WordPress, affecting all versions up to and including 5.6.0. The weakness allows attackers with Contributor-level access or higher to execute arbitrary code on the server. The CVSS score is 8.8, reflecting the high impact and ease of exploitation.

Root Cause: The root cause lies in insufficient authorization checks across multiple AJAX handlers within the plugin. The code diff shows that in the vulnerable version, numerous AJAX endpoints lacked proper capability checks, relying only on a nonce that could be bypassed or was shared across different privilege levels. Key files affected include /integracao-rd-station/includes/events/rdsm_oauth_connected.php, rdsm_oauth_disconnected.php, rdsm_log_file.php, rdsm_integration_form_changed.php, rdsm_integration_form_woocommerce.php, rdsm_tracking_status_updated.php, and rdsm_settings_page_loaded.php. The vulnerability is further exacerbated by the log file handling in /integracao-rd-station/includes/helpers/rdsm_log_file_helper.php, where the log file path was derived unsafely from the refresh token stored in the database (get_option(‘rdsm_refresh_token’)), allowing for potential path traversal or code injection into log files that could later be executed.

Exploitation: An attacker with Contributor-level access can craft a POST request to /wp-admin/admin-ajax.php with specific actions and parameters. For example, sending a request with action=rdsm_authorization_check_nonce and the appropriate POST parameters to trigger the check_authorization() function, which lacks proper authorization. However, the more severe exploitation path involves the log file mechanism: the log filename was built using get_option(‘rdsm_refresh_token’) without validation, enabling an attacker to manipulate the refresh token via the save_refresh_token() method (if they can modify options) to write a malicious PHP snippet into the log directory under a controlled filename. With the log directory previously located under /uploads/rdsm_logs (a publicly accessible path), an attacker could directly access the log file and trigger code execution.

Patch Analysis: The patch introduces a new security helper class RDSMSecurityHelper to centralize authorization checks. Each AJAX endpoint now calls RDSMSecurityHelper::verify_admin_ajax() or RDSMSecurityHelper::verify_editor_or_admin_ajax(), which enforces proper capability validation (e.g., edit_post for the custom fields endpoint, manage_options for sensitive operations). Additionally, the log file path construction is moved into a private method get_log_file_path() within RDSMLogFileHelper, which uses a sanitized basename derived from RDSMSecurityHelper::get_log_file_basename(), preventing direct user-controlled data from being used in the file path. The log directory is also moved from /uploads/rdsm_logs (publicly accessible) to /rdstation_logs (outside uploads) to prevent direct HTTP access.

Impact: Successful exploitation allows an authenticated attacker with Contributor-level privileges to execute arbitrary PHP code on the server. This can lead to full site compromise, including data theft, malware injection, privilege escalation to administrator, and potential server takeover. The vulnerability is particularly dangerous because it requires only low-level Contributor access, which is easier to obtain than Administrator access.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/integracao-rd-station/config.php
+++ b/integracao-rd-station/config.php
@@ -1,38 +1,58 @@
 <?php

-define('RDSM_ASSETS_URL', plugin_dir_url(__FILE__) . 'assets');
-define('RDSM_SRC_DIR', dirname(__FILE__) . '/includes');
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
+if ( ! defined( 'RDSM_PLUGIN_DIR' ) ) {
+	define( 'RDSM_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
+}
+
+define( 'RDSM_ASSETS_URL', plugin_dir_url( __FILE__ ) . 'assets' );
+define( 'RDSM_SRC_DIR', RDSM_PLUGIN_DIR . 'includes' );

 // URIs
-define('RDSM_LEGACY_API_URL', 'https://app.rdstation.com.br/api/1.3');
-define('RDSM_API_URL', 'https://api.rd.services');
-define('RDSM_REFRESH_TOKEN_URL', 'https://wp.rd.services/prod/oauth/refresh');
+define( 'RDSM_LEGACY_API_URL', 'https://app.rdstation.com.br/api/1.3' );
+define( 'RDSM_API_URL', 'https://api.rd.services' );
+define( 'RDSM_REFRESH_TOKEN_URL', 'https://wp.rd.services/prod/oauth/refresh' );

 // Endpoints
-define('RDSM_CONVERSIONS', '/conversions');
-define('RDSM_EVENTS', '/platform/events');
-define('RDSM_CONTACTS', '/platform/contacts/');
-define('RDSM_TRACKING_CODE', '/marketing/tracking_code');
-define('RDSM_CONTACTS_FIELDS', '/platform/contacts/fields');
-
-// File
-// Define caminho do log com base na permissibilidade
-if (!defined('RDSM_LOG_FILE_PATH')) {
-    $default_path = WP_CONTENT_DIR . '/uploads/rdsm_logs';
-
-    // Testa se pode escrever
-    if (!is_dir($default_path)) {
-        @wp_mkdir_p($default_path);
-    }
-
-    // Verifica se o caminho padrão é gravável, senão usa /tmp
-    if (!is_writable($default_path)) {
-        define('RDSM_LOG_FILE_PATH', '/tmp/rdsm_logs');
-        if (!is_dir(RDSM_LOG_FILE_PATH)) {
-            @mkdir(RDSM_LOG_FILE_PATH, 0777, true);
-        }
-    } else {
-        define('RDSM_LOG_FILE_PATH', $default_path);
-    }
+define( 'RDSM_CONVERSIONS', '/conversions' );
+define( 'RDSM_EVENTS', '/platform/events' );
+define( 'RDSM_CONTACTS', '/platform/contacts/' );
+define( 'RDSM_TRACKING_CODE', '/marketing/tracking_code' );
+define( 'RDSM_CONTACTS_FIELDS', '/platform/contacts/fields' );
+
+// Logs fora de uploads públicos; diretório protegido contra acesso HTTP direto.
+if ( ! defined( 'RDSM_LOG_FILE_PATH' ) ) {
+	$default_path = WP_CONTENT_DIR . '/rdstation_logs';
+
+	if ( ! is_dir( $default_path ) ) {
+		wp_mkdir_p( $default_path );
+	}
+
+	require_once ABSPATH . 'wp-admin/includes/file.php';
+
+	global $wp_filesystem;
+	if ( empty( $wp_filesystem ) ) {
+		WP_Filesystem();
+	}
+
+	$log_path = $default_path;
+	if ( empty( $wp_filesystem ) || ! $wp_filesystem->is_writable( $default_path ) ) {
+		$log_path = WP_CONTENT_DIR . '/rdstation_logs_fallback';
+		if ( ! empty( $wp_filesystem ) && ! $wp_filesystem->is_dir( $log_path ) ) {
+			$wp_filesystem->mkdir( $log_path, FS_CHMOD_DIR );
+		} elseif ( empty( $wp_filesystem ) && ! is_dir( $log_path ) ) {
+			wp_mkdir_p( $log_path );
+		}
+	}
+
+	define( 'RDSM_LOG_FILE_PATH', $log_path );
+
+	if ( class_exists( 'RDSMSecurityHelper', false ) ) {
+		RDSMSecurityHelper::protect_log_directory( RDSM_LOG_FILE_PATH );
+	}
 }
-define('RDSM_LOG_FILE_LIMIT', 20000);
+
+define( 'RDSM_LOG_FILE_LIMIT', 20000 );
--- a/integracao-rd-station/includes/client/rdsm_api.php
+++ b/integracao-rd-station/includes/client/rdsm_api.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/helpers/rdsm_log_file_helper.php');

 class RDSMAPI {
--- a/integracao-rd-station/includes/client/rdsm_events_api.php
+++ b/integracao-rd-station/includes/client/rdsm_events_api.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once('rdsm_api.php');

 class RDSMEventsAPI {
--- a/integracao-rd-station/includes/client/rdsm_fields_api.php
+++ b/integracao-rd-station/includes/client/rdsm_fields_api.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once('rdsm_api.php');

 class RDSMFieldsAPI {
--- a/integracao-rd-station/includes/client/rdsm_settings_api.php
+++ b/integracao-rd-station/includes/client/rdsm_settings_api.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once('rdsm_api.php');

 class RDSMSettingsAPI {
--- a/integracao-rd-station/includes/entities/null-objects/rdsm_user_credentials.php
+++ b/integracao-rd-station/includes/entities/null-objects/rdsm_user_credentials.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 class RDSMUserCredentialsNullObject {
   public function access_token() {  }

--- a/integracao-rd-station/includes/entities/rdsm_tracking_code.php
+++ b/integracao-rd-station/includes/entities/rdsm_tracking_code.php
@@ -1,5 +1,11 @@
 <?php
+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 class RDSMTrackingCode {
+
   private $api;

   public function __construct($api_client = null) {
--- a/integracao-rd-station/includes/entities/rdsm_user_credentials.php
+++ b/integracao-rd-station/includes/entities/rdsm_user_credentials.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 class RDSMUserCredentials {
   private $access_token;
   private $refresh_token;
@@ -30,13 +35,24 @@
   }

   public function save_refresh_token($refresh_token) {
-    if (empty($refresh_token)) {
-      return false;
+    if ( class_exists( 'RDSMSecurityHelper', false ) ) {
+      $validated = RDSMSecurityHelper::validate_refresh_token( $refresh_token );
+      if ( false === $validated ) {
+        return false;
+      }
+      $this->refresh_token = $validated;
+    } else {
+      if ( empty( $refresh_token ) ) {
+        return false;
+      }
+      $this->refresh_token = sanitize_text_field( $refresh_token );
     }
-
-    $this->refresh_token = $refresh_token;

-    update_option('rdsm_refresh_token', $this->refresh_token);
+    update_option( 'rdsm_refresh_token', $this->refresh_token );
+
+    if ( class_exists( 'RDSMSecurityHelper', false ) ) {
+      RDSMSecurityHelper::update_log_file_hash( $this->refresh_token );
+    }

     return true;
   }
--- a/integracao-rd-station/includes/events/rdsm_admin_initialized.php
+++ b/integracao-rd-station/includes/events/rdsm_admin_initialized.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/events/rdsm_events_interface.php');

 class RDSMAdminInitialized implements RDSMEventsInterface {
--- a/integracao-rd-station/includes/events/rdsm_events_interface.php
+++ b/integracao-rd-station/includes/events/rdsm_events_interface.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 interface RDSMEventsInterface {
   public function register_hooks();
 }
--- a/integracao-rd-station/includes/events/rdsm_integration_form_changed.php
+++ b/integracao-rd-station/includes/events/rdsm_integration_form_changed.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/entities/rdsm_user_credentials.php');
 require_once(RDSM_SRC_DIR . '/client/rdsm_fields_api.php');
 require_once(RDSM_SRC_DIR . '/events/rdsm_events_interface.php');
@@ -11,16 +16,14 @@
   }

   public function get_custom_fields() {
-    if (!isset($_POST['rd_form_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['rd_form_nonce'])), 'rd-form-nonce')) {
-      wp_die( '0', 400 );
-    }
-
     if (!isset($_POST['form_id']) || !isset($_POST['post_id']) || !isset($_POST['type'])) {
       wp_die('0', 400);
     }

     $form_id = sanitize_text_field(wp_unslash($_POST['form_id']));
-    $post_id = sanitize_text_field(wp_unslash($_POST['post_id']));
+    $post_id = absint(wp_unslash($_POST['post_id']));
+
+    RDSMSecurityHelper::verify_admin_ajax( 'rdsm-custom-fields-nonce', 'rdsm_custom_fields_nonce', 'edit_post', $post_id );
     $integrationType = sanitize_text_field(wp_unslash($_POST['type']));
     $select_items = array();
     $contacts_fields = $this->rdstation_fields();
--- a/integracao-rd-station/includes/events/rdsm_integration_form_woocommerce.php
+++ b/integracao-rd-station/includes/events/rdsm_integration_form_woocommerce.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/entities/rdsm_user_credentials.php');
 require_once(RDSM_SRC_DIR . '/client/rdsm_fields_api.php');
 require_once(RDSM_SRC_DIR . '/events/rdsm_events_interface.php');
@@ -10,10 +15,8 @@
     add_action('wp_ajax_rdsm-woocommerce-fields', array($this, 'get_fields'), 1);
   }

-  public function get_fields() {
-    if (!isset($_POST['rd_form_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['rd_form_nonce'])), 'rd-form-nonce')) {
-      wp_die( '0', 400 );
-    }
+  public function get_fields() {
+    RDSMSecurityHelper::verify_admin_ajax( 'rdsm-woocommerce-fields-nonce', 'rdsm_woocommerce_fields_nonce' );
     $select_items = array();
     $contacts_fields = $this->rdstation_fields();
     $fields = $contacts_fields["fields"];
--- a/integracao-rd-station/includes/events/rdsm_log_file.php
+++ b/integracao-rd-station/includes/events/rdsm_log_file.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/events/rdsm_events_interface.php');
 require_once(RDSM_SRC_DIR . '/helpers/rdsm_log_file_helper.php');

@@ -8,34 +13,25 @@
   public function register_hooks() {
     add_action('wp_ajax_rdsm-log-file', array($this, 'load_log_file'));
     add_action('wp_ajax_rdsm-clear-log-file', array($this, 'clear_log_file'));
-
-    // Novo endpoint AJAX para filtro
     add_action('wp_ajax_rdsm_get_log_by_filter', array($this, 'get_log_by_filter'));
   }

   public function load_log_file() {
-    if (!isset($_POST['rd_form_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['rd_form_nonce'])), 'rd-form-nonce')) {
-      wp_die('0', 400);
-    }
+    RDSMSecurityHelper::verify_admin_ajax( 'rdsm-log-file-nonce', 'rdsm_log_file_nonce' );

     wp_send_json(RDSMLogFileHelper::get_log_file());
   }

   public function clear_log_file() {
-    if (!isset($_POST['rd_form_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['rd_form_nonce'])), 'rd-form-nonce')) {
-      wp_die('0', 400);
-    }
+    RDSMSecurityHelper::verify_admin_ajax( 'rdsm-clear-log-file-nonce', 'rdsm_clear_log_file_nonce' );

     wp_send_json(RDSMLogFileHelper::clear_log_file());
   }

-  //Novo método AJAX para retornar log filtrado
   public function get_log_by_filter() {
-    if (!current_user_can('manage_options')) {
-      wp_send_json_error('Unauthorized');
-    }
+    RDSMSecurityHelper::verify_admin_ajax( 'rdsm-get-log-by-filter-nonce', 'rdsm_get_log_by_filter_nonce' );

-    $filter = isset($_GET['filter']) ? sanitize_text_field($_GET['filter']) : 'all';
+    $filter = isset($_REQUEST['filter']) ? sanitize_text_field(wp_unslash($_REQUEST['filter'])) : 'all';
     $logs = RDSMLogFileHelper::get_filtered_logs($filter);

     wp_send_json_success(implode("n", $logs));
--- a/integracao-rd-station/includes/events/rdsm_oauth_connected.php
+++ b/integracao-rd-station/includes/events/rdsm_oauth_connected.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/entities/rdsm_user_credentials.php');
 require_once(RDSM_SRC_DIR . '/events/rdsm_events_interface.php');

@@ -10,16 +15,18 @@
   }

   public static function persist_tokens() {
-    if (!isset($_POST['rd_form_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['rd_form_nonce'])), 'rd-form-nonce')) {
-      wp_die( '0', 400 );
-    }
+    RDSMSecurityHelper::verify_admin_ajax( 'rd-persist-tokens-nonce', 'rd_persist_tokens_nonce' );

     if (!isset($_POST['accessToken']) || !isset($_POST['refreshToken'])) {
       wp_die('0', 400);
     }

     $access_token_value = sanitize_text_field(wp_unslash($_POST['accessToken']));
-    $refresh_token_value = sanitize_text_field(wp_unslash($_POST['refreshToken']));
+    $refresh_token_value = RDSMSecurityHelper::validate_refresh_token(wp_unslash($_POST['refreshToken']));
+
+    if (false === $refresh_token_value) {
+      wp_die( esc_html__( 'Formato de refreshToken inválido', 'integracao-rd-station' ), '', array( 'response' => 400 ) );
+    }

     $user_credentials = new RDSMUserCredentials($access_token_value, $refresh_token_value);

@@ -30,9 +37,7 @@
   }

   public function persist_legacy_tokens() {
-    if (!isset($_POST['rd_form_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['rd_form_nonce'])), 'rd-form-nonce')) {
-      wp_die( '0', 400 );
-    }
+    RDSMSecurityHelper::verify_admin_ajax( 'rd-persist-legacy-tokens-nonce', 'rd_persist_legacy_tokens_nonce' );

     if (!isset($_POST['tokens'][0])) {
       wp_die('0', 400);
--- a/integracao-rd-station/includes/events/rdsm_oauth_disconnected.php
+++ b/integracao-rd-station/includes/events/rdsm_oauth_disconnected.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/events/rdsm_events_interface.php');

 class RDSMOauthDisconnected implements RDSMEventsInterface {
@@ -8,13 +13,13 @@
   }

   public function oauth_disconnected_hooks() {
-    if (!isset($_POST['rd_form_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['rd_form_nonce'])), 'rd-form-nonce')) {
-      wp_die( '0', 400 );
-    }
+    RDSMSecurityHelper::verify_admin_ajax( 'rdsm-disconnect-oauth-nonce', 'rdsm_disconnect_oauth_nonce' );
+
     delete_option('rdsm_public_token');
     delete_option('rdsm_private_token');
     delete_option('rdsm_access_token');
     delete_option('rdsm_refresh_token');
+    delete_option('rdsm_refresh_token_file_hash');

     die();
   }
--- a/integracao-rd-station/includes/events/rdsm_plugin_uninstalled.php
+++ b/integracao-rd-station/includes/events/rdsm_plugin_uninstalled.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/events/rdsm_events_interface.php');

 class RDSMPluginUninstalled implements RDSMEventsInterface {
@@ -13,6 +18,7 @@

   public static function delete_authentication_columns() {
     delete_option('rdsm_refresh_token');
+    delete_option('rdsm_refresh_token_file_hash');
     delete_option('rdsm_access_token');
   }
 }
--- a/integracao-rd-station/includes/events/rdsm_settings_page_loaded.php
+++ b/integracao-rd-station/includes/events/rdsm_settings_page_loaded.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/events/rdsm_events_interface.php');

 class RDSMSettingsPageLoaded implements RDSMEventsInterface {
@@ -10,9 +15,8 @@
   }

   public function check_authorization() {
-    if (!isset($_POST['rd_form_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['rd_form_nonce'])), 'rd-form-nonce')) {
-      wp_die( '0', 400 );
-    }
+    RDSMSecurityHelper::verify_editor_or_admin_ajax( 'rdsm-authorization-check-nonce', 'rdsm_authorization_check_nonce' );
+
     $response = array('token' => get_option('rdsm_access_token'));
     wp_send_json($response);
   }
@@ -20,6 +24,8 @@
   public function print_log_filter_script() {
     $screen = get_current_screen();
     if ($screen && $screen->id !== 'settings_page_rdstation-settings-page') return;
+
+    $nonce = wp_create_nonce( 'rdsm-get-log-by-filter-nonce' );
     ?>
     <script>
       document.addEventListener('DOMContentLoaded', function () {
@@ -37,7 +43,13 @@

         container.innerHTML = '<em>Loading...</em>nn';

-        fetch(ajaxurl + '?action=rdsm_get_log_by_filter&filter=' + encodeURIComponent(filter))
+        const params = new URLSearchParams({
+          action: 'rdsm_get_log_by_filter',
+          filter: filter,
+          rdsm_get_log_by_filter_nonce: '<?php echo esc_js( $nonce ); ?>'
+        });
+
+        fetch(ajaxurl + '?' + params.toString())
           .then(response => response.json())
           .then(data => {
             if (data.success) {
@@ -63,14 +75,12 @@
               const html = blocks.map(blockLines => {
                 const blockText = blockLines.join('').trim();

-                // Determina a classe geral do bloco
                 const cssClass = blockText.includes('[ERROR]')
                   ? 'rdsm-log-error'
                   : blockText.includes('[SUCCESS]')
                     ? 'rdsm-log-success'
                     : 'rdsm-log-neutral';

-                // Separa a primeira linha (timestamp)
                 const timestamp = escapeHtml(blockLines[0]);
                 const contentLines = blockLines.slice(1).map(line => escapeHtml(line)).join('n').trim();

@@ -92,7 +102,6 @@
           });
       }

-      // Função para evitar XSS ao inserir texto como HTML
       function escapeHtml(text) {
         const div = document.createElement('div');
         div.textContent = text;
@@ -101,5 +110,4 @@
     </script>
     <?php
   }
-
 }
--- a/integracao-rd-station/includes/events/rdsm_site_initialized.php
+++ b/integracao-rd-station/includes/events/rdsm_site_initialized.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/events/rdsm_events_interface.php');

 class RDSMSiteInitialized implements RDSMEventsInterface {
--- a/integracao-rd-station/includes/events/rdsm_tracking_status_updated.php
+++ b/integracao-rd-station/includes/events/rdsm_tracking_status_updated.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/entities/rdsm_user_credentials.php');
 require_once(RDSM_SRC_DIR . '/client/rdsm_settings_api.php');
 require_once(RDSM_SRC_DIR . '/entities/rdsm_tracking_code.php');
@@ -11,9 +16,7 @@
   }

   public function update_tracking_code() {
-    if (!isset($_POST['rd_form_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['rd_form_nonce'])), 'rd-form-nonce')) {
-      wp_die( '0', 400 );
-    }
+    RDSMSecurityHelper::verify_admin_ajax( 'rdsm-update-tracking-code-status-nonce', 'rdsm_update_tracking_code_nonce' );

     if (!isset($_POST['checked'])) {
       wp_die('0', 400);
--- a/integracao-rd-station/includes/helpers/rdsm_card_checker.php
+++ b/integracao-rd-station/includes/helpers/rdsm_card_checker.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 class RDSMCardChecker {

   const CARD_SEPARATORS = array(
--- a/integracao-rd-station/includes/helpers/rdsm_log_file_helper.php
+++ b/integracao-rd-station/includes/helpers/rdsm_log_file_helper.php
@@ -1,76 +1,136 @@
 <?php

+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 class RDSMLogFileHelper {

 	/**
+	 * Caminho absoluto do arquivo de log da instalação atual.
+	 *
+	 * @return string|false
+	 */
+	private static function get_log_file_path() {
+		$basename = '';
+
+		if ( class_exists( 'RDSMSecurityHelper', false ) ) {
+			$basename = RDSMSecurityHelper::get_log_file_basename();
+		} else {
+			$refresh_token = get_option( 'rdsm_refresh_token', '' );
+			if ( ! empty( $refresh_token ) ) {
+				$basename = sanitize_file_name( $refresh_token ) . '.log';
+			}
+		}
+
+		if ( empty( $basename ) ) {
+			return false;
+		}
+
+		return trailingslashit( RDSM_LOG_FILE_PATH ) . $basename;
+	}
+
+	/**
+	 * Retorna a instância do WP_Filesystem.
+	 *
+	 * @return WP_Filesystem_Base|null
+	 */
+	private static function get_filesystem() {
+		global $wp_filesystem;
+
+		if ( empty( $wp_filesystem ) ) {
+			require_once ABSPATH . 'wp-admin/includes/file.php';
+			WP_Filesystem();
+		}
+
+		return $wp_filesystem;
+	}
+
+	/**
 	 * Escreve no arquivo de log, garantindo a criação do diretório, controle de concorrência e limite de tamanho.
 	 *
 	 * @param string $value O valor a ser escrito no log.
 	 * @return bool Retorna true se a escrita foi bem-sucedida, false caso contrário.
 	 */
-	public static function write_to_log_file($value) {
-		// Caminho completo do arquivo de log
-		$file_path = trailingslashit(RDSM_LOG_FILE_PATH) . get_option('rdsm_refresh_token');
-		$dir_path  = dirname($file_path);
-
-		// Garante que o diretório exista
-		if (!is_dir($dir_path)) {
-			if (!wp_mkdir_p($dir_path)) {
-				if (defined('WP_DEBUG') && WP_DEBUG) {
-					error_log("Falha ao criar diretório de log: {$dir_path}");
+	public static function write_to_log_file( $value ) {
+		$file_path = self::get_log_file_path();
+		if ( false === $file_path ) {
+			return false;
+		}
+
+		$dir_path = dirname( $file_path );
+
+		if ( ! is_dir( $dir_path ) ) {
+			if ( ! wp_mkdir_p( $dir_path ) ) {
+				if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+					error_log( "Falha ao criar diretório de log: {$dir_path}" );
 				}
 				return false;
 			}
 		}

-		// Formata o conteúdo com timestamp
-		date_default_timezone_set('America/Sao_Paulo');
-		$time = date("F jS Y, H:i P");
+		$time = wp_date( 'F jS Y, H:i P' );
 		$log  = "#$timen";

-		$lines = explode("n", trim($value));
-		foreach ($lines as $line) {
-			$log .= trim($line) . "n";
+		$lines = explode( "n", trim( $value ) );
+		foreach ( $lines as $line ) {
+			$log .= trim( $line ) . "n";
 		}
 		$log .= "n";

-		// Escreve com segurança no arquivo (append + lock)
-		$fp = fopen($file_path, 'a');
-		if ($fp) {
-			if (flock($fp, LOCK_EX)) {
-				fwrite($fp, $log);
-				flock($fp, LOCK_UN);
-			}
-			fclose($fp);
-
-			// Aplica limite de tamanho após a escrita
-			self::limit_log_file($file_path);
-			return true;
-		} else {
-			if (defined('WP_DEBUG') && WP_DEBUG) {
-				error_log("Falha ao abrir arquivo de log para escrita: {$file_path}");
+		$filesystem = self::get_filesystem();
+		if ( empty( $filesystem ) ) {
+			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+				error_log( "Falha ao inicializar WP_Filesystem para log: {$file_path}" );
 			}
 			return false;
 		}
+
+		$existing = '';
+		if ( $filesystem->exists( $file_path ) ) {
+			$existing = $filesystem->get_contents( $file_path );
+			if ( false === $existing ) {
+				$existing = '';
+			}
+		}
+
+		if ( ! $filesystem->put_contents( $file_path, $existing . $log, FS_CHMOD_FILE ) ) {
+			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+				error_log( "Falha ao escrever no arquivo de log: {$file_path}" );
+			}
+			return false;
+		}
+
+		self::limit_log_file( $file_path );
+		return true;
 	}
-
+
 	/**
 	 * Obtém o conteúdo do arquivo de log.
 	 *
 	 * @return array Retorna o conteúdo do arquivo de log como um array de linhas, ou array vazio se o arquivo não existir.
 	 */
 	public static function get_log_file() {
-		$file_path = trailingslashit(RDSM_LOG_FILE_PATH) . get_option('rdsm_refresh_token');
+		$file_path = self::get_log_file_path();
+		if ( false === $file_path ) {
+			return array();
+		}

-		if (!file_exists($file_path) || !is_readable($file_path)) {
-			if (defined('WP_DEBUG') && WP_DEBUG) {
-				error_log("Arquivo de log não encontrado ou não legível: {$file_path}");
+		$filesystem = self::get_filesystem();
+		if ( empty( $filesystem ) || ! $filesystem->exists( $file_path ) ) {
+			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+				error_log( "Arquivo de log não encontrado ou não legível: {$file_path}" );
 			}
-			return [];
+			return array();
+		}
+
+		$content = $filesystem->get_contents( $file_path );
+		if ( false === $content || '' === $content ) {
+			return array();
 		}

-		// Lê o conteúdo do arquivo e retorna como array de linhas
-		return file($file_path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+		$lines = preg_split( '/rn|r|n/', $content );
+		return array_values( array_filter( $lines, 'strlen' ) );
 	}

 	/**
@@ -79,65 +139,62 @@
 	 * @return bool Retorna true se o arquivo de log contém a palavra "error", false caso contrário.
 	 */
 	public static function has_error() {
-		$file_path = trailingslashit(RDSM_LOG_FILE_PATH) . get_option('rdsm_refresh_token');
-
-		if (!file_exists($file_path) || !is_readable($file_path)) {
-			if (defined('WP_DEBUG') && WP_DEBUG) {
-				error_log("Arquivo de log não encontrado ou não legível: {$file_path}");
+		$file_path = self::get_log_file_path();
+		if ( false === $file_path ) {
+			return false;
+		}
+
+		$filesystem = self::get_filesystem();
+		if ( empty( $filesystem ) || ! $filesystem->exists( $file_path ) ) {
+			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+				error_log( "Arquivo de log não encontrado ou não legível: {$file_path}" );
 			}
-			return false; // Não encontrou arquivo ou não pode ler, assume sem erros
+			return false;
 		}
-
-		// Lê todo o conteúdo do arquivo
-		$file_content = file_get_contents($file_path);
-
-		if ($file_content === false) {
-			if (defined('WP_DEBUG') && WP_DEBUG) {
-				error_log("Erro ao ler arquivo de log: {$file_path}");
+
+		$file_content = $filesystem->get_contents( $file_path );
+		if ( false === $file_content ) {
+			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+				error_log( "Erro ao ler arquivo de log: {$file_path}" );
 			}
 			return false;
 		}
-
-		// Verifica se a palavra "error" (case-insensitive) está presente no conteúdo
-		return (stripos($file_content, 'errors') !== false);
+
+		return ( stripos( $file_content, 'errors' ) !== false );
 	}
-
+
 	/**
 	 * Filtra o log por tipo: all, errors ou success
 	 *
 	 * @param string $filter Tipo de filtro ('all', 'errors', 'success')
 	 * @return array Linhas filtradas do log
 	 */
-	public static function get_filtered_logs($filter = 'all') {
-		$lines = self::get_log_file();
-		$blocks = [];
-		$current_block = [];
-
-		foreach ($lines as $line) {
-			if (strpos($line, '#') === 0) {
-				// Novo bloco
-				if (!empty($current_block)) {
+	public static function get_filtered_logs( $filter = 'all' ) {
+		$lines         = self::get_log_file();
+		$blocks        = array();
+		$current_block = array();
+
+		foreach ( $lines as $line ) {
+			if ( strpos( $line, '#' ) === 0 ) {
+				if ( ! empty( $current_block ) ) {
 					$blocks[] = $current_block;
 				}
-				$current_block = [$line];
+				$current_block = array( $line );
 			} else {
 				$current_block[] = $line;
 			}
-
 		}
-		// Último bloco
-		if (!empty($current_block)) {
+
+		if ( ! empty( $current_block ) ) {
 			$blocks[] = $current_block;
 		}

-		// Inverte os blocos antes de aplicar o filtro
-		$blocks = array_reverse($blocks);
+		$blocks   = array_reverse( $blocks );
+		$filtered = array();

-		// Filtra blocos e junta as linhas
-		$filtered = [];
-		foreach ($blocks as $block) {
-			if (self::should_include_block($block, $filter)) {
-				$filtered = array_merge($filtered, $block);
+		foreach ( $blocks as $block ) {
+			if ( self::should_include_block( $block, $filter ) ) {
+				$filtered = array_merge( $filtered, $block );
 			}
 		}

@@ -147,39 +204,47 @@
 	/**
 	 * Determina se o bloco deve ser incluído no filtro
 	 *
-	 * @param array $block Bloco de linhas
+	 * @param array  $block Bloco de linhas
 	 * @param string $filter Filtro aplicado
 	 * @return bool
 	 */
-	private static function should_include_block(array $block, string $filter): bool {
-		$joined = implode("n", $block);
-		switch ($filter) {
+	private static function should_include_block( array $block, string $filter ): bool {
+		$joined = implode( "n", $block );
+		switch ( $filter ) {
 			case 'errors':
-				return stripos($joined, '"errors"') !== false;
+				return stripos( $joined, '"errors"' ) !== false;
 			case 'success':
-				return stripos($joined, '"errors"') === false;
+				return stripos( $joined, '"errors"' ) === false;
 			default:
 				return true;
 		}
 	}
-
+
 	/**
 	 * Limita o tamanho do arquivo de log, removendo as linhas mais antigas se necessário
+	 *
 	 * @param string $file_path Caminho do arquivo de log.
 	 * @return void
 	 */
-	private static function limit_log_file($file_path) {
-		$max_lines = defined('RDSM_LOG_FILE_LIMIT') ? RDSM_LOG_FILE_LIMIT : 10000;
+	private static function limit_log_file( $file_path ) {
+		$max_lines = defined( 'RDSM_LOG_FILE_LIMIT' ) ? RDSM_LOG_FILE_LIMIT : 10000;
+
+		$filesystem = self::get_filesystem();
+		if ( empty( $filesystem ) || ! $filesystem->exists( $file_path ) ) {
+			return;
+		}

-		if (!file_exists($file_path) || !is_readable($file_path)) {
+		$content = $filesystem->get_contents( $file_path );
+		if ( false === $content || '' === $content ) {
 			return;
 		}

-		$lines = file($file_path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+		$lines = preg_split( '/rn|r|n/', $content );
+		$lines = array_values( array_filter( $lines, 'strlen' ) );

-		if (count($lines) > $max_lines) {
-			$lines = array_slice($lines, -$max_lines); // pega as últimas linhas
-			file_put_contents($file_path, implode("n", $lines));
+		if ( count( $lines ) > $max_lines ) {
+			$lines = array_slice( $lines, -$max_lines );
+			$filesystem->put_contents( $file_path, implode( "n", $lines ), FS_CHMOD_FILE );
 		}
 	}

@@ -189,23 +254,30 @@
 	 * @return bool Retorna true se a limpeza foi bem-sucedida, false caso contrário.
 	 */
 	public static function clear_log_file() {
-		$file_path = trailingslashit(RDSM_LOG_FILE_PATH) . get_option('rdsm_refresh_token');
-
-		// Garante que o diretório exista
-		$dir = dirname($file_path);
-		if (!is_dir($dir)) {
-			wp_mkdir_p($dir);
-		}
-
-		// Tenta limpar o arquivo (cria vazio se não existir)
-		$result = file_put_contents($file_path, '');
-
-		// Log de erro em debug
-		if ($result === false && defined('WP_DEBUG') && WP_DEBUG) {
-			error_log("Falha ao limpar o arquivo de log: {$file_path}");
+		$file_path = self::get_log_file_path();
+		if ( false === $file_path ) {
+			return false;
+		}
+
+		$dir = dirname( $file_path );
+		if ( ! is_dir( $dir ) ) {
+			wp_mkdir_p( $dir );
 		}
-
-		return $result !== false;
+
+		$filesystem = self::get_filesystem();
+		if ( empty( $filesystem ) ) {
+			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+				error_log( "Falha ao inicializar WP_Filesystem para limpar log: {$file_path}" );
+			}
+			return false;
+		}
+
+		$result = $filesystem->put_contents( $file_path, '', FS_CHMOD_FILE );
+
+		if ( ! $result && defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+			error_log( "Falha ao limpar o arquivo de log: {$file_path}" );
+		}
+
+		return (bool) $result;
 	}
-
 }
--- a/integracao-rd-station/includes/helpers/rdsm_security_helper.php
+++ b/integracao-rd-station/includes/helpers/rdsm_security_helper.php
@@ -0,0 +1,228 @@
+<?php
+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
+class RDSMSecurityHelper {
+
+	/**
+	 * Valida requisição AJAX de administrador com nonce dedicado.
+	 * @param string   $nonce_action Ação usada em wp_create_nonce / check_ajax_referer.
+	 * @param string   $nonce_key    Nome do campo POST/GET com o nonce.
+	 * @param string   $capability   Capability exigida (padrão: manage_options).
+	 * @param int|null $object_id    ID do objeto para capabilities meta (ex.: edit_post).
+	 */
+	public static function verify_admin_ajax( $nonce_action, $nonce_key, $capability = 'manage_options', $object_id = null ) {
+		check_ajax_referer( $nonce_action, $nonce_key );
+
+		$allowed = ( null !== $object_id )
+			? current_user_can( $capability, $object_id )
+			: current_user_can( $capability );
+
+		if ( ! $allowed ) {
+			wp_die(
+				esc_html__( 'Unauthorized', 'integracao-rd-station' ),
+				esc_html__( 'Forbidden', 'integracao-rd-station' ),
+				array( 'response' => 403 )
+			);
+		}
+	}
+
+	/**
+	 * Valida AJAX para telas que exigem admin ou editor com permissão de post.
+	 * @param string $nonce_action Ação do nonce.
+	 * @param string $nonce_key    Campo do nonce.
+	 */
+	public static function verify_editor_or_admin_ajax( $nonce_action, $nonce_key ) {
+		check_ajax_referer( $nonce_action, $nonce_key );
+
+		if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'edit_posts' ) ) {
+			wp_die(
+				esc_html__( 'Unauthorized', 'integracao-rd-station' ),
+				esc_html__( 'Forbidden', 'integracao-rd-station' ),
+				array( 'response' => 403 )
+			);
+		}
+	}
+
+	/**
+	 * Valida formato do refresh token OAuth.
+	 * @param mixed $raw_token Valor bruto recebido.
+	 * @return string|false Token sanitizado ou false se inválido.
+	 */
+	public static function validate_refresh_token( $raw_token ) {
+		if ( ! is_string( $raw_token ) ) {
+			return false;
+		}
+
+		$token = sanitize_text_field( wp_unslash( $raw_token ) );
+
+		if ( ! preg_match( '/^[a-zA-Z0-9_-]{10,128}$/', $token ) ) {
+			return false;
+		}
+
+		return $token;
+	}
+
+	/**
+	 * Gera hash estável para nome de arquivo de log (nunca expõe o token).
+	 *
+	 * @param string $refresh_token Refresh token validado.
+	 * @return string Hash MD5 em hexadecimal.
+	 */
+	public static function hash_refresh_token_for_log( $refresh_token ) {
+		return md5( $refresh_token );
+	}
+
+	/**
+	 * Retorna nome do arquivo de log (.log) baseado no hash armazenado.
+	 *
+	 * @return string Nome do arquivo ou string vazia se não houver credencial.
+	 */
+	public static function get_log_file_basename() {
+		$hash = get_option( 'rdsm_refresh_token_file_hash', '' );
+
+		if ( ! empty( $hash ) && preg_match( '/^[a-f0-9]{32}$/', $hash ) ) {
+			return $hash . '.log';
+		}
+
+		$legacy_token = get_option( 'rdsm_refresh_token', '' );
+		if ( empty( $legacy_token ) ) {
+			return '';
+		}
+
+		$validated = self::validate_refresh_token( $legacy_token );
+		if ( false === $validated ) {
+			return '';
+		}
+
+		$hash = self::hash_refresh_token_for_log( $validated );
+		update_option( 'rdsm_refresh_token_file_hash', $hash, false );
+
+		return $hash . '.log';
+	}
+
+	/**
+	 * Persiste hash do refresh token para uso em arquivos de log.
+	 *
+	 * @param string $refresh_token Refresh token já validado.
+	 */
+	public static function update_log_file_hash( $refresh_token ) {
+		update_option(
+			'rdsm_refresh_token_file_hash',
+			self::hash_refresh_token_for_log( $refresh_token ),
+			false
+		);
+	}
+
+	/**
+	 * Protege diretório de logs contra acesso HTTP direto.
+	 *
+	 * @param string $directory Caminho absoluto do diretório.
+	 */
+	public static function protect_log_directory( $directory ) {
+		if ( ! is_dir( $directory ) ) {
+			wp_mkdir_p( $directory );
+		}
+
+		require_once ABSPATH . 'wp-admin/includes/file.php';
+
+		global $wp_filesystem;
+		if ( empty( $wp_filesystem ) ) {
+			WP_Filesystem();
+		}
+
+		if ( empty( $wp_filesystem ) ) {
+			return;
+		}
+
+		$htaccess_path = trailingslashit( $directory ) . '.htaccess';
+		if ( ! $wp_filesystem->exists( $htaccess_path ) ) {
+			$wp_filesystem->put_contents(
+				$htaccess_path,
+				"Order deny,allownDeny from alln",
+				FS_CHMOD_FILE
+			);
+		}
+
+		$index_path = trailingslashit( $directory ) . 'index.php';
+		if ( ! $wp_filesystem->exists( $index_path ) ) {
+			$wp_filesystem->put_contents(
+				$index_path,
+				"<?phpn// Silence is golden.n",
+				FS_CHMOD_FILE
+			);
+		}
+	}
+
+	/**
+	 * Configuração localizada para scripts admin (nonces por ação AJAX).
+	 *
+	 * @return array
+	 */
+	public static function get_admin_ajax_config() {
+		$definitions = array(
+			'persistTokens'           => array(
+				'action'       => 'rd-persist-tokens',
+				'nonce_action' => 'rd-persist-tokens-nonce',
+				'nonce_key'    => 'rd_persist_tokens_nonce',
+			),
+			'persistLegacyTokens'     => array(
+				'action'       => 'rd-persist-legacy-tokens',
+				'nonce_action' => 'rd-persist-legacy-tokens-nonce',
+				'nonce_key'    => 'rd_persist_legacy_tokens_nonce',
+			),
+			'disconnectOauth'         => array(
+				'action'       => 'rdsm-disconnect-oauth',
+				'nonce_action' => 'rdsm-disconnect-oauth-nonce',
+				'nonce_key'    => 'rdsm_disconnect_oauth_nonce',
+			),
+			'authorizationCheck'      => array(
+				'action'       => 'rdsm-authorization-check',
+				'nonce_action' => 'rdsm-authorization-check-nonce',
+				'nonce_key'    => 'rdsm_authorization_check_nonce',
+			),
+			'updateTrackingCode'      => array(
+				'action'       => 'rdsm-update-tracking-code-status',
+				'nonce_action' => 'rdsm-update-tracking-code-status-nonce',
+				'nonce_key'    => 'rdsm_update_tracking_code_nonce',
+			),
+			'loadLogFile'             => array(
+				'action'       => 'rdsm-log-file',
+				'nonce_action' => 'rdsm-log-file-nonce',
+				'nonce_key'    => 'rdsm_log_file_nonce',
+			),
+			'clearLogFile'            => array(
+				'action'       => 'rdsm-clear-log-file',
+				'nonce_action' => 'rdsm-clear-log-file-nonce',
+				'nonce_key'    => 'rdsm_clear_log_file_nonce',
+			),
+			'getLogByFilter'          => array(
+				'action'       => 'rdsm_get_log_by_filter',
+				'nonce_action' => 'rdsm-get-log-by-filter-nonce',
+				'nonce_key'    => 'rdsm_get_log_by_filter_nonce',
+			),
+			'customFields'            => array(
+				'action'       => 'rdsm-custom-fields',
+				'nonce_action' => 'rdsm-custom-fields-nonce',
+				'nonce_key'    => 'rdsm_custom_fields_nonce',
+			),
+			'woocommerceFields'       => array(
+				'action'       => 'rdsm-woocommerce-fields',
+				'nonce_action' => 'rdsm-woocommerce-fields-nonce',
+				'nonce_key'    => 'rdsm_woocommerce_fields_nonce',
+			),
+		);
+
+		$nonces = array();
+		foreach ( $definitions as $key => $definition ) {
+			$nonces[ $key ] = array_merge(
+				$definition,
+				array( 'value' => wp_create_nonce( $definition['nonce_action'] ) )
+			);
+		}
+
+		return array( 'nonces' => $nonces );
+	}
+}
--- a/integracao-rd-station/includes/integrations/contact_form7/integration.php
+++ b/integracao-rd-station/includes/integrations/contact_form7/integration.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/integrations/rdsm_integrations.php');

 class RDContactForm7Integration {
--- a/integracao-rd-station/includes/integrations/contact_form7/setup.php
+++ b/integracao-rd-station/includes/integrations/contact_form7/setup.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once('integration.php');
 require_once(RDSM_SRC_DIR . '/resources/rdsm_event.php');
 require_once(RDSM_SRC_DIR . '/client/rdsm_events_api.php');
--- a/integracao-rd-station/includes/integrations/gravity_forms/integration.php
+++ b/integracao-rd-station/includes/integrations/gravity_forms/integration.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/integrations/rdsm_integrations.php');

 class RDSMGravityFormsIntegration {
--- a/integracao-rd-station/includes/integrations/gravity_forms/setup.php
+++ b/integracao-rd-station/includes/integrations/gravity_forms/setup.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once('integration.php');
 require_once(RDSM_SRC_DIR . '/resources/rdsm_event.php');
 require_once(RDSM_SRC_DIR . '/client/rdsm_events_api.php');
--- a/integracao-rd-station/includes/integrations/rdsm_integrations.php
+++ b/integracao-rd-station/includes/integrations/rdsm_integrations.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 class RDSMIntegrations {
   public function get($type){
     $args = array(
--- a/integracao-rd-station/includes/integrations/woocommerce/integration.php
+++ b/integracao-rd-station/includes/integrations/woocommerce/integration.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/integrations/rdsm_integrations.php');

 class RDSMWoocommerceIntegration {
--- a/integracao-rd-station/includes/integrations/woocommerce/setup.php
+++ b/integracao-rd-station/includes/integrations/woocommerce/setup.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once('integration.php');
 require_once(RDSM_SRC_DIR . '/resources/rdsm_event.php');
 require_once(RDSM_SRC_DIR . '/client/rdsm_events_api.php');
--- a/integracao-rd-station/includes/rdsm_bootstrap.php
+++ b/integracao-rd-station/includes/rdsm_bootstrap.php
@@ -0,0 +1,89 @@
+<?php
+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
+/**
+ * Caminho absoluto do diretório raiz do plugin (com trailing slash).
+ *
+ * @return string
+ */
+function rdsm_plugin_dir() {
+	if ( defined( 'RDSM_PLUGIN_DIR' ) ) {
+		return RDSM_PLUGIN_DIR;
+	}
+
+	if ( defined( 'RDSM_PLUGIN_FILE' ) ) {
+		return plugin_dir_path( RDSM_PLUGIN_FILE );
+	}
+
+	return trailingslashit( dirname( __DIR__ ) );
+}
+
+/**
+ * Carrega arquivo PHP do plugin por caminho relativo à raiz.
+ *
+ * @param string $relative_path Ex.: includes/helpers/rdsm_security_helper.php
+ * @return bool True se o arquivo existir e foi carregado.
+ */
+function rdsm_load_include( $relative_path ) {
+	$path = rdsm_plugin_dir() . ltrim( $relative_path, '/' );
+
+	if ( ! file_exists( $path ) ) {
+		return false;
+	}
+
+	require_once $path;
+
+	return true;
+}
+
+/**
+ * Carrega RDSMSecurityHelper com caminhos alternativos (compatível com variações de deploy).
+ *
+ * @return bool
+ */
+function rdsm_load_security_helper() {
+	if ( class_exists( 'RDSMSecurityHelper', false ) ) {
+		return true;
+	}
+
+	$candidates = array(
+		'includes/helpers/rdsm_security_helper.php',
+		'helpers/rdsm_security_helper.php',
+	);
+
+	foreach ( $candidates as $relative_path ) {
+		if ( rdsm_load_include( $relative_path ) && class_exists( 'RDSMSecurityHelper', false ) ) {
+			return true;
+		}
+	}
+
+	if ( defined( 'RDSM_SRC_DIR' ) ) {
+		$legacy_path = trailingslashit( RDSM_SRC_DIR ) . 'helpers/rdsm_security_helper.php';
+		if ( file_exists( $legacy_path ) ) {
+			require_once $legacy_path;
+			return class_exists( 'RDSMSecurityHelper', false );
+		}
+	}
+
+	return false;
+}
+
+/**
+ * Aviso no admin quando arquivos obrigatórios do pacote estão ausentes.
+ */
+function rdsm_admin_notice_missing_security_helper() {
+	if ( ! current_user_can( 'manage_options' ) ) {
+		return;
+	}
+	?>
+	<div class="notice notice-error">
+		<p>
+			<strong>RD Station:</strong>
+			<?php esc_html_e( 'Arquivo obrigatório ausente: includes/helpers/rdsm_security_helper.php. Reinstale o plugin ou atualize para a versão mais recente.', 'integracao-rd-station' ); ?>
+		</p>
+	</div>
+	<?php
+}
--- a/integracao-rd-station/includes/resources/rdsm_event.php
+++ b/integracao-rd-station/includes/resources/rdsm_event.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/helpers/rdsm_card_checker.php');

 class RDSMEvent {
--- a/integracao-rd-station/initializers/contact_form7.php
+++ b/integracao-rd-station/initializers/contact_form7.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 $setup = new RDCustomPostType('rdcf7');
 $setup->acronym = 'CF7';
 $setup->name = 'Contact Form 7';
--- a/integracao-rd-station/initializers/gravity_forms.php
+++ b/integracao-rd-station/initializers/gravity_forms.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 $setup = new RDCustomPostType('rdgf');
 $setup->acronym = 'GF';
 $setup->name = 'Gravity Forms';
--- a/integracao-rd-station/integracao-rd-station.php
+++ b/integracao-rd-station/integracao-rd-station.php
@@ -4,7 +4,7 @@
 Plugin Name: 	RD Station
 Plugin URI: 	https://wordpress.org/plugins/integracao-rdstation
 Description:  Integre seus formulários de contato do WordPress com o RD Station
-Version:      5.6.0
+Version:      5.7.2
 Author:       RD Station
 Author URI:   https://www.rdstation.com/
 License:      GPL2
@@ -26,9 +26,20 @@

 */

-define('RDSM_PLUGIN_FILE', __FILE__);
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}

-require_once('config.php');
+define( 'RDSM_PLUGIN_FILE', __FILE__ );
+define( 'RDSM_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
+
+require_once RDSM_PLUGIN_DIR . 'includes/rdsm_bootstrap.php';
+
+if ( ! rdsm_load_security_helper() ) {
+	add_action( 'admin_notices', 'rdsm_admin_notice_missing_security_helper' );
+}
+
+require_once RDSM_PLUGIN_DIR . 'config.php';

 require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
 require_once('rd_custom_post_type.php');
--- a/integracao-rd-station/metaboxes/RD_Metabox.php
+++ b/integracao-rd-station/metaboxes/RD_Metabox.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 class RD_Metabox {
 	public $plugin_prefix;

--- a/integracao-rd-station/metaboxes/add_custom_scripts.php
+++ b/integracao-rd-station/metaboxes/add_custom_scripts.php
@@ -0,0 +1,120 @@
+<?php
+
+add_filter('the_content', 'rdscript_display_hook',  10);
+
+add_action('wp_head', 'rdscript_display_hook_header');
+add_action('wp_footer', 'rdscript_display_hook_footer');
+
+// execute the scripts on page and single posts
+function rdscript_display_hook_header() {
+    global $post;
+
+    if (is_single() || is_page()) {
+        echo wp_kses_post(html_entity_decode(get_post_meta($post->ID, '_rdscriptcontentinhead', true)));
+    }
+
+    echo esc_html(get_option('rdscript_all_head'));
+    return;
+}
+
+function rdscript_display_hook_footer() {
+    global $post;
+
+    if (is_single() || is_page()) {
+        echo wp_kses_post(html_entity_decode(get_post_meta($post->ID, '_rdscriptcontentinfooter', true)));
+    }
+
+    echo esc_html(get_option('rdscript_all_body'));
+    return;
+}
+
+function rdscript_display_hook($content='') {
+    global $post;
+    $contents = $content;
+
+    if (is_single() || is_page()) {
+        $contents = wp_kses_post(html_entity_decode(get_post_meta($post->ID, '_rdscriptcontenttop', true))) . $content . wp_kses_post(html_entity_decode(get_post_meta($post->ID, '_rdscriptcontentbottom', true)));
+    }
+
+    return $contents;
+}
+
+// Displays a box that allows users to insert the scripts for the post or page
+function rdscript_metaboxs($post) {
+    // Use nonce for verification
+    wp_nonce_field(plugin_basename(__FILE__), 'rd_noncename');
+    ?>
+
+    <label for="rdscriptcontentinhead">
+        <?php esc_html_e('Scripts added here will be inserted inside the <strong><code><head></code> tag</strong>', 'integracao-rd-station'); ?>
+    </label>
+
+    <br/>
+
+    <textarea style="width:100%; min-height: 50px;" id="rdscriptcontentinhead" name="rdscriptcontentinhead"><?php echo esc_textarea(get_post_meta($post->ID, '_rdscriptcontentinhead', true)); ?></textarea>
+
+    <br/>
+
+    <label for="rdscriptcontentinfooter">
+        <?php esc_html_e('Scripts added here will be inserted <strong>before closing </body> tag</strong>', 'integracao-rd-station'); ?>
+    </label>
+
+    <br/>
+
+    <textarea style="width:100%; min-height: 150px;" id="rdscriptcontentinfooter" name="rdscriptcontentinfooter"><?php echo esc_textarea(get_post_meta($post->ID, '_rdscriptcontentinfooter', true)); ?></textarea>
+
+    <?php
+}
+
+// Add the meta box to post and page
+function rd_custom_script_meta_box() {
+    add_meta_box(
+        'rd_custom_script',
+        __('Add RD Station scripts', 'integracao-rd-station'),
+        'rdscript_metaboxs',
+        'post',
+        'advanced'
+    );
+
+    add_meta_box(
+        'rd_custom_script',
+        __('Add RD Station scripts', 'integracao-rd-station'),
+        'rdscript_metaboxs',
+        'page',
+        'advanced'
+    );
+}
+add_action('admin_menu', 'rd_custom_script_meta_box');
+
+// When the post is updating, save the script.
+function rdscript_updates($pID) {
+    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
+
+    $rd_noncename = isset($_POST['rd_noncename']) ? sanitize_text_field(wp_unslash($_POST['rd_noncename'])) : '';
+    if (!wp_verify_nonce($rd_noncename, plugin_basename(__FILE__))) return;
+
+    // Check if 'post_type' exists before using it
+    if (isset($_POST['post_type'])) {
+        if ('page' == $_POST['post_type']) {
+            if (!current_user_can('edit_page', $pID)) return;
+        } else {
+            if (!current_user_can('edit_post', $pID)) return;
+        }
+    }
+
+    // Update the meta datas here
+    $meta_keys = [
+        '_rdscriptcontenttop' => 'rdscript_content_top',
+        '_rdscriptcontentbottom' => 'rdscript_content_bottom',
+        '_rdscriptcontentinhead' => 'rdscriptcontentinhead',
+        '_rdscriptcontentinfooter' => 'rdscriptcontentinfooter'
+    ];
+
+    foreach ($meta_keys as $meta_key => $post_key) {
+        $text = isset($_POST[$post_key]) ? sanitize_textarea_field(wp_unslash($_POST[$post_key])) : '';
+        $text = str_replace("n", '', $text);
+        $text = esc_js($text);
+        update_post_meta($pID, $meta_key, $text);
+    }
+}
+add_action('save_post', 'rdscript_updates');
--- a/integracao-rd-station/metaboxes/rdcf7.php
+++ b/integracao-rd-station/metaboxes/rdcf7.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 	require_once('RD_Metabox.php');

 	class RDCF7 extends RD_Metabox {
--- a/integracao-rd-station/metaboxes/rdgf.php
+++ b/integracao-rd-station/metaboxes/rdgf.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 	require_once('RD_Metabox.php');

 	class RDGF extends RD_Metabox {
--- a/integracao-rd-station/rd_custom_post_type.php
+++ b/integracao-rd-station/rd_custom_post_type.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 class RDCustomPostType {
   private $slug;
   public $acronym;
--- a/integracao-rd-station/rdsm_assets_loader.php
+++ b/integracao-rd-station/rdsm_assets_loader.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 class RDSMAssetsLoader {
   private static $src_url;

@@ -12,12 +17,33 @@
     add_action('admin_enqueue_scripts', array(get_class(), 'post_page_scripts'));
   }

-  // Pega a data de modificação do arquivo para usar como versão
   private static function get_version($file) {
     $file_path = plugin_dir_path(__FILE__) . 'assets' . $file;
     return file_exists($file_path) ? filemtime($file_path) : false;
   }

+  private static function enqueue_ajax_helper($hook, $dependent_handles = array()) {
+    wp_enqueue_script(
+      'rdsm_ajax_helper',
+      self::$src_url . '/js/rdsm_ajax.js',
+      array('jquery'),
+      self::get_version('/js/rdsm_ajax.js'),
+      true
+    );
+
+    if ( class_exists( 'RDSMSecurityHelper', false ) ) {
+      wp_localize_script(
+        'rdsm_ajax_helper',
+        'rdsmAdmin',
+        RDSMSecurityHelper::get_admin_ajax_config()
+      );
+    }
+
+    foreach ($dependent_handles as $handle) {
+      wp_enqueue_script($handle);
+    }
+  }
+
   public static function form_integrations_style($hook) {
     $screen = get_current_screen();
     if ('post.php' != $hook && 'post-new.php' != $hook) return;
@@ -34,11 +60,25 @@
     global $rdsm_settings_page;
     if ($hook != $rdsm_settings_page) return;

-    wp_enqueue_script('rdsm_general_settings_script', self::$src_url . '/js/general_settings.js', array(), self::get_version('/js/general_settings.js'));
-    wp_enqueue_script('rdsm_tracking_code_script', self::$src_url . '/js/tracking_code.js', array(), self::get_version('/js/tracking_code.js'));
-    wp_enqueue_script('rdsm_authorization_script', self::$src_url . '/js/authorization.js', array(), self::get_version('/js/authorization.js'));
-    wp_enqueue_script('rdsm_woocommerce_fields_script', self::$src_url . '/js/woocommerce_fields.js', array(), self::get_version('/js/woocommerce_fields.js'));
-    wp_enqueue_script('rdsm_log_file_script', self::$src_url . '/js/log_file.js', array(), self::get_version('/js/log_file.js'));
+    self::enqueue_ajax_helper($hook);
+
+    $scripts = array(
+      'rdsm_general_settings_script' => '/js/general_settings.js',
+      'rdsm_tracking_code_script' => '/js/tracking_code.js',
+      'rdsm_authorization_script' => '/js/authorization.js',
+      'rdsm_woocommerce_fields_script' => '/js/woocommerce_fields.js',
+      'rdsm_log_file_script' => '/js/log_file.js',
+    );
+
+    foreach ($scripts as $handle => $file) {
+      wp_enqueue_script(
+        $handle,
+        self::$src_url . $file,
+        array('jquery', 'rdsm_ajax_helper'),
+        self::get_version($file),
+        true
+      );
+    }
   }

   public static function settings_page_style($hook) {
@@ -57,10 +97,27 @@
     if ('post.php' != $hook && 'post-new.php' != $hook) return;

     wp_enqueue_script(
+      'rdsm_ajax_helper',
+      self::$src_url . '/js/rdsm_ajax.js',
+      array('jquery'),
+      self::get_version('/js/rdsm_ajax.js'),
+      true
+    );
+
+    if ( class_exists( 'RDSMSecurityHelper', false ) ) {
+      wp_localize_script(
+        'rdsm_ajax_helper',
+        'rdsmAdmin',
+        RDSMSecurityHelper::get_admin_ajax_config()
+      );
+    }
+
+    wp_enqueue_script(
       'rdsm_custom_fields_page_script',
       self::$src_url . '/js/custom_fields.js',
-      array(),
-      self::get_version('/js/custom_fields.js')
+      array('jquery', 'rdsm_ajax_helper'),
+      self::get_version('/js/custom_fields.js'),
+      true
     );
   }
 }
--- a/integracao-rd-station/rdsm_event_hooks.php
+++ b/integracao-rd-station/rdsm_event_hooks.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once(RDSM_SRC_DIR . '/events/rdsm_site_initialized.php');
 require_once(RDSM_SRC_DIR . '/events/rdsm_admin_initialized.php');
 require_once(RDSM_SRC_DIR . '/events/rdsm_oauth_connected.php');
--- a/integracao-rd-station/settings/settings_fields.php
+++ b/integracao-rd-station/settings/settings_fields.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 class RDSettingsFields {
   public function register_fields() {
     self::general_fields();
--- a/integracao-rd-station/settings/settings_menu.php
+++ b/integracao-rd-station/settings/settings_menu.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 add_action( 'admin_menu', 'rdstation_menu' );
 function rdstation_menu() {
   global $rdsm_settings_page;
--- a/integracao-rd-station/settings/settings_page.php
+++ b/integracao-rd-station/settings/settings_page.php
@@ -1,12 +1,61 @@
 <?php

+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 require_once('settings_menu.php');
 require_once('settings_sections.php');
 require_once('settings_fields.php');

+function rdsm_sanitize_general_settings( $input ) {
+  if ( ! is_array( $input ) ) {
+    return array();
+  }
+
+  $output = array();
+  if ( isset( $input['enable_tracking_code'] ) ) {
+    $output['enable_tracking_code'] = ( 1 === (int) $input['enable_tracking_code'] ) ? 1 : 0;
+  }
+
+  return $output;
+}
+
+function rdsm_sanitize_woocommerce_settings( $input ) {
+  if ( ! is_array( $input ) ) {
+    return array();
+  }
+
+  $output = array();
+
+  if ( isset( $input['conversion_identifier'] ) ) {
+    $output['conversion_identifier'] = sanitize_text_field( $input['conversion_identifier'] );
+  }
+
+  if ( isset( $input['field_mapping'] ) && is_array( $input['field_mapping'] ) ) {
+    $output['field_mapping'] = map_deep( $input['field_mapping'], 'sanitize_text_field' );
+  }
+
+  return $output;
+}
+
 function initialize_rdstation_settings_page() {
-  register_setting('rdsm_general_settings', 'rdsm_general_settings');
-  register_setting('rdsm_woocommerce_settings', 'rdsm_woocommerce_settings');
+  register_setting(
+    'rdsm_general_settings',
+    'rdsm_general_settings',
+    array(
+      'type'              => 'array',
+      'sanitize_callback' => 'rdsm_sanitize_general_settings',
+    )
+  );
+  register_setting(
+    'rdsm_woocommerce_settings',
+    'rdsm_woocommerce_settings',
+    array(
+      'type'              => 'array',
+      'sanitize_callback' => 'rdsm_sanitize_woocommerce_settings',
+    )
+  );

   $sections = new RDSettingsSection;
   $sections->register_sections();
@@ -63,7 +112,6 @@
   </p>

   <form action='options.php' method='post'>
-  <input id="rd_form_nonce" name="rd_form_nonce" type="hidden" value="<?php echo esc_attr(wp_create_nonce('rd-form-nonce'))?>" />
     <?php
       switch ($active_tab) {
         case 'general':
--- a/integracao-rd-station/settings/settings_sections.php
+++ b/integracao-rd-station/settings/settings_sections.php
@@ -1,5 +1,10 @@
 <?php

+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 class RDSettingsSection {
   public function register_sections() {
     add_settings_section(

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-49774
# This rule targets the missing authorization in AJAX handlers by blocking direct access to sensitive AJAX actions
# that should only be available to users with sufficient permissions. However, since the same actions are used
# legitimately by authorized users, this rule may cause false positives and is provided only for environments
# where the plugin cannot be patched immediately and strict access control is acceptable.

SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
  "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-49774 - RD Station AJAX missing authorization',severity:'CRITICAL',tag:'CVE-2026-49774'"
  SecRule ARGS_POST:action "@pm rdsm_authorization_check rdsm_log_file rdsm_clear_log_file rdsm_get_log_by_filter rdsm_update_tracking_code rdsm_woocommerce_fields rdsm_custom_fields rd_persist_tokens rd_persist_legacy_tokens rdsm_disconnect_oauth" "chain"
    SecRule REQUEST_METHOD "@streq POST" "t:none"

Frequently Asked Questions

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.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School