Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/generateblocks/dist/block-styles-imported.asset.php
+++ b/generateblocks/dist/block-styles-imported.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-primitives'), 'version' => '32c13c2d41956f0a2114');
+<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-primitives'), 'version' => '5a299c4ea3f583492a88');
--- a/generateblocks/dist/block-styles.asset.php
+++ b/generateblocks/dist/block-styles.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => '1aa48bee1f28155add8f');
+<?php return array('dependencies' => array(), 'version' => '1ced1364f58063651ff9');
--- a/generateblocks/dist/blocks.asset.php
+++ b/generateblocks/dist/blocks.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-rich-text', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => 'db357e3dd61e960d01a6');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-rich-text', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '53fc4c8a5af028e0edf2');
--- a/generateblocks/dist/blocks/element/index.asset.php
+++ b/generateblocks/dist/blocks/element/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '51196552e65528dac0ca');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => 'e17bcd02cda783524c7d');
--- a/generateblocks/dist/blocks/loop-item/index.asset.php
+++ b/generateblocks/dist/blocks/loop-item/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => 'a1d1a4d3d9c9786a033d');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '50e20994be30d4409efa');
--- a/generateblocks/dist/blocks/looper/index.asset.php
+++ b/generateblocks/dist/blocks/looper/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '53cca35b75b35225ca01');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '054ebf604f788fbd1704');
--- a/generateblocks/dist/blocks/media/index.asset.php
+++ b/generateblocks/dist/blocks/media/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-rich-text', 'wp-url', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '6daba08e7a12042835b5');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-rich-text', 'wp-url', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '7206190c31b045d4da59');
--- a/generateblocks/dist/blocks/query-page-numbers/index.asset.php
+++ b/generateblocks/dist/blocks/query-page-numbers/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => 'caf070ab3b6985dc3212');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '3090edd2b9d282595772');
--- a/generateblocks/dist/blocks/query/index.asset.php
+++ b/generateblocks/dist/blocks/query/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '7ed2e3c07e89bbf8d708');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => 'd3897e4bf18e4e0a62cc');
--- a/generateblocks/dist/blocks/shape/index.asset.php
+++ b/generateblocks/dist/blocks/shape/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '67389ff85748fcd4f5d6');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '7ba05ff505bc57b103bd');
--- a/generateblocks/dist/blocks/text/index.asset.php
+++ b/generateblocks/dist/blocks/text/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '1f5c4b464951d73a6cc5');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives', 'wp-rich-text', 'generateblocks-block-styles', 'generateblocks-components', 'generateblocks-styles-builder'), 'version' => '60f42021ac9354581e61');
--- a/generateblocks/dist/components-imported.asset.php
+++ b/generateblocks/dist/components-imported.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives'), 'version' => '8ef3584c930296a96056');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives'), 'version' => '06abed89c01bf4487af6');
--- a/generateblocks/dist/components.asset.php
+++ b/generateblocks/dist/components.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => 'c9b4558761b6a0600feb');
+<?php return array('dependencies' => array(), 'version' => '14f487719f36042daa77');
--- a/generateblocks/dist/editor-sidebar.asset.php
+++ b/generateblocks/dist/editor-sidebar.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-components', 'wp-data', 'wp-edit-post', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins', 'wp-primitives'), 'version' => '521af1dc95b114499b98');
+<?php return array('dependencies' => array('react', 'wp-components', 'wp-data', 'wp-edit-post', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins', 'wp-primitives'), 'version' => '3579a361e27555dc8314');
--- a/generateblocks/dist/editor.asset.php
+++ b/generateblocks/dist/editor.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-hooks', 'wp-i18n', 'generateblocks-styles-builder'), 'version' => '8e257f8c535e78ea1951');
+<?php return array('dependencies' => array('lodash', 'react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-hooks', 'wp-i18n', 'generateblocks-styles-builder'), 'version' => '6a3842a230b2512408b1');
--- a/generateblocks/dist/looper.asset.php
+++ b/generateblocks/dist/looper.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => 'a2764d77dadb926974fa');
+<?php return array('dependencies' => array(), 'version' => '8bb4e340fa77dd8e826b');
--- a/generateblocks/dist/packages.asset.php
+++ b/generateblocks/dist/packages.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => 'ff6cec35c1a43d6d3156');
+<?php return array('dependencies' => array(), 'version' => 'd4575a13713a6dbb8ca9');
--- a/generateblocks/dist/pattern-library.asset.php
+++ b/generateblocks/dist/pattern-library.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-primitives', 'wp-url', 'generateblocks-components'), 'version' => '583a8fa7b688af7ea068');
+<?php return array('dependencies' => array('lodash', 'react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-primitives', 'wp-url', 'generateblocks-components'), 'version' => '614f6f2ee69a9a023161');
--- a/generateblocks/dist/settings.asset.php
+++ b/generateblocks/dist/settings.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '705a0eafa900419b51d0');
+<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '4cf302fbe8d7a64b7413');
--- a/generateblocks/dist/styles-builder-imported.asset.php
+++ b/generateblocks/dist/styles-builder-imported.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('generateblocks-components', 'lodash', 'react', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom', 'wp-edit-post', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-primitives'), 'version' => 'ef96c3175dcdba3e03ba');
+<?php return array('dependencies' => array('generateblocks-components', 'lodash', 'react', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom', 'wp-edit-post', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-primitives'), 'version' => 'c372420b43785c738617');
--- a/generateblocks/dist/styles-builder.asset.php
+++ b/generateblocks/dist/styles-builder.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => '45641983462deb0532dd');
+<?php return array('dependencies' => array(), 'version' => '41f06e2ee1eeb62d21b1');
--- a/generateblocks/includes/blocks/class-headline.php
+++ b/generateblocks/includes/blocks/class-headline.php
@@ -807,7 +807,7 @@
if ( $dynamic_link ) {
$dynamic_content = sprintf(
'<a href="%s">%s</a>',
- $dynamic_link,
+ esc_url( $dynamic_link ),
$dynamic_content
);
}
--- a/generateblocks/includes/class-dynamic-content.php
+++ b/generateblocks/includes/class-dynamic-content.php
@@ -773,7 +773,7 @@
if ( $image_id && ! is_numeric( $image_id ) ) {
// Our image ID isn't a number - must be a static URL.
- return $image_id;
+ return is_string( $image_id ) ? esc_url_raw( $image_id ) : '';
}
} else {
$image_id = ! empty( $attributes['mediaId'] ) ? $attributes['mediaId'] : false;
@@ -889,12 +889,14 @@
}
}
- return apply_filters(
+ $url = apply_filters(
'generateblocks_dynamic_url_output',
$url,
$attributes,
$block
);
+
+ return is_string( $url ) ? esc_url_raw( $url ) : '';
}
/**
--- a/generateblocks/includes/class-query-utils.php
+++ b/generateblocks/includes/class-query-utils.php
@@ -67,30 +67,45 @@
public function get_user_query( WP_REST_Request $request ) {
$args = $request->get_param( 'args' ) ?? [];
+ if ( ! is_array( $args ) ) {
+ $args = [];
+ }
+
+ $args['number'] = min( max( 1, absint( $args['number'] ?? 150 ) ), 150 );
+ $args['paged'] = max( 1, (int) ( $args['paged'] ?? 1 ) );
+ $args['fields'] = 'all';
+
+ $can_list_users = current_user_can( 'list_users' );
+ $can_manage_options = current_user_can( 'manage_options' );
+
// Sanitize dangerous query args for users without list_users capability.
- if ( ! current_user_can( 'list_users' ) ) {
+ if ( ! $can_list_users ) {
unset( $args['meta_query'] );
unset( $args['meta_key'] );
unset( $args['meta_value'] );
unset( $args['meta_compare'] );
- }
+ unset( $args['role'] );
+ unset( $args['role__in'] );
+ unset( $args['role__not_in'] );
+ unset( $args['capability'] );
+ unset( $args['capability__in'] );
+ unset( $args['capability__not_in'] );
- if ( ! isset( $args['number'] ) ) {
- $args['number'] = 150;
+ $args['has_published_posts'] = true;
}
$number = $args['number'];
$query = new WP_User_Query( $args );
- $max_pages = round( $query->get_total() / $number );
+ $max_pages = (int) ceil( $query->get_total() / $number );
// Filter sensitive data from user objects while maintaining structure.
$users = array_map(
- function( $user ) {
+ function( $user ) use ( $can_manage_options, $can_list_users ) {
// Always remove critical security fields.
unset( $user->data->user_pass );
unset( $user->data->user_activation_key );
- if ( ! current_user_can( 'manage_options' ) ) {
+ if ( ! $can_manage_options ) {
// Remove sensitive values for non-admin users.
unset( $user->data->user_login );
unset( $user->data->user_email );
@@ -100,6 +115,11 @@
unset( $user->allcaps );
}
+ if ( ! $can_list_users ) {
+ unset( $user->roles );
+ unset( $user->cap_key );
+ }
+
return $user;
},
$query->get_results()
@@ -123,7 +143,9 @@
*/
public function get_wp_query( $request ) {
$args = $request->get_param( 'args' );
+ $args = is_array( $args ) ? $args : [];
$page = $args['paged'] ?? $request->get_param( 'page' ) ?? 1;
+ $page = is_scalar( $page ) ? (int) $page : 1;
$attributes = $request->get_param( 'attributes' ) ?? [];
$current_post = $request->get_param( 'postId' ) ?? null;
$current_author = $request->get_param( 'authorId' ) ?? null;
@@ -208,7 +230,7 @@
$args['paged'] = $page;
}
- if ( isset( $args['tax_query'] ) ) {
+ if ( isset( $args['tax_query'] ) && is_array( $args['tax_query'] ) ) {
if ( count( $args['tax_query'] ) > 1 ) {
$args['tax_query']['relation'] = 'AND';
}
--- a/generateblocks/includes/dynamic-tags/class-dynamic-tags.php
+++ b/generateblocks/includes/dynamic-tags/class-dynamic-tags.php
@@ -502,7 +502,12 @@
$content = $request->get_param( 'content' );
$context = $request->get_param( 'context' );
$client_id = $request->get_param( 'clientId' );
- $post_id = absint( $context['postId'] ?? 0 );
+ $post_id = absint( is_array( $context ) ? ( $context['postId'] ?? 0 ) : 0 );
+
+ // Bail if the content isn't a string we can scan for dynamic tags.
+ if ( ! is_string( $content ) ) {
+ return rest_ensure_response( [] );
+ }
// Verify the current user can read the context post.
if ( $post_id && ! current_user_can( 'read_post', $post_id ) ) {
--- a/generateblocks/includes/functions.php
+++ b/generateblocks/includes/functions.php
@@ -651,6 +651,20 @@
}
/**
+ * Determine whether an HTML attribute name holds a URL value.
+ *
+ * @since 2.3.0
+ *
+ * @param string $name The attribute name.
+ * @return bool True when the attribute should be treated as a URL.
+ */
+function generateblocks_is_url_attribute( $name ) {
+ $url_attributes = array( 'href', 'src', 'action' );
+
+ return in_array( strtolower( (string) $name ), $url_attributes, true );
+}
+
+/**
* Build list of attributes into a string and apply contextual filter on string.
*
* The contextual filter is of the form `generateblocks_attr_{context}_output`.
@@ -678,7 +692,13 @@
if ( true === $value ) {
$output .= esc_html( $key ) . ' ';
} else {
- $output .= sprintf( '%s="%s" ', esc_html( $key ), esc_attr( $value ) );
+ $is_url_attribute = generateblocks_is_url_attribute( $key );
+
+ $escaped_value = $is_url_attribute
+ ? esc_url( do_shortcode( $value ) )
+ : esc_attr( $value );
+
+ $output .= sprintf( '%s="%s" ', esc_html( $key ), $escaped_value );
}
}
@@ -1263,7 +1283,7 @@
// Add inline <style> elements if we don't have access to wp_head.
$content = sprintf(
'<style>%s</style>',
- $css
+ wp_strip_all_tags( $css )
) . $content;
} else {
// Add our CSS to the pool of existing CSS in wp_head.
@@ -1327,7 +1347,7 @@
if ( $compiled_css ) {
$content = sprintf(
'<style>%s</style>',
- $compiled_css
+ wp_strip_all_tags( $compiled_css )
) . $content;
}
} else {
@@ -2085,15 +2105,10 @@
function generateblocks_get_backup_html_attributes( $block_slug, $attributes ) {
$classes = generateblocks_get_block_classes( $block_slug, $attributes );
- $html_attributes = generateblocks_with_html_attributes(
- [
- 'id' => $attributes['anchor'] ?? null,
- 'class' => implode( ' ', $classes ),
- ],
- $attributes
- );
-
- return $html_attributes;
+ return [
+ 'id' => $attributes['anchor'] ?? null,
+ 'class' => implode( ' ', $classes ),
+ ];
}
/**
@@ -2128,8 +2143,7 @@
* @param string $value The raw attribute value.
*/
function generateblocks_get_escaped_html_attribute( $name, $value ) {
- $url_fields = [ 'src', 'href' ];
- $is_url_field = in_array( $name, $url_fields, true );
+ $is_url_field = generateblocks_is_url_attribute( $name );
if ( $is_url_field && ! empty( $value ) ) {
$value = do_shortcode( $value ); // esc_url() escapes shortcodes, so we need to do this first.
--- a/generateblocks/includes/general.php
+++ b/generateblocks/includes/general.php
@@ -222,48 +222,30 @@
$packages_asset_info['version']
);
- // Enqueue scripts for all edge22 packages in the plugin.
- $package_json = GENERATEBLOCKS_DIR . 'package.json';
-
- if ( file_exists( $package_json ) ) {
- $package_json_parsed = json_decode(
- file_get_contents( $package_json ), // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
+ $edge22_packages = [
+ 'block-styles',
+ 'components',
+ 'styles-builder',
+ ];
+
+ foreach ( $edge22_packages as $name ) {
+ $package_asset_info = generateblocks_get_enqueue_assets( "{$name}-imported" );
+ $script_asset_info = generateblocks_get_enqueue_assets( $name );
+
+ wp_register_script(
+ "generateblocks-$name",
+ GENERATEBLOCKS_DIR_URL . 'dist/' . $name . '.js',
+ $package_asset_info['dependencies'],
+ $script_asset_info['version'],
true
);
- $edge22_packages = array_filter(
- $package_json_parsed['dependencies'],
- function( $package_name ) {
- return 0 === strpos( $package_name, '@edge22/' );
- },
- ARRAY_FILTER_USE_KEY
+ wp_register_style(
+ "generateblocks-$name",
+ false,
+ [ 'generateblocks-packages' ],
+ $packages_asset_info['version']
);
-
- foreach ( $edge22_packages as $name => $version ) {
- $name = str_replace( '@edge22/', '', $name );
- $path = GENERATEBLOCKS_DIR . "dist/{$name}-imported.asset.php";
-
- if ( ! file_exists( $path ) ) {
- continue;
- }
-
- $package_info = require $path;
-
- wp_register_script(
- "generateblocks-$name",
- GENERATEBLOCKS_DIR_URL . 'dist/' . $name . '.js',
- $package_info['dependencies'],
- $version,
- true
- );
-
- wp_register_style(
- "generateblocks-$name",
- GENERATEBLOCKS_DIR_URL . 'dist/' . $name . '.css',
- [],
- $version
- );
- }
}
$editor_assets = generateblocks_get_enqueue_assets( 'editor' );
@@ -292,6 +274,16 @@
'generateBlocksEditor',
[
'useV1Blocks' => generateblocks_use_v1_blocks(),
+ /**
+ * Filters whether WordPress 7.0's core "Additional CSS" textarea is
+ * shown on GenerateBlocks blocks. We disable it by default because our
+ * blocks have their own CSS editing; return true to re-enable it.
+ *
+ * @since 2.3.0
+ *
+ * @param bool $enabled Whether to allow the core Additional CSS field.
+ */
+ 'enableCoreAdditionalCss' => (bool) apply_filters( 'generateblocks_enable_core_additional_css', false ),
'dynamicTags' => $tag_list,
'hasGPFontLibrary' => function_exists( 'generatepress_is_module_active' )
? generatepress_is_module_active( 'generate_package_font_library', 'GENERATE_FONT_LIBRARY' )
--- a/generateblocks/includes/pattern-library/class-libraries.php
+++ b/generateblocks/includes/pattern-library/class-libraries.php
@@ -91,13 +91,23 @@
// Get saved library data.
$saved_libraries = get_option( 'generateblocks_pattern_libraries', [] );
+ if ( ! is_array( $saved_libraries ) ) {
+ $saved_libraries = [];
+ }
+
// Create library instances for any remote sites that provide the complete set of data.
$remote_libraries = array_map(
[ $this, 'create' ],
array_filter(
$saved_libraries,
function( $saved_library ) {
- return isset( $saved_library['isLocal'] ) && ! $saved_library['isLocal'];
+ return is_array( $saved_library ) &&
+ isset( $saved_library['isLocal'] ) &&
+ ! rest_sanitize_boolean( $saved_library['isLocal'] ) &&
+ ! empty( $saved_library['id'] ) &&
+ is_scalar( $saved_library['id'] ) &&
+ ! empty( $saved_library['domain'] ) &&
+ is_string( $saved_library['domain'] );
}
)
);
@@ -108,6 +118,17 @@
$remote_libraries
);
+ if ( ! is_array( $libraries ) ) {
+ $libraries = [];
+ }
+
+ $libraries = array_filter(
+ $libraries,
+ function( $library ) {
+ return $this->is_valid_library( $library );
+ }
+ );
+
// Add our default library at the start of the list.
if ( ! self::exists( $libraries, $this->default_library_id ) ) {
$default_library = self::get_default();
@@ -117,8 +138,12 @@
// Loop our libraries and set their status based on their saved value.
foreach ( $libraries as $key => $library ) {
$saved_data = array_filter(
- get_option( 'generateblocks_pattern_libraries', [] ),
+ $saved_libraries,
function( $saved_library ) use ( $library ) {
+ if ( ! is_array( $saved_library ) || ! isset( $saved_library['id'] ) ) {
+ return false;
+ }
+
return $saved_library['id'] === $library->id;
}
);
@@ -126,7 +151,7 @@
$saved_data = array_values( $saved_data );
if ( isset( $saved_data[0]['isEnabled'] ) ) {
- $library->setStatus( $saved_data[0]['isEnabled'] );
+ $library->setStatus( $this->normalize_boolean_value( $saved_data[0]['isEnabled'] ) );
}
}
@@ -173,14 +198,87 @@
* @return GenerateBlocks_Library_DTO
*/
public function create( array $data ): GenerateBlocks_Library_DTO {
+ $data = wp_parse_args(
+ $data,
+ array(
+ 'id' => '',
+ 'name' => '',
+ 'domain' => '',
+ 'publicKey' => '',
+ 'isEnabled' => false,
+ 'isDefault' => false,
+ 'isLocal' => false,
+ )
+ );
+
return ( new GenerateBlocks_Library_DTO() )
- ->set( 'id', $data['id'] )
- ->set( 'name', $data['name'] )
- ->set( 'domain', $data['domain'] )
- ->set( 'public_key', $data['publicKey'] )
- ->set( 'is_enabled', $data['isEnabled'] )
- ->set( 'is_default', $data['isDefault'] )
- ->set( 'is_local', $data['isLocal'] );
+ ->set( 'id', $this->normalize_string_value( $data['id'] ) )
+ ->set( 'name', $this->normalize_string_value( $data['name'] ) )
+ ->set( 'domain', $this->normalize_string_value( $data['domain'] ) )
+ ->set( 'public_key', $this->normalize_string_value( $data['publicKey'] ) )
+ ->set( 'is_enabled', $this->normalize_boolean_value( $data['isEnabled'] ) )
+ ->set( 'is_default', $this->normalize_boolean_value( $data['isDefault'] ) )
+ ->set( 'is_local', $this->normalize_boolean_value( $data['isLocal'] ) );
+ }
+
+ /**
+ * Normalize values that are expected to be strings.
+ *
+ * @param mixed $value The value to normalize.
+ * @return string
+ */
+ private function normalize_string_value( $value ): string {
+ if ( ! is_scalar( $value ) ) {
+ return '';
+ }
+
+ return (string) $value;
+ }
+
+ /**
+ * Normalize values that are expected to be booleans.
+ *
+ * @param mixed $value The value to normalize.
+ * @return bool
+ */
+ private function normalize_boolean_value( $value ): bool {
+ if ( ! is_scalar( $value ) ) {
+ return false;
+ }
+
+ return rest_sanitize_boolean( $value );
+ }
+
+ /**
+ * Check that a library has values safe to access through the DTO.
+ *
+ * @param mixed $library The library object to validate.
+ * @return bool
+ */
+ private function is_valid_library( $library ): bool {
+ if ( ! $library instanceof GenerateBlocks_Library_DTO ) {
+ return false;
+ }
+
+ $data = $library->serialize();
+
+ if ( empty( $data['id'] ) || ! is_scalar( $data['id'] ) ) {
+ return false;
+ }
+
+ foreach ( array( 'name', 'domain', 'publicKey' ) as $key ) {
+ if ( isset( $data[ $key ] ) && ! is_scalar( $data[ $key ] ) ) {
+ return false;
+ }
+ }
+
+ foreach ( array( 'isEnabled', 'isDefault', 'isLocal' ) as $key ) {
+ if ( isset( $data[ $key ] ) && ! is_scalar( $data[ $key ] ) ) {
+ return false;
+ }
+ }
+
+ return true;
}
/**
@@ -279,6 +377,10 @@
$cached_data = array_filter(
$cached_data,
function( $data ) use ( $query_args ) {
+ if ( ! isset( $data['categories'] ) || ! is_array( $data['categories'] ) ) {
+ return false;
+ }
+
return in_array( $query_args['categoryId'], $data['categories'] );
}
);
@@ -288,6 +390,10 @@
$cached_data = array_filter(
$cached_data,
function( $data ) use ( $query_args ) {
+ if ( ! is_array( $data ) ) {
+ return false;
+ }
+
foreach ( $data as $key => $value ) {
if ( is_string( $value ) && stripos( $value, $query_args['search'] ) !== false ) {
return true;
@@ -324,9 +430,15 @@
return;
}
+ if ( ! is_array( $data ) ) {
+ return;
+ }
+
$expiration = self::get_cache_expiry();
if ( 'patterns' === $collection ) {
+ self::delete_cached_data( $cache_key, $collection );
+
if ( ! empty( $data ) ) {
$chunks = array_chunk( $data, 20, true );
@@ -343,6 +455,30 @@
}
/**
+ * Delete cached library data by collection.
+ *
+ * @param string $cache_key The key to delete.
+ * @param string $collection The collection to delete.
+ */
+ public static function delete_cached_data( $cache_key = '', $collection = '' ) {
+ if ( ! $cache_key ) {
+ return;
+ }
+
+ if ( 'patterns' !== $collection ) {
+ delete_transient( $cache_key );
+ return;
+ }
+
+ $index = 0;
+
+ while ( false !== get_transient( $cache_key . '_' . $index ) ) {
+ delete_transient( $cache_key . '_' . $index );
+ $index++;
+ }
+ }
+
+ /**
* Adds GB custom query variables.
*
* @param array $vars The variables.
@@ -385,6 +521,10 @@
* @return bool
*/
public function hide_admin_bar( $show_admin_bar ) {
+ if ( ! isset( $GLOBALS['wp_query'] ) ) {
+ return $show_admin_bar;
+ }
+
if ( false !== get_query_var( 'gb-template-viewer', false ) ) {
$show_admin_bar = false;
}
--- a/generateblocks/includes/pattern-library/class-pattern-library-rest.php
+++ b/generateblocks/includes/pattern-library/class-pattern-library-rest.php
@@ -163,21 +163,18 @@
*/
public function save_libraries( WP_REST_Request $request ): WP_REST_Response {
$data = $request->get_param( 'data' );
- $libraries = array_map(
- function( $library ) {
- if ( ! $library['isLocal'] && ! $library['isDefault'] ) {
- // Save all data if this is a remote library.
- return $library;
- }
-
- // Only save the ID and status for local and default libraries.
- // The rest of the data will be supplied via the PHP filter.
- return [
- 'id' => $library['id'],
- 'isEnabled' => $library['isEnabled'],
- ];
- },
- $data
+
+ if ( ! is_array( $data ) ) {
+ return $this->error( 400, __( 'Invalid library data.', 'generateblocks' ) );
+ }
+
+ $libraries = array_values(
+ array_filter(
+ array_map(
+ [ $this, 'sanitize_library_data' ],
+ $data
+ )
+ )
);
update_option( 'generateblocks_pattern_libraries', $libraries );
@@ -192,7 +189,7 @@
* @return WP_REST_Response
*/
public function list_categories( WP_REST_Request $request ): WP_REST_Response {
- $library_id = $request->get_param( 'libraryId' );
+ $library_id = $this->sanitize_request_string( $request->get_param( 'libraryId' ) );
if ( ! $library_id ) {
return $this->error( 404, "Library of id "$library_id" was not found." );
@@ -216,9 +213,9 @@
* @return WP_REST_Response
*/
public function list_patterns( WP_REST_Request $request ): WP_REST_Response {
- $library_id = $request->get_param( 'libraryId' );
- $search = $request->get_param( 'search' );
- $category_id = $request->get_param( 'categoryId' );
+ $library_id = $this->sanitize_request_string( $request->get_param( 'libraryId' ) );
+ $search = $this->sanitize_request_string( $request->get_param( 'search' ) );
+ $category_id = $this->sanitize_request_string( $request->get_param( 'categoryId' ) );
if ( ! $library_id ) {
return $this->error( 404, "Library of id "$library_id" was not found." );
@@ -255,7 +252,17 @@
string $collection,
array $query_args = array()
): WP_REST_Response {
- $endpoint = "$library->domain/wp-json/generateblocks-pro/v1/pattern-library/$collection";
+ if ( ! in_array( $collection, array( 'categories', 'patterns' ), true ) ) {
+ return $this->error( 400, __( 'Invalid library collection.', 'generateblocks' ) );
+ }
+
+ $domain = esc_url_raw( $library->domain );
+
+ if ( ! $domain ) {
+ return $this->error( 400, __( 'Invalid library domain.', 'generateblocks' ) );
+ }
+
+ $endpoint = trailingslashit( $domain ) . "wp-json/generateblocks-pro/v1/pattern-library/$collection";
$url = add_query_arg( $query_args, $endpoint );
$cache_key = $library->id . '-' . $collection;
$cache = GenerateBlocks_Libraries::get_cached_data( $cache_key, $query_args, $collection );
@@ -270,6 +277,7 @@
'headers' => array(
'X-GB-Public-Key' => $library->public_key,
),
+ 'timeout' => 15,
)
);
@@ -277,10 +285,25 @@
return $this->error( 500, "Unable to request from $endpoint" );
}
+ $response_code = (int) wp_remote_retrieve_response_code( $request );
+
+ if ( 200 !== $response_code ) {
+ return $this->error( $response_code ? $response_code : 500, "Unable to request from $endpoint" );
+ }
+
$body = wp_remote_retrieve_body( $request );
$body = json_decode( $body, true );
+
+ if ( ! is_array( $body ) ) {
+ return $this->error( 500, __( 'Invalid library response.', 'generateblocks' ) );
+ }
+
$data = $body['response']['data'] ?? [];
+ if ( ! is_array( $data ) ) {
+ return $this->error( 500, __( 'Invalid library response data.', 'generateblocks' ) );
+ }
+
// Cache our data.
GenerateBlocks_Libraries::set_cached_data( $data, $cache_key, $collection );
@@ -295,7 +318,16 @@
* @return WP_REST_Response
*/
public function get_cache_data( WP_REST_Request $request ): WP_REST_Response {
- $id = $request->get_param( 'id' );
+ $id = $this->sanitize_request_string( $request->get_param( 'id' ) );
+
+ if ( ! $id ) {
+ return $this->error( 400, __( 'Invalid library id.', 'generateblocks' ) );
+ }
+
+ if ( is_null( GenerateBlocks_Libraries::get_instance()->get_one( $id ) ) ) {
+ return $this->error( 404, __( 'Library not found.', 'generateblocks' ) );
+ }
+
$expiration_time = get_option( '_transient_timeout_' . $id . '-patterns_0' );
if ( ! $expiration_time ) {
@@ -323,28 +355,100 @@
* @return WP_REST_Response
*/
public function clear_cache( WP_REST_Request $request ): WP_REST_Response {
- $id = $request->get_param( 'id' );
+ $id = $this->sanitize_request_string( $request->get_param( 'id' ) );
- global $wpdb;
- $id = sanitize_text_field( $id );
- $prefix = $wpdb->esc_like( '_transient_' . $id );
-
- $transient_ids = $wpdb->get_col(
- $wpdb->prepare(
- "SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s",
- $prefix . '%'
- )
- );
+ if ( ! $id ) {
+ return $this->error( 400, __( 'Invalid library id.', 'generateblocks' ) );
+ }
- foreach ( $transient_ids as $transient_id ) {
- $transient = str_replace( '_transient_', '', $transient_id );
- delete_transient( $transient );
+ if ( is_null( GenerateBlocks_Libraries::get_instance()->get_one( $id ) ) ) {
+ return $this->error( 404, __( 'Library not found.', 'generateblocks' ) );
}
+ GenerateBlocks_Libraries::delete_cached_data( $id . '-categories' );
+ GenerateBlocks_Libraries::delete_cached_data( $id . '-patterns', 'patterns' );
+ GenerateBlocks_Libraries::delete_cached_data( $id . '_global-style-data' );
+
return $this->success( [] );
}
/**
+ * Sanitize scalar REST request values.
+ *
+ * @param mixed $value The value to sanitize.
+ * @return string
+ */
+ private function sanitize_request_string( $value ): string {
+ if ( ! is_scalar( $value ) ) {
+ return '';
+ }
+
+ return sanitize_text_field( (string) $value );
+ }
+
+ /**
+ * Sanitize scalar REST request URLs.
+ *
+ * @param mixed $value The value to sanitize.
+ * @return string
+ */
+ private function sanitize_request_url( $value ): string {
+ if ( ! is_string( $value ) ) {
+ return '';
+ }
+
+ return esc_url_raw( $value, array( 'http', 'https' ) );
+ }
+
+ /**
+ * Sanitize a saved library row.
+ *
+ * @param mixed $library The library data.
+ * @return array
+ */
+ private function sanitize_library_data( $library ): array {
+ if ( ! is_array( $library ) ) {
+ return [];
+ }
+
+ $id = $this->sanitize_request_string( $library['id'] ?? '' );
+
+ if ( ! $id ) {
+ return [];
+ }
+
+ $is_local = rest_sanitize_boolean( $library['isLocal'] ?? false );
+ $is_default = rest_sanitize_boolean( $library['isDefault'] ?? false );
+ $is_enabled = rest_sanitize_boolean( $library['isEnabled'] ?? false );
+
+ if ( ! $is_local && ! $is_default ) {
+ $domain = $this->sanitize_request_url( $library['domain'] ?? '' );
+
+ if ( ! $domain ) {
+ return [];
+ }
+
+ // Save all data if this is a remote library.
+ return [
+ 'id' => $id,
+ 'name' => $this->sanitize_request_string( $library['name'] ?? '' ),
+ 'domain' => $domain,
+ 'publicKey' => $this->sanitize_request_string( $library['publicKey'] ?? '' ),
+ 'isEnabled' => $is_enabled,
+ 'isDefault' => $is_default,
+ 'isLocal' => $is_local,
+ ];
+ }
+
+ // Only save the ID and status for local and default libraries.
+ // The rest of the data will be supplied via the PHP filter.
+ return [
+ 'id' => $id,
+ 'isEnabled' => $is_enabled,
+ ];
+ }
+
+ /**
* Returns a success response.
*
* @param array $data The data.
--- a/generateblocks/plugin.php
+++ b/generateblocks/plugin.php
@@ -5,7 +5,7 @@
* Description: A small collection of lightweight WordPress blocks that can accomplish nearly anything.
* Author: Tom Usborne
* Author URI: https://tomusborne.com
- * Version: 2.2.1
+ * Version: 2.3.0
* Requires at least: 6.5
* Requires PHP: 7.2
* License: GPL2+
@@ -19,7 +19,7 @@
exit; // Exit if accessed directly.
}
-define( 'GENERATEBLOCKS_VERSION', '2.2.1' );
+define( 'GENERATEBLOCKS_VERSION', '2.3.0' );
define( 'GENERATEBLOCKS_DIR', plugin_dir_path( __FILE__ ) );
define( 'GENERATEBLOCKS_DIR_URL', plugin_dir_url( __FILE__ ) );