Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/kadence-blocks/includes/blocks/class-kadence-blocks-abstract-block.php
+++ b/kadence-blocks/includes/blocks/class-kadence-blocks-abstract-block.php
@@ -197,11 +197,16 @@
/**
* Render Block CSS in Page Head.
*
+ * @since 3.7.8.1 Normalize the unique id before use.
+ *
* @param array $block the block data.
*/
public function output_head_data( $block ) {
if ( isset( $block['attrs'] ) && is_array( $block['attrs'] ) ) {
$attributes = $block['attrs'];
+ if ( ! empty( $attributes['uniqueID'] ) ) {
+ $attributes['uniqueID'] = $this->sanitize_unique_id( $attributes['uniqueID'] );
+ }
if ( in_array( $this->block_name, $this->is_cpt_block ) ) {
$unique_id = ! empty( $attributes['id'] ) ? strval( $attributes['id'] ) . '-cpt-id' : '';
if ( empty( $unique_id ) ) {
@@ -211,7 +216,7 @@
$unique_id = ! empty( $attributes['uniqueID'] ) ? $attributes['uniqueID'] : '';
}
if ( ! empty( $unique_id ) ) {
- $unique_id = str_replace( '/', '-', $unique_id );
+ $unique_id = $this->sanitize_unique_id( $unique_id );
if ( in_array( $this->block_name, $this->supports_merged_defaults ) ) {
$attributes = $this->get_attributes_with_defaults( $unique_id, $attributes );
}
@@ -251,12 +256,17 @@
/**
* Render Block CSS
*
+ * @since 3.7.8.1 Normalize the unique id before use.
+ *
* @param array $attributes the blocks attribtues.
* @param string $content the blocks content.
* @param WP_Block $block_instance The instance of the WP_Block class that represents the block being rendered.
*/
public function render_css( $attributes, $content, $block_instance ) {
$this->render_scripts( $attributes, true );
+ if ( ! empty( $attributes['uniqueID'] ) ) {
+ $attributes['uniqueID'] = $this->sanitize_unique_id( $attributes['uniqueID'] );
+ }
if ( in_array( $this->block_name, $this->is_cpt_block ) ) {
$unique_id = ! empty( $attributes['id'] ) ? strval( $attributes['id'] ) . '-cpt-id' : '';
if ( empty( $unique_id ) ) {
@@ -266,7 +276,7 @@
$unique_id = ! empty( $attributes['uniqueID'] ) ? $attributes['uniqueID'] : '';
}
if ( ! empty( $unique_id ) ) {
- $unique_id = str_replace( '/', '-', $unique_id );
+ $unique_id = $this->sanitize_unique_id( $unique_id );
$unique_style_id = apply_filters( 'kadence_blocks_build_render_unique_id', $unique_id, $this->block_name, $attributes );
$css_class = Kadence_Blocks_CSS::get_instance();
@@ -548,6 +558,19 @@
}
/**
+ * Normalize a blocks unique id so it is safe to use in markup and style rules.
+ *
+ * @since 3.7.8.1
+ *
+ * @param string $unique_id The blocks unique id.
+ *
+ * @return string
+ */
+ protected function sanitize_unique_id( $unique_id ) {
+ return preg_replace( '/[^A-Za-z0-9_-]/', '', str_replace( '/', '-', (string) $unique_id ) );
+ }
+
+ /**
* Build escaped HTML attributes to be placed in an HTML tag.
*
* @param array<string, string|int|float|bool> $attributes The html attributes to render to a tag.
--- a/kadence-blocks/includes/blocks/class-kadence-blocks-advancedgallery-block.php
+++ b/kadence-blocks/includes/blocks/class-kadence-blocks-advancedgallery-block.php
@@ -1132,7 +1132,7 @@
$output .= '<div class="kadence-blocks-gallery-item-inner">';
$figure_style = '';
if ( ! empty( $padding_bottom ) && 'below' === $caption_style ) {
- $figure_style = ' style="max-width:' . $image['width'] . 'px;"';
+ $figure_style = ' style="max-width:' . esc_attr( $image['width'] ) . 'px;"';
}
$output .= '<figure class="' . esc_attr( implode( ' ', $fig_classes ) ) . '"' . $figure_style . '>';
if ( ! empty( $href ) ) {
--- a/kadence-blocks/includes/blocks/class-kadence-blocks-googlemaps-block.php
+++ b/kadence-blocks/includes/blocks/class-kadence-blocks-googlemaps-block.php
@@ -235,8 +235,8 @@
}
$zoom = empty( $attributes['zoom'] ) ? 11 : esc_attr( $attributes['zoom'] );
- $gMapLat = empty( $attributes['lat'] ) ? '37.8201' : esc_attr($attributes['lat'] );
- $gMapLng = empty( $attributes['lng'] ) ? '-122.4781' : esc_attr( $attributes['lng'] );
+ $gMapLat = ! empty( $attributes['lat'] ) && is_numeric( $attributes['lat'] ) ? $attributes['lat'] : '37.8201';
+ $gMapLng = ! empty( $attributes['lng'] ) && is_numeric( $attributes['lng'] ) ? $attributes['lng'] : '-122.4781';
$content .= '<script>';
$content .= 'function kb_google_map' . $this->escape_for_js_variable( $unique_id ) . '() {';
--- a/kadence-blocks/includes/blocks/class-kadence-blocks-identity-block.php
+++ b/kadence-blocks/includes/blocks/class-kadence-blocks-identity-block.php
@@ -102,7 +102,7 @@
*/
public function build_html( $attributes, $unique_id, $content, $block_instance ) {
$layout = isset( $attributes['layout'] ) ? $attributes['layout'] : 'logo-title';
- $layout_class = 'kb-identity-layout-container kb-identity-layout-' . $layout;
+ $layout_class = esc_attr( 'kb-identity-layout-container kb-identity-layout-' . $layout );
$content = $this->strip_anchor_tags( $content );
if (!empty($attributes['urlTransparent'])) {
--- a/kadence-blocks/includes/blocks/class-kadence-blocks-progress-bar-block.php
+++ b/kadence-blocks/includes/blocks/class-kadence-blocks-progress-bar-block.php
@@ -66,6 +66,8 @@
/**
* Builds CSS for block.
*
+ * @since 3.7.8.1 Escape the mask url output.
+ *
* @param array $attributes the blocks attributes.
* @param Kadence_Blocks_CSS $css the css class for blocks.
* @param string $unique_id the blocks attr ID.
@@ -175,7 +177,7 @@
}
}
- $mask_image_string = trim( str_repeat( 'url(' . $mask_url . '),', $iterations ), ',' );
+ $mask_image_string = trim( str_repeat( 'url(' . esc_url_raw( $mask_url ) . '),', $iterations ), ',' );
$mask_repeat_string = trim( str_repeat( 'no-repeat,', $iterations ), ',' );
$mask_position_array = $iterations > 1 ? range( 0, 100, 100 / ( $iterations - 1 ) ) : [ 0 ];
$mask_position_string = trim( implode( '%,', $mask_position_array ) . '%', ',' );
@@ -234,6 +236,8 @@
}
/**
* Builds HTML for block.
+ *
+ * @since 3.7.8.1 Escape the container class output.
*/
public function build_html( $attributes, $unique_id, $content, $block_instance ) {
@@ -257,7 +261,7 @@
! empty( $attributes['progressWidthMobile'] ) ? $attributes['progressWidthMobile'] : ( ! empty( $attributes['progressWidthTablet'] ) ? $attributes['progressWidthTablet'] : ( ! empty( $attributes['progressWidth'] ) ? $attributes['progressWidth'] : 2 ) ),
];
- $content = '<div class="kb-progress-bar-container kb-progress-bar-container' . $unique_id . ' kb-progress-bar-init kb-progress-bar-type-' . $attributes['barType'] . ' ' . ( ! empty( $attributes['align'] ) ? 'align' . $attributes['align'] : '' ) . '">';
+ $content = '<div class="' . esc_attr( 'kb-progress-bar-container kb-progress-bar-container' . $unique_id . ' kb-progress-bar-init kb-progress-bar-type-' . $attributes['barType'] . ' ' . ( ! empty( $attributes['align'] ) ? 'align' . $attributes['align'] : '' ) ) . '">';
$content .= $this->get_label( $attributes, 'above' );
@@ -400,6 +404,8 @@
/**
* Get HTML for displaying the percent complete.
*
+ * @since 3.7.8.1 Escape the rendered output.
+ *
* @param $attributes
*
* @return string
@@ -419,7 +425,7 @@
$position = $attributes['labelPosition'] ?? 'above';
- return '<span id="current-progress-' . $position . $attributes['uniqueID'] . '" class="kb-current-progress-' . $position . ' kt-progress-percent">' . $prefix . $starting . $suffix . '</span>';
+ return '<span id="' . esc_attr( 'current-progress-' . $position . $attributes['uniqueID'] ) . '" class="' . esc_attr( 'kb-current-progress-' . $position ) . ' kt-progress-percent">' . esc_html( $prefix . $starting . $suffix ) . '</span>';
}
/**
--- a/kadence-blocks/includes/blocks/class-kadence-blocks-row-layout-block.php
+++ b/kadence-blocks/includes/blocks/class-kadence-blocks-row-layout-block.php
@@ -1670,7 +1670,7 @@
if ( 'local' == $background_video_type ) {
$output = sprintf( '<div class="kb-blocks-bg-video-container"><video %1$s></video>%2$s</div>', implode( ' ', $video_html_attributes ), $btns_output );
} else {
- $output = sprintf( '<div class="kb-blocks-bg-video-container embedded"><div class="kb-bg-video-iframe kb-bg-video-ratio-%1$s"><iframe frameborder="0" %2$s></iframe></div></div>', $ratio, implode( ' ', $video_html_attributes ) );
+ $output = sprintf( '<div class="kb-blocks-bg-video-container embedded"><div class="kb-bg-video-iframe kb-bg-video-ratio-%1$s"><iframe frameborder="0" %2$s></iframe></div></div>', esc_attr( $ratio ), implode( ' ', $video_html_attributes ) );
}
return $output;
}
--- a/kadence-blocks/includes/blocks/class-kadence-blocks-testimonial-block.php
+++ b/kadence-blocks/includes/blocks/class-kadence-blocks-testimonial-block.php
@@ -199,7 +199,7 @@
$svg = Kadence_Blocks_Svg_Render::render( $iconStyles[0]['icon'], $fill, $stroke_width, $title );
- $icon .= "<div class='kt-svg-testimonial-global-icon kt-svg-testimonial-global-icon-icon-" . $iconStyles[0]['icon'] . "'>";
+ $icon .= "<div class='kt-svg-testimonial-global-icon kt-svg-testimonial-global-icon-icon-" . esc_attr( $iconStyles[0]['icon'] ) . "'>";
$icon .= $svg;
$icon .= '</div>';
@@ -223,7 +223,7 @@
$media .= '<div class="kadence-testimonial-image-intrisic">';
if ( $attributes['media'] === 'icon' && $attributes['icon'] ) {
- $extras = ' height="' . esc_attr( $attributes['isize'] ) . '" width="' . esc_attr( $attributes['isize'] ) . '" style="color: ' . ( isset( $attributes['color'] ) ? $css_class->render_color( $attributes['color'] ) : 'undefined' ) . '"';
+ $extras = ' height="' . esc_attr( $attributes['isize'] ) . '" width="' . esc_attr( $attributes['isize'] ) . '" style="color: ' . ( isset( $attributes['color'] ) ? esc_attr( $css_class->render_color( $attributes['color'] ) ) : 'undefined' ) . '"';
$svg = Kadence_Blocks_Svg_Render::render( $attributes['icon'], $fill, $stroke_width, $attributes['ititle'], false, $extras );
$media .= '<div class="kt-svg-testimonial-icon kt-svg-testimonial-icon-' . esc_attr( $attributes['icon'] ) . '">';
@@ -287,7 +287,7 @@
return wp_get_attachment_image( $attributes['id'], $size, false, $args );
}
$image_url = $this->get_media_url( $attributes, $media_styles, $style, $container_max_width );
- return '<img src="' . $image_url . '" class="kt-testimonial-image"' . ( ! empty( $css_style ) ? ' style="' . $css_style . '"' : '' ) . ' />';
+ return '<img src="' . esc_url( $image_url ) . '" class="kt-testimonial-image"' . ( ! empty( $css_style ) ? ' style="' . esc_attr( $css_style ) . '"' : '' ) . ' />';
}
/**
* Get the media URL
--- a/kadence-blocks/includes/blocks/form/class-kadence-blocks-accept-block.php
+++ b/kadence-blocks/includes/blocks/form/class-kadence-blocks-accept-block.php
@@ -80,15 +80,15 @@
$check_label = $attributes;
$check_label['inputName'] = 'cb' . $class_id;
- $inner_content .= '<fieldset class="kb-radio-check-item-wrap" id="' . $this->field_name( $check_label ) . '" data-type="accept" data-required="' . $is_required . '" ' . $this->additional_fieldset_attributes( $attributes ) . '>';
+ $inner_content .= '<fieldset class="kb-radio-check-item-wrap" id="' . esc_attr( $this->field_name( $check_label ) ) . '" data-type="accept" data-required="' . esc_attr( $is_required ) . '" ' . $this->additional_fieldset_attributes( $attributes ) . '>';
$inner_content .= $this->field_legend( $check_label );
$inner_content .= $this->field_aria_label( $attributes );
$is_checked_from_param = ! empty( $this->get_default( $attributes ) );
$is_checked_from_editor = isset( $attributes['isChecked'] ) && true === $attributes['isChecked'] ? true : false;
$is_checked = $is_checked_from_editor || $is_checked_from_param;
$inner_content .= '<div class="kb-radio-check-item">';
- $inner_content .= '<input name="' . $this->field_name( $attributes ) . '" id="' . $this->field_id( $attributes ) . '_0"' . $this->aria_described_by( $attributes ) . ' data-label="' . esc_attr( $this->get_label( $attributes ) ) . '"' . $this->get_auto_complete( $attributes ) . ' type="' . $type . '" value="' . esc_attr( $this->get_accept_default( $attributes ) ) . '"' . ( $is_checked ? ' checked' : '' ) . ' data-type="accept" class="kb-field kb-accept-field kb-' . $type . '-field" data-required="' . $is_required . '" ' . $this->additional_field_attributes( $attributes ) . '/>';
- $inner_content .= '<label for="' . $this->field_id( $attributes ) . '_0">' . $attributes['description'];
+ $inner_content .= '<input name="' . esc_attr( $this->field_name( $attributes ) ) . '" id="' . esc_attr( $this->field_id( $attributes ) ) . '_0"' . $this->aria_described_by( $attributes ) . ' data-label="' . esc_attr( $this->get_label( $attributes ) ) . '"' . $this->get_auto_complete( $attributes ) . ' type="' . esc_attr( $type ) . '" value="' . esc_attr( $this->get_accept_default( $attributes ) ) . '"' . ( $is_checked ? ' checked' : '' ) . ' data-type="accept" class="kb-field kb-accept-field kb-' . esc_attr( $type ) . '-field" data-required="' . esc_attr( $is_required ) . '" ' . $this->additional_field_attributes( $attributes ) . '/>';
+ $inner_content .= '<label for="' . esc_attr( $this->field_id( $attributes ) ) . '_0">' . $attributes['description'];
if ( ! empty( $attributes['required'] ) && $attributes['required'] && ( empty( $attributes['label'] ) || ( isset( $attributes['showLabel'] ) && ! $attributes['showLabel'] ) ) ) {
$inner_content .= '<span class="' . self::REQUIRED_CLASS_NAME . '">*</span>';
}
--- a/kadence-blocks/includes/blocks/form/class-kadence-blocks-time-input-block.php
+++ b/kadence-blocks/includes/blocks/form/class-kadence-blocks-time-input-block.php
@@ -79,7 +79,7 @@
$inner_content = '';
$inner_content .= $this->field_label( $attributes );
$inner_content .= $this->field_aria_label( $attributes );
- $inner_content .= '<input name="' . $this->field_name( $attributes ) . '" id="' . $this->field_id( $attributes ) . '"' . $this->aria_described_by( $attributes ) . ' data-label="' . esc_attr( $this->get_label( $attributes ) ) . '"' . $this->get_auto_complete( $attributes ) . ' type="' . $type . '" value="' . esc_attr( $this->get_default( $attributes ) ) . '" data-type="' . $type . '" class="kb-field kb-' . $type . '-field" data-required="' . $is_required . '" ' . $this->additional_field_attributes( $attributes ) . '/>';
+ $inner_content .= '<input name="' . esc_attr( $this->field_name( $attributes ) ) . '" id="' . esc_attr( $this->field_id( $attributes ) ) . '"' . $this->aria_described_by( $attributes ) . ' data-label="' . esc_attr( $this->get_label( $attributes ) ) . '"' . $this->get_auto_complete( $attributes ) . ' type="' . esc_attr( $type ) . '" value="' . esc_attr( $this->get_default( $attributes ) ) . '" data-type="' . esc_attr( $type ) . '" class="kb-field kb-' . esc_attr( $type ) . '-field" data-required="' . esc_attr( $is_required ) . '" ' . $this->additional_field_attributes( $attributes ) . '/>';
$inner_content .= $this->field_help_text( $attributes );
--- a/kadence-blocks/includes/class-kadence-blocks-prebuilt-library-rest-api.php
+++ b/kadence-blocks/includes/class-kadence-blocks-prebuilt-library-rest-api.php
@@ -1388,6 +1388,8 @@
/**
* Retrieves a collection of objects.
*
+ * @since 3.7.8.1 Restrict requests to known library locations.
+ *
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
@@ -1404,10 +1406,14 @@
if ( ! empty( $library_url ) ) {
if ( 'page' === $pattern_type ) {
- $library_url = rtrim( $library_url, '/' ) . '/wp-json/kadence-cloud/v1/page/';
- $extra = 'page-item';
+ $extra = 'page-item';
+ $endpoint = '/wp-json/kadence-cloud/v1/page/';
} else {
- $library_url = rtrim( $library_url, '/' ) . '/wp-json/kadence-cloud/v1/single/';
+ $endpoint = '/wp-json/kadence-cloud/v1/single/';
+ }
+ $library_url = $this->resolve_library_url( $library_url, $endpoint );
+ if ( empty( $library_url ) ) {
+ return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Unknown Library', 'kadence-blocks' ), [ 'status' => 400 ] ) );
}
} else {
$library_url = $this->get_patterns_single_url();
@@ -1430,7 +1436,8 @@
if ( 'templates' !== $library && 'pages' !== $library && 'template' !== $library ) {
$args['data'] = 'true';
}
- if ( 'templates' === $library || 'section' === $library || 'pages' === $library || 'template' === $library ) {
+ // License credentials are only ever sent to the Kadence library hosts.
+ if ( $this->is_kadence_api_url( $library_url ) && ( 'templates' === $library || 'section' === $library || 'pages' === $library || 'template' === $library ) ) {
$args['api_key'] = $this->api_key;
if ( ! empty( $this->api_email ) ) {
// Send in case we need to verify with old api.
@@ -1483,6 +1490,8 @@
/**
* Retrieves a collection of objects.
*
+ * @since 3.7.8.1 Restrict requests to known library locations.
+ *
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response Response object on success, or WP_Error object on failure.
*/
@@ -1493,7 +1502,10 @@
if ( empty( $library_url ) || empty( $key ) ) {
return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Incorrect Access Key', 'kadence-blocks' ), [ 'status' => 401 ] ) );
}
- $url = empty( $library_url ) ? '' : rtrim( sanitize_text_field( $library_url ), '/' ) . '/wp-json/kadence-cloud/v1/info/';
+ $url = $this->resolve_connection_url( sanitize_text_field( $library_url ), '/wp-json/kadence-cloud/v1/info/' );
+ if ( empty( $url ) ) {
+ return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Unknown Library', 'kadence-blocks' ), [ 'status' => 400 ] ) );
+ }
// Do you have the data?
$site_url = get_original_domain();
$args = [
@@ -1539,6 +1551,8 @@
/**
* Retrieves a collection of objects.
*
+ * @since 3.7.8.1 Restrict requests to known library locations.
+ *
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response Response object on success, or WP_Error object on failure.
*/
@@ -1551,10 +1565,10 @@
$meta = $request->get_param( self::PROP_META );
if ( ! empty( $library_url ) ) {
- if ( ! empty( $meta ) && 'pages' === $meta ) {
- $library_url = rtrim( $library_url, '/' ) . '/wp-json/kadence-cloud/v1/page-categories/';
- } else {
- $library_url = rtrim( $library_url, '/' ) . '/wp-json/kadence-cloud/v1/categories/';
+ $endpoint = ! empty( $meta ) && 'pages' === $meta ? '/wp-json/kadence-cloud/v1/page-categories/' : '/wp-json/kadence-cloud/v1/categories/';
+ $library_url = $this->resolve_library_url( $library_url, $endpoint );
+ if ( empty( $library_url ) ) {
+ return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Unknown Library', 'kadence-blocks' ), [ 'status' => 400 ] ) );
}
} elseif ( ! empty( $library ) && 'pages' === $library ) {
$library_url = $this->remote_pages_cat_url;
@@ -1648,6 +1662,8 @@
/**
* Retrieves a collection of objects.
*
+ * @since 3.7.8.1 Restrict requests to known library locations.
+ *
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response Response object on success, or WP_Error object on failure.
*/
@@ -1660,10 +1676,10 @@
$meta = $request->get_param( self::PROP_META );
if ( ! empty( $library_url ) ) {
- if ( ! empty( $meta ) && 'pages' === $meta ) {
- $library_url = rtrim( $library_url, '/' ) . '/wp-json/kadence-cloud/v1/pages/';
- } else {
- $library_url = rtrim( $library_url, '/' ) . '/wp-json/kadence-cloud/v1/get/';
+ $endpoint = ! empty( $meta ) && 'pages' === $meta ? '/wp-json/kadence-cloud/v1/pages/' : '/wp-json/kadence-cloud/v1/get/';
+ $library_url = $this->resolve_library_url( $library_url, $endpoint );
+ if ( empty( $library_url ) ) {
+ return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Unknown Library', 'kadence-blocks' ), [ 'status' => 400 ] ) );
}
} elseif ( ! empty( $library ) && 'pages' === $library ) {
$library_url = $this->remote_pages_url;
@@ -2349,6 +2365,8 @@
/**
* Get remote file contents.
*
+ * @since 3.7.8.1 Only attach credentials for known library locations.
+ *
* @access public
* @return string Returns the remote URL contents.
*/
@@ -2358,7 +2376,8 @@
'key' => $key,
'site' => $site_url,
];
- if ( 'templates' === $library || 'section' === $library || 'pages' === $library || 'template' === $library ) {
+ // License credentials are only ever sent to the Kadence library hosts.
+ if ( $this->is_kadence_api_url( $library_url ) && ( 'templates' === $library || 'section' === $library || 'pages' === $library || 'template' === $library ) ) {
$args['api_email'] = $this->api_email;
$args['api_key'] = $this->api_key;
$args['product_id'] = $this->product_id;
@@ -2405,6 +2424,8 @@
/**
* Get remote file contents.
*
+ * @since 3.7.8.1 Only attach credentials for known library locations.
+ *
* @access public
* @return string Returns the remote URL contents.
*/
@@ -2414,7 +2435,8 @@
'key' => $key,
'site' => $site_url,
];
- if ( 'templates' === $library || 'section' === $library || 'pages' === $library || 'template' === $library ) {
+ // License credentials are only ever sent to the Kadence library hosts.
+ if ( $this->is_kadence_api_url( $library_url ) && ( 'templates' === $library || 'section' === $library || 'pages' === $library || 'template' === $library ) ) {
$args['api_email'] = $this->api_email;
$args['api_key'] = $this->api_key;
$args['product_id'] = $this->product_id;
--- a/kadence-blocks/includes/class-kadence-blocks-prebuilt-library.php
+++ b/kadence-blocks/includes/class-kadence-blocks-prebuilt-library.php
@@ -431,6 +431,8 @@
/**
* Get remote file contents.
*
+ * @since 3.7.8.1 Only attach credentials for known library locations.
+ *
* @access public
* @return string Returns the remote URL contents.
*/
@@ -440,7 +442,8 @@
'key' => $this->key,
'site' => $site_url,
];
- if ( 'templates' === $this->package || 'section' === $this->package || 'pages' === $this->package || $this->is_template ) {
+ // License credentials are only ever sent to the Kadence library hosts.
+ if ( $this->is_kadence_api_url( $this->url ) && ( 'templates' === $this->package || 'section' === $this->package || 'pages' === $this->package || $this->is_template ) ) {
if ( ! empty( $this->api_email ) ) {
$args['api_email'] = $this->api_email;
}
@@ -540,6 +543,8 @@
* 2). query api for data if needed
* 3). import content
* 4). execute 'after content import' actions (before widget import WP action, widget import, customizer import, after import WP action)
+ *
+ * @since 3.7.8.1 Restrict requests to known library locations.
*/
public function prebuilt_connection_info_ajax_callback() {
// Verify if the AJAX call is valid (checks nonce and current_user_can).
@@ -548,8 +553,11 @@
$this->api_key = $this->get_stored_license_key();
$this->api_email = $this->get_stored_license_email();
$this->package = empty( $_POST['package'] ) ? 'section' : sanitize_text_field( $_POST['package'] );
- $this->url = empty( $_POST['url'] ) ? '' : rtrim( sanitize_text_field( $_POST['url'] ), '/' ) . '/wp-json/kadence-cloud/v1/info/';
+ $this->url = $this->resolve_connection_url( empty( $_POST['url'] ) ? '' : sanitize_text_field( $_POST['url'] ), '/wp-json/kadence-cloud/v1/info/' );
$this->key = empty( $_POST['key'] ) ? 'section' : sanitize_text_field( $_POST['key'] );
+ if ( empty( $this->url ) ) {
+ wp_send_json( esc_html__( 'No Connection data', 'kadence-blocks' ) );
+ }
// Do you have the data?
$get_data = $this->get_connection_data();
if ( ! $get_data ) {
@@ -618,6 +626,8 @@
* 2). query api for data if needed
* 3). import content
* 4). execute 'after content import' actions (before widget import WP action, widget import, customizer import, after import WP action)
+ *
+ * @since 3.7.8.1 Restrict requests to known library locations.
*/
public function prebuilt_data_ajax_callback() {
// Verify if the AJAX call is valid (checks nonce and current_user_can).
@@ -628,9 +638,12 @@
$this->product_id = empty( $_POST['product_id'] ) ? '' : sanitize_text_field( $_POST['product_id'] );
$this->product_slug = empty( $_POST['product_slug'] ) ? '' : sanitize_text_field( $_POST['product_slug'] );
$this->package = empty( $_POST['package'] ) ? 'section' : sanitize_text_field( $_POST['package'] );
- $this->url = empty( $_POST['url'] ) ? $this->remote_url : rtrim( sanitize_text_field( $_POST['url'] ), '/' ) . '/wp-json/kadence-cloud/v1/get/';
+ $this->url = $this->resolve_library_url( empty( $_POST['url'] ) ? '' : sanitize_text_field( $_POST['url'] ), '/wp-json/kadence-cloud/v1/get/', $this->remote_url );
$this->key = isset( $_POST['key'] ) && ! empty( $_POST['key'] ) ? sanitize_text_field( $_POST['key'] ) : 'section';
$this->is_template = isset( $_POST['is_template'] ) && ! empty( $_POST['is_template'] ) ? true : false;
+ if ( empty( $this->url ) ) {
+ wp_send_json( esc_html__( 'No library data', 'kadence-blocks' ) );
+ }
// Do you have the data?
$get_data = $this->get_template_data();
if ( ! $get_data ) {
@@ -921,6 +934,7 @@
* Ajax function for processing the import data.
*
* @since 3.7.8 Require the upload_files capability.
+ * @since 3.7.8.1 Restrict requests to known library locations.
*/
public function process_data_ajax_callback() {
// Verify if the AJAX call is valid (checks nonce and current_user_can).
@@ -937,8 +951,11 @@
$import_style = empty( $_POST['import_style'] ) ? 'normal' : sanitize_text_field( $_POST['import_style'] );
$this->api_key = $this->get_stored_license_key();
$this->package = empty( $_POST['package'] ) ? 'section' : sanitize_text_field( $_POST['package'] );
- $this->url = empty( $_POST['url'] ) ? $this->remote_url : rtrim( sanitize_text_field( $_POST['url'] ), '/' ) . '/wp-json/kadence-cloud/v1/get/';
+ $this->url = $this->resolve_library_url( empty( $_POST['url'] ) ? '' : sanitize_text_field( $_POST['url'] ), '/wp-json/kadence-cloud/v1/get/', $this->remote_url );
$this->key = isset( $_POST['key'] ) && ! empty( $_POST['key'] ) ? sanitize_text_field( $_POST['key'] ) : 'section';
+ if ( empty( $this->url ) ) {
+ wp_send_json( esc_html__( 'No data', 'kadence-blocks' ) );
+ }
$data = $this->process_content( $data, $import_library, $import_type, $import_id, $import_style );
if ( ! $data ) {
// Send JSON Error response to the AJAX call.
@@ -1252,6 +1269,8 @@
* 2). query api for data if needed
* 3). import content
* 4). execute 'after content import' actions (before widget import WP action, widget import, customizer import, after import WP action)
+ *
+ * @since 3.7.8.1 Restrict requests to known library locations.
*/
public function prebuilt_data_reload_ajax_callback() {
@@ -1263,9 +1282,12 @@
$this->product_id = empty( $_POST['product_id'] ) ? '' : sanitize_text_field( $_POST['product_id'] );
$this->product_slug = empty( $_POST['product_slug'] ) ? '' : sanitize_text_field( $_POST['product_slug'] );
$this->package = empty( $_POST['package'] ) ? 'section' : sanitize_text_field( $_POST['package'] );
- $this->url = empty( $_POST['url'] ) ? $this->remote_url : rtrim( sanitize_text_field( $_POST['url'] ), '/' ) . '/wp-json/kadence-cloud/v1/get/';
+ $this->url = $this->resolve_library_url( empty( $_POST['url'] ) ? '' : sanitize_text_field( $_POST['url'] ), '/wp-json/kadence-cloud/v1/get/', $this->remote_url );
$this->key = empty( $_POST['key'] ) ? 'section' : sanitize_text_field( $_POST['key'] );
$this->is_template = isset( $_POST['is_template'] ) && ! empty( $_POST['is_template'] ) ? true : false;
+ if ( empty( $this->url ) ) {
+ wp_send_json( esc_html__( 'No library data', 'kadence-blocks' ) );
+ }
// $removed = $this->delete_block_library_folder();
// if ( ! $removed ) {
--- a/kadence-blocks/includes/class-kadence-blocks-table-of-contents.php
+++ b/kadence-blocks/includes/class-kadence-blocks-table-of-contents.php
@@ -933,7 +933,7 @@
$output = '<nav class="' . esc_attr( $class ) . ( $enable_toggle ? ' kb-collapsible-toc kb-toc-toggle-' . ( $start_closed ? 'hidden' : 'active' ) : '' ) . '" role="navigation" aria-label="' . esc_attr__( 'Table of Contents', 'kadence-blocks' ) . '"' . ( $enable_scroll ? ' data-scroll-offset="' . esc_attr( $scroll_offset ) . '"' : '' ) . ( isset( $attributes['enableScrollSpy'] ) && true === $attributes['enableScrollSpy'] ? ' data-scroll-spy="true"' : '' ) . '>';
$output .= '<div class="kb-table-of-content-wrap">';
if ( ! isset( $attributes['enableTitle'] ) || isset( $attributes['enableTitle'] ) && $attributes['enableTitle'] ) {
- $output .= '<div class="kb-table-of-contents-title-wrap kb-toggle-icon-style-' . ( $enable_toggle && isset( $attributes['toggleIcon'] ) && $attributes['toggleIcon'] ? $attributes['toggleIcon'] : 'arrow' ) . '">';
+ $output .= '<div class="kb-table-of-contents-title-wrap kb-toggle-icon-style-' . esc_attr( $enable_toggle && isset( $attributes['toggleIcon'] ) && $attributes['toggleIcon'] ? $attributes['toggleIcon'] : 'arrow' ) . '">';
if ( $title_toggle ) {
$output .= '<button class="kb-table-of-contents-title-btn kb-table-of-contents-toggle" aria-expanded="' . esc_attr( $start_closed ? 'false' : 'true' ) . '" aria-label="' . ( $start_closed ? esc_attr__( 'Expand Table of Contents', 'kadence-blocks' ) : esc_attr__( 'Collapse Table of Contents', 'kadence-blocks' ) ) . '">';
}
--- a/kadence-blocks/includes/resources/Traits/API_Url_Trait.php
+++ b/kadence-blocks/includes/resources/Traits/API_Url_Trait.php
@@ -95,4 +95,117 @@
protected function get_starter_get_url(): string {
return $this->get_starter_base_url() . '/wp-json/kadence-starter/v1/get/';
}
+
+ /**
+ * Resolve the starter-template sites base URL with override.
+ *
+ * KADENCE_BLOCKS_TEMPLATE_SITES_BASE_URL constant, otherwise production default.
+ *
+ * @since 3.7.8.1
+ */
+ protected function get_template_sites_base_url(): string {
+ $url = defined( 'KADENCE_BLOCKS_TEMPLATE_SITES_BASE_URL' ) && KADENCE_BLOCKS_TEMPLATE_SITES_BASE_URL
+ ? KADENCE_BLOCKS_TEMPLATE_SITES_BASE_URL
+ : 'https://startertemplatecloud.com';
+ return rtrim( (string) $url, '/' );
+ }
+
+ /**
+ * Whether a URL points at one of the Kadence library hosts.
+ *
+ * @since 3.7.8.1
+ *
+ * @param string $url The URL to check.
+ */
+ protected function is_kadence_api_url( string $url ): bool {
+ $host = wp_parse_url( $url, PHP_URL_HOST );
+
+ if ( empty( $host ) ) {
+ return false;
+ }
+
+ $allowed = array_filter(
+ [
+ wp_parse_url( $this->get_patterns_base_url(), PHP_URL_HOST ),
+ wp_parse_url( $this->get_starter_base_url(), PHP_URL_HOST ),
+ wp_parse_url( $this->get_template_sites_base_url(), PHP_URL_HOST ),
+ ]
+ );
+
+ return in_array( $host, $allowed, true );
+ }
+
+ /**
+ * The library URLs saved in the cloud connection settings.
+ *
+ * @since 3.7.8.1
+ *
+ * @return string[]
+ */
+ protected function get_saved_library_urls(): array {
+ $settings = json_decode( (string) get_option( 'kadence_blocks_cloud' ), true );
+ $urls = [];
+
+ if ( ! empty( $settings['connections'] ) && is_array( $settings['connections'] ) ) {
+ foreach ( $settings['connections'] as $connection ) {
+ if ( ! empty( $connection['url'] ) ) {
+ $urls[] = rtrim( (string) $connection['url'], '/' );
+ }
+ }
+ }
+
+ return $urls;
+ }
+
+ /**
+ * Resolve a requested library URL into a full endpoint URL.
+ *
+ * Only the Kadence library hosts and the saved cloud connections are
+ * allowed as request targets.
+ *
+ * @since 3.7.8.1
+ *
+ * @param string $requested The requested library URL.
+ * @param string $endpoint The endpoint path to append.
+ * @param string $fallback Returned when no library URL was requested.
+ *
+ * @return string Empty string when the requested URL is not allowed.
+ */
+ protected function resolve_library_url( string $requested, string $endpoint, string $fallback = '' ): string {
+ $requested = rtrim( trim( $requested ), '/' );
+
+ if ( '' === $requested ) {
+ return $fallback;
+ }
+
+ if ( $this->is_kadence_api_url( $requested ) || in_array( $requested, $this->get_saved_library_urls(), true ) ) {
+ return $requested . $endpoint;
+ }
+
+ return '';
+ }
+
+ /**
+ * Resolve a requested library URL for a connection lookup.
+ *
+ * Adding a connection to a new library is limited to users who can manage
+ * the connection settings.
+ *
+ * @since 3.7.8.1
+ *
+ * @param string $requested The requested library URL.
+ * @param string $endpoint The endpoint path to append.
+ *
+ * @return string Empty string when the requested URL is not allowed.
+ */
+ protected function resolve_connection_url( string $requested, string $endpoint ): string {
+ $url = $this->resolve_library_url( $requested, $endpoint );
+
+ if ( '' === $url && current_user_can( 'manage_options' ) ) {
+ $requested = rtrim( trim( $requested ), '/' );
+ $url = '' === $requested ? '' : $requested . $endpoint;
+ }
+
+ return $url;
+ }
}
--- a/kadence-blocks/kadence-blocks.php
+++ b/kadence-blocks/kadence-blocks.php
@@ -5,7 +5,7 @@
* Description: Advanced Page Building Blocks for Gutenberg. Create custom column layouts, backgrounds, dual buttons, icons etc.
* Author: Kadence WP
* Author URI: https://www.kadencewp.com
- * Version: 3.7.8
+ * Version: 3.7.8.1
* Requires PHP: 7.4
* Text Domain: kadence-blocks
* License: GPL2+
@@ -20,7 +20,7 @@
}
define( 'KADENCE_BLOCKS_PATH', realpath( plugin_dir_path( __FILE__ ) ) . DIRECTORY_SEPARATOR );
define( 'KADENCE_BLOCKS_URL', plugin_dir_url( __FILE__ ) );
-define( 'KADENCE_BLOCKS_VERSION', '3.7.8' );
+define( 'KADENCE_BLOCKS_VERSION', '3.7.8.1' );
/**
* Plugin basename, e.g. "kadence-blocks/kadence-blocks.php".
--- a/kadence-blocks/vendor/composer/installed.php
+++ b/kadence-blocks/vendor/composer/installed.php
@@ -1,9 +1,9 @@
<?php return array(
'root' => array(
'name' => 'kadencewp/kadence-blocks',
- 'pretty_version' => '3.7.8',
- 'version' => '3.7.8.0',
- 'reference' => '1aa7a45a1f7a64ced56fcf57d595cf2a530bac17',
+ 'pretty_version' => '3.7.8.1',
+ 'version' => '3.7.8.1',
+ 'reference' => 'aee44d94934adb1f8953767aa96047137816c842',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -47,9 +47,9 @@
'dev_requirement' => false,
),
'kadencewp/kadence-blocks' => array(
- 'pretty_version' => '3.7.8',
- 'version' => '3.7.8.0',
- 'reference' => '1aa7a45a1f7a64ced56fcf57d595cf2a530bac17',
+ 'pretty_version' => '3.7.8.1',
+ 'version' => '3.7.8.1',
+ 'reference' => 'aee44d94934adb1f8953767aa96047137816c842',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
--- a/kadence-blocks/vendor/vendor-prefixed/autoload.php
+++ b/kadence-blocks/vendor/vendor-prefixed/autoload.php
@@ -19,4 +19,4 @@
require_once __DIR__ . '/composer/autoload_real.php';
-return ComposerAutoloaderInit92b3cffb5143a5f64698f5f85d5414ba::getLoader();
+return ComposerAutoloaderInit52cc7f545a393c45a887c332557d6102::getLoader();
--- a/kadence-blocks/vendor/vendor-prefixed/composer/autoload_real.php
+++ b/kadence-blocks/vendor/vendor-prefixed/composer/autoload_real.php
@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
-class ComposerAutoloaderInit92b3cffb5143a5f64698f5f85d5414ba
+class ComposerAutoloaderInit52cc7f545a393c45a887c332557d6102
{
private static $loader;
@@ -24,17 +24,17 @@
require __DIR__ . '/platform_check.php';
- spl_autoload_register(array('ComposerAutoloaderInit92b3cffb5143a5f64698f5f85d5414ba', 'loadClassLoader'), true, true);
+ spl_autoload_register(array('ComposerAutoloaderInit52cc7f545a393c45a887c332557d6102', 'loadClassLoader'), true, true);
self::$loader = $loader = new KadenceWPKadenceBlocksComposerAutoloadClassLoader(dirname(__DIR__));
- spl_autoload_unregister(array('ComposerAutoloaderInit92b3cffb5143a5f64698f5f85d5414ba', 'loadClassLoader'));
+ spl_autoload_unregister(array('ComposerAutoloaderInit52cc7f545a393c45a887c332557d6102', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
- call_user_func(KadenceWPKadenceBlocksComposerAutoloadComposerStaticInit92b3cffb5143a5f64698f5f85d5414ba::getInitializer($loader));
+ call_user_func(KadenceWPKadenceBlocksComposerAutoloadComposerStaticInit52cc7f545a393c45a887c332557d6102::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
- $filesToLoad = KadenceWPKadenceBlocksComposerAutoloadComposerStaticInit92b3cffb5143a5f64698f5f85d5414ba::$files;
+ $filesToLoad = KadenceWPKadenceBlocksComposerAutoloadComposerStaticInit52cc7f545a393c45a887c332557d6102::$files;
$requireFile = Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
--- a/kadence-blocks/vendor/vendor-prefixed/composer/autoload_static.php
+++ b/kadence-blocks/vendor/vendor-prefixed/composer/autoload_static.php
@@ -4,7 +4,7 @@
namespace KadenceWPKadenceBlocksComposerAutoload;
-class ComposerStaticInit92b3cffb5143a5f64698f5f85d5414ba
+class ComposerStaticInit52cc7f545a393c45a887c332557d6102
{
public static $files = array (
'dfdcea4d84cc56c3d81598b2472a5a5d' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
@@ -1304,9 +1304,9 @@
public static function getInitializer(ClassLoader $loader)
{
return Closure::bind(function () use ($loader) {
- $loader->prefixLengthsPsr4 = ComposerStaticInit92b3cffb5143a5f64698f5f85d5414ba::$prefixLengthsPsr4;
- $loader->prefixDirsPsr4 = ComposerStaticInit92b3cffb5143a5f64698f5f85d5414ba::$prefixDirsPsr4;
- $loader->classMap = ComposerStaticInit92b3cffb5143a5f64698f5f85d5414ba::$classMap;
+ $loader->prefixLengthsPsr4 = ComposerStaticInit52cc7f545a393c45a887c332557d6102::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInit52cc7f545a393c45a887c332557d6102::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInit52cc7f545a393c45a887c332557d6102::$classMap;
}, null, ClassLoader::class);
}
--- a/kadence-blocks/vendor/vendor-prefixed/composer/installed.php
+++ b/kadence-blocks/vendor/vendor-prefixed/composer/installed.php
@@ -2,9 +2,9 @@
'root' =>
array (
'name' => 'kadencewp/kadence-blocks',
- 'pretty_version' => '3.7.8',
- 'version' => '3.7.8.0',
- 'reference' => '1aa7a45a1f7a64ced56fcf57d595cf2a530bac17',
+ 'pretty_version' => '3.7.8.1',
+ 'version' => '3.7.8.1',
+ 'reference' => 'aee44d94934adb1f8953767aa96047137816c842',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../',
'aliases' =>