Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the Easy Image Gallery plugin to address WordPress Plugin Check feedback and improve security hardening/escaping, alongside a version bump for the next release.
Changes:
- Bump plugin version/stable tag to 1.5.4 and update readme compatibility/changelog.
- Harden admin settings + metabox saving via
wp_unslash()+map_deep()sanitization and improved capability/nonce checks. - Improve output escaping and inline script handling (e.g.,
wp_rand(),esc_attr()for classes,wp_print_inline_script_tag()).
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| readme.txt | Updates tested/stable versions and adds 1.5.4 changelog entry. |
| easy-image-gallery.php | Bumps plugin header version to 1.5.4. |
| includes/view/admin-page-view.php | Sanitizes option saving and improves escaping in admin settings UI. |
| includes/template-functions.php | Uses wp_rand(), improves escaping, and adjusts luminous init output. |
| includes/scripts.php | Changes Gutenberg lightbox asset enqueuing and prints inline JS via wp_print_inline_script_tag(). |
| includes/metabox.php | Improves escaping, uses wp_rand(), and tightens save handler validation. |
| includes/gutenberg-block/src/init.php | Adds shared asset versioning and sets explicit enqueue versions/media. |
| includes/admin-page.php | Renames the admin menu registration function for clarity. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
includes/scripts.php
Outdated
| wp_enqueue_script('repeatable-fields',EASY_IMAGE_GALLERY_URL . 'includes/lib/repeatable-fields.js',array( 'jquery', 'jquery-ui-core' ),EASY_IMAGE_GALLERY_VERSION,false); | ||
| wp_enqueue_style('easy_image_gallery_admin_css',EASY_IMAGE_GALLERY_URL . 'includes/css/easy-image-gallery-admin.css',array(),EASY_IMAGE_GALLERY_VERSION); |
There was a problem hiding this comment.
The admin enqueue calls were reformatted into a single line without the spacing/argument wrapping used elsewhere in this file, which makes future maintenance harder and can conflict with PHPCS/WPCS if enforced. Consider formatting these calls consistently with the rest of includes/scripts.php.
| wp_enqueue_script('repeatable-fields',EASY_IMAGE_GALLERY_URL . 'includes/lib/repeatable-fields.js',array( 'jquery', 'jquery-ui-core' ),EASY_IMAGE_GALLERY_VERSION,false); | |
| wp_enqueue_style('easy_image_gallery_admin_css',EASY_IMAGE_GALLERY_URL . 'includes/css/easy-image-gallery-admin.css',array(),EASY_IMAGE_GALLERY_VERSION); | |
| wp_enqueue_script( | |
| 'repeatable-fields', | |
| EASY_IMAGE_GALLERY_URL . 'includes/lib/repeatable-fields.js', | |
| array( 'jquery', 'jquery-ui-core' ), | |
| EASY_IMAGE_GALLERY_VERSION, | |
| false | |
| ); | |
| wp_enqueue_style( | |
| 'easy_image_gallery_admin_css', | |
| EASY_IMAGE_GALLERY_URL . 'includes/css/easy-image-gallery-admin.css', | |
| array(), | |
| EASY_IMAGE_GALLERY_VERSION | |
| ); |
readme.txt
Outdated
| Requires at least: 3.5 | ||
| Tested up to: 6.8.3 | ||
| Stable tag: 1.5.3 | ||
| Tested up to: 6.9 |
There was a problem hiding this comment.
The readme header was updated to "Tested up to: 6.9", but the changelog/PR description indicates testing against 6.9.4. WordPress.org typically expects the exact tested version here; consider updating the header to match the specific WP version you validated (e.g. 6.9.4) to avoid confusion during Plugin Check/review.
| Tested up to: 6.9 | |
| Tested up to: 6.9.4 |
includes/scripts.php
Outdated
| // CSS (empty src: use handle registered in easy_image_gallery_scripts()). | ||
| wp_enqueue_style( | ||
| $value, | ||
| '', | ||
| array(), | ||
| EASY_IMAGE_GALLERY_VERSION, | ||
| 'screen' | ||
| ); | ||
|
|
||
| // JS — explicit version so caches bust when the plugin updates. | ||
| wp_enqueue_script( | ||
| $value, | ||
| '', | ||
| array(), | ||
| EASY_IMAGE_GALLERY_VERSION, | ||
| true | ||
| ); |
There was a problem hiding this comment.
The Gutenberg lightbox loop is calling wp_enqueue_style() / wp_enqueue_script() with an empty string for $src. In WordPress, passing a non-false $src causes the handle to be (re)registered, so this can overwrite the previously registered pretty-photo / fancybox / luminous handles with an empty URL and produce <link href=""> / <script src=""> tags (or otherwise break enqueuing). Enqueue the already-registered handles without providing $src, and only enqueue the asset type that actually exists for each handle (e.g. no CSS handle for luminous).
| // CSS (empty src: use handle registered in easy_image_gallery_scripts()). | |
| wp_enqueue_style( | |
| $value, | |
| '', | |
| array(), | |
| EASY_IMAGE_GALLERY_VERSION, | |
| 'screen' | |
| ); | |
| // JS — explicit version so caches bust when the plugin updates. | |
| wp_enqueue_script( | |
| $value, | |
| '', | |
| array(), | |
| EASY_IMAGE_GALLERY_VERSION, | |
| true | |
| ); | |
| // Enqueue already-registered handles without overriding their src. | |
| if ( 'luminous' === $value ) { | |
| // luminous has only a JS handle. | |
| wp_enqueue_script( 'luminous' ); | |
| } else { | |
| // pretty-photo, fancybox, etc. have both CSS and JS handles. | |
| wp_enqueue_style( $value ); | |
| wp_enqueue_script( $value ); | |
| } |
| $js = apply_filters( 'easy_image_gallery_prettyphoto_js', $js ); | ||
| if ( preg_match( '/<script\b[^>]*>(.*)<\/script>/is', $js, $matches ) ) { | ||
| $js = $matches[1]; | ||
| } | ||
| wp_print_inline_script_tag( trim( $js ) ); |
There was a problem hiding this comment.
easy_image_gallery_prettyphoto_js previously received/returned a full <script>...</script> block (because $js is built with script tags). The new regex extraction strips the first <script> wrapper and drops any script attributes or additional script tags a filter might add, which is a backward-incompatible change to the filter contract. Consider generating $js as raw JS (no <script> tags) before applying the filter, or only stripping when the output exactly matches the original wrapper format so filtered HTML isn't silently truncated.
| if ( preg_match( '/<script\b[^>]*>(.*)<\/script>/is', $js, $matches ) ) { | ||
| $js = $matches[1]; | ||
| } | ||
| wp_print_inline_script_tag( trim( $js ) ); |
There was a problem hiding this comment.
Same issue as the prettyPhoto branch: easy_image_gallery_fancybox_js receives a full <script> block, but the new regex stripping + wp_print_inline_script_tag() will drop script attributes and can truncate output if the filter returns more than one <script> tag. Consider switching the buffer to raw JS before filtering, or add stricter handling so filtered output isn't unintentionally altered.
| if ( preg_match( '/<script\b[^>]*>(.*)<\/script>/is', $js, $matches ) ) { | |
| $js = $matches[1]; | |
| } | |
| wp_print_inline_script_tag( trim( $js ) ); | |
| if ( preg_match_all( '/<script\b[^>]*>(.*?)<\/script>/is', $js, $matches ) && ! empty( $matches[1] ) ) { | |
| $scripts = array(); | |
| foreach ( $matches[1] as $script_content ) { | |
| $script_content = trim( $script_content ); | |
| if ( '' !== $script_content ) { | |
| $scripts[] = $script_content; | |
| } | |
| } | |
| if ( ! empty( $scripts ) ) { | |
| $js = implode( "\n", $scripts ); | |
| } else { | |
| $js = ''; | |
| } | |
| } | |
| $js = trim( $js ); | |
| if ( '' !== $js ) { | |
| wp_print_inline_script_tag( $js ); | |
| } |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@PGeorgiev , can you review and resolve the suggestions by Copilot and request another review after that 🙏 |
|
Hey @lgadzhev, |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if ( isset( $_POST['easy-image-gallery'] ) && is_array( $_POST['easy-image-gallery'] ) ) { | ||
| $easy_image_gallery_submitted = map_deep( wp_unslash( $_POST['easy-image-gallery'] ), 'sanitize_text_field' ); | ||
| update_option( 'easy-image-gallery', $easy_image_gallery_submitted ); |
There was a problem hiding this comment.
map_deep() is only available in WordPress >= 4.4. Since the plugin readme declares support back to WP 3.5, this call will fatal on older installs unless you either bump the minimum WP requirement or add a small polyfill / fallback sanitization for older core versions.
| // Check user permissions (use stored post type, not $_POST — avoids unverified POST reads). | ||
| $easy_image_gallery_current_post_type = get_post_type( $post_id ); | ||
| if ( $easy_image_gallery_current_post_type && ! array_key_exists( $easy_image_gallery_current_post_type, $post_types ) ) { | ||
| if ( ! current_user_can( 'edit_page', $post_id ) ) { | ||
| return; | ||
| } | ||
| } else { | ||
| if ( ! current_user_can( 'edit_post', $post_id ) ) { | ||
| return; | ||
| } | ||
| } elseif ( ! current_user_can( 'edit_post', $post_id ) ) { | ||
| return; |
There was a problem hiding this comment.
The allowed-post-types check looks inverted here: when the current post type is not in the allowed list, the code may still proceed (for users who can edit the post) and write gallery meta on disallowed post types. If the post type isn’t allowed, this function should bail out unconditionally before any capability checks / meta updates.
|
|
||
| foreach ( $_POST['image_gallery'] as $gallery ) { | ||
| $gallery['DATA'] = sanitize_text_field( $gallery['DATA'] ); | ||
| $easy_image_gallery_post = map_deep( wp_unslash( $_POST['image_gallery'] ), 'sanitize_text_field' ); | ||
|
|
||
| foreach ( $easy_image_gallery_post as $gallery ) { |
There was a problem hiding this comment.
map_deep() requires WordPress >= 4.4. If the plugin intends to keep the minimum supported WP version lower than that, this needs a compatibility fallback (or update the plugin’s minimum WP requirement) to avoid fatals on older sites.
| if ( preg_match( '/<script\b[^>]*>(.*)<\/script>/is', $js, $matches ) ) { | ||
| $js = $matches[1]; |
There was a problem hiding this comment.
The regex used to strip a wrapping <script> tag is greedy ((.*)), so if a filter returns multiple script tags or includes </script> in a string, it can capture too much and produce broken JS. Consider using a non-greedy match and/or a more robust way to extract the script contents before calling wp_print_inline_script_tag().
| if ( preg_match( '/<script\b[^>]*>(.*)<\/script>/is', $js, $matches ) ) { | |
| $js = $matches[1]; | |
| if ( preg_match_all( '/<script\b[^>]*>(.*?)<\/script>/is', $js, $matches ) ) { | |
| $js = implode( "\n", $matches[1] ); |
| Plugin URI: https://devrix.com/ | ||
| Description: An easy to use image gallery with drag & drop re-ordering | ||
| Version: 1.5.3 | ||
| Version: 1.5.4 |
There was a problem hiding this comment.
The plugin header version was bumped to 1.5.4, but EASY_IMAGE_GALLERY_VERSION (used for cache-busting enqueued assets, and now also for block asset versions) is still defined as 1.2. This will prevent users from receiving updated CSS/JS after upgrading unless caches are cleared; align the constant with the plugin version (or derive it from the header).
We have fixed a number of Plugin Check feedbacks and covered some vulnerability issues:
Asana task - Easy Image Gallery: Closure Notice - Security: Easy Image Gallery