Conversation
Security: - Add nonce verification and capability checks to settings and widgets admin pages (CSRF fix) - Sanitize $_POST['ua_display'] array before saving to post meta - Replace query_posts() with WP_Query to avoid corrupting global $wp_query - Escape all unescaped widget output (esc_html/esc_attr/wp_kses_post) across 15+ widgets - Fix unescaped admin category list output - Replace _e() without escaping with esc_html() in metabox label PHP 8+ compatibility: - Fix incorrect cast syntax `(int) $var = $val` → `$var = (int) $val` (shortcode.php, functions.php) - Replace extract() in shortcode.php with explicit variable access - Replace extract() in ultraaddons_get_image_cropped_url() with explicit assignments - Raise minimum PHP version constant from 5.4 to 7.4 (consistent with enforced minimum) Performance: - Restrict admin asset enqueue to UltraAddons pages only (owl, remodal, select2, admin.js) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- settings.php: unset nonce + _wp_http_referer before update_option() to prevent WP internal fields from being stored in the plugin settings option - loader.php: use elementor/widgets/register hook (Elementor 3.5.0+) with fallback to elementor/widgets/widgets_registered for older installs - loader.php: use widgets_manager->register() (Elementor 3.5.0+) with fallback to register_widget_type() to eliminate deprecation notices on current Elementor versions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates UltraAddons’ Elementor integration and hardens multiple frontend/admin outputs for newer Elementor versions and improved security.
Changes:
- Added Elementor 3.5+ compatibility for widget registration hooks/APIs.
- Raised the plugin’s minimum PHP requirement to 7.4.
- Improved security by adding escaping/sanitization and adding nonces/capability checks on admin saves.
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| loader.php | Switch widget registration hook and widget manager API based on Elementor version. |
| init.php | Update minimum PHP version constants to 7.4. |
| inc/wp/shortcode.php | Refactor shortcode attribute parsing / casting. |
| inc/wp/header-footer-post.php | Escape dynamic label output; replace query_posts() with WP_Query; sanitize meta input. |
| inc/functions.php | Fix incorrect cast assignment; replace extract() with explicit arg defaults. |
| admin/pages/widgets.php | Add nonce/capability checks for saving disabled widgets; sanitize saved values; escape category output. |
| admin/pages/settings.php | Add nonce/capability checks before saving settings; avoid persisting nonce fields. |
| admin/admin-handle.php | Restrict admin asset enqueues to UltraAddons/admin-relevant screens. |
| inc/widget/*.php | Apply escaping (esc_html, esc_attr, wp_kses_post) to various widget outputs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <img src="<?php echo $image;?>"> | ||
| <div class="team-content-wrap"> | ||
| <h2 class="ua-team-title"><?php echo $settings['title'];?></h2> | ||
| <span class="who"><?php echo $settings['designation'];?></span> | ||
| <p class="member-text"><?php echo $settings['description'];?></p> | ||
| <h2 class="ua-team-title"><?php echo esc_html( $settings['title'] );?></h2> |
There was a problem hiding this comment.
The member image URL is output without escaping (<img src="...">). Since this comes from widget settings, it should be escaped with esc_url() (and ideally include an alt attribute) to prevent attribute injection and invalid markup.
| <a href="<?php echo esc_url($url); ?>" <?php echo esc_attr($is_external);?> <?php echo esc_attr($nofollow);?> class="ua-hotspot elementor-repeater-item-<?php echo esc_attr( $item['_id'] ); ?> ua-hotspot--<?php echo esc_attr( $count );?>"> | ||
| <span class="ua-hotspot--title"><?php echo esc_html( $item['list_title'] );?></span> |
There was a problem hiding this comment.
$is_external / $nofollow are full attribute fragments like target="_blank" / rel="nofollow". Escaping them with esc_attr() will convert quotes to entities and results in broken attributes in the rendered <a> tag. Instead, output these attributes in a safer structured way (e.g., conditionally add target/rel with escaped values, or use Elementor render attributes).
| <h1 class="ua-slider-title elementor-repeater-item-<?php echo esc_attr( $item['_id'] ); ?>"><?php echo wp_kses_post( $item['list_content'] );?></h1> | ||
| <a href="<?php echo esc_url($url); ?>" <?php echo esc_attr($is_external);?> <?php echo esc_attr($nofollow);?> class="ua-slider-buttton elementor-repeater-item-<?php echo esc_attr( $item['_id'] ); ?> <?php echo esc_attr( $settings['_ua_btn_animation'] );?>"><?php echo esc_html( $item['list_btn'] );?></a> | ||
| </div> |
There was a problem hiding this comment.
$is_external / $nofollow are attribute fragments (e.g., target="_blank", rel="nofollow"). Wrapping them in esc_attr() breaks the attributes (quotes become entities), so the link won’t actually get target/rel. Build these as real attributes (or via Elementor render attributes) instead of echoing escaped fragments.
| <a class="button ua-sign-up" href="<?php echo esc_url($url); ?>" <?php echo esc_attr($is_external);?> <?php echo esc_attr($nofollow);?>> | ||
| <?php echo $item['list_button'];?> | ||
| <?php echo esc_html( $item['list_button'] );?> | ||
| </a> |
There was a problem hiding this comment.
$is_external / $nofollow are complete attribute snippets and should not be passed through esc_attr(), otherwise the quotes are entity-encoded and the resulting <a> tag has broken target/rel attributes. Add target/rel as proper attributes with escaped values (or use Elementor render attributes).
| if( empty( $atts['id'] ) ){ | ||
| return; | ||
| } | ||
| $pairs = array( 'exclude' => false ); | ||
| extract( shortcode_atts( $pairs, $atts ) ); | ||
| $post_id = (int) $atts['id']; | ||
|
|
||
| $post_id = ! empty( $atts['id'] ) ? (int) $atts['id'] : 0; | ||
|
|
||
| if( ! $post_id ){ | ||
| $post_id = (int) $atts['template_id']; | ||
| $post_id = ! empty( $atts['template_id'] ) ? (int) $atts['template_id'] : 0; | ||
| } | ||
| if( ! $post_id ){ | ||
| $post_id = (int) $atts['post_id']; | ||
| $post_id = ! empty( $atts['post_id'] ) ? (int) $atts['post_id'] : 0; | ||
| } |
There was a problem hiding this comment.
show_template() returns early when id is missing, but the code (and the Settings page text) indicates template_id and post_id are also supported. As written, those fallback attributes are unreachable. Remove the early return and let the $post_id resolution logic decide whether to return.
|
|
||
| (int) $select_post_id = $post_id; | ||
| $select_post_id = (int) $post_id; | ||
| if ( \Elementor\Plugin::instance()->documents->get( $select_post_id )->is_built_with_elementor() ) { |
There was a problem hiding this comment.
ultraaddons_elementor_display_content() dereferences documents->get( $select_post_id ) without checking it returned a document. If the post ID is invalid or not an Elementor document, this can be null and cause a fatal error when calling ->is_built_with_elementor(). Add a null check (similar to the shortcode implementation) before calling methods on the document.
| if ( \Elementor\Plugin::instance()->documents->get( $select_post_id )->is_built_with_elementor() ) { | |
| $document = \Elementor\Plugin::instance()->documents->get( $select_post_id ); | |
| if ( $document && $document->is_built_with_elementor() ) { |
No description provided.