Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- 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(